From 5a843b13427e3d884f6779abb3af55efd67fc3c7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 14 Dec 2017 11:03:59 -0800 Subject: [PATCH 001/381] adding support for filters to get old properties --- libraries/entities/src/EntityEditFilters.cpp | 11 ++++-- libraries/entities/src/EntityEditFilters.h | 2 +- libraries/entities/src/EntityTree.cpp | 2 +- .../entity_edit_filters/postition-example.js | 36 +++++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 scripts/tutorials/entity_edit_filters/postition-example.js diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 550c8f17c4..09c165e9cb 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -42,7 +42,7 @@ QList EntityEditFilters::getZonesByPosition(glm::vec3& position) { } bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, - EntityTree::FilterType filterType, EntityItemID& itemID) { + EntityTree::FilterType filterType, EntityItemID& itemID, EntityItemPointer& existingEntity) { // get the ids of all the zones (plus the global entity edit filter) that the position // lies within @@ -68,9 +68,15 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper propertiesIn.setDesiredProperties(oldProperties); auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter. + + // get the current properties for then entity and include them for the filter call + auto currentProperties = existingEntity ? existingEntity->getProperties() : EntityItemProperties(); + QScriptValue currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true); + QScriptValueList args; args << inputValues; args << filterType; + args << currentValues; QScriptValue result = filterData.filterFn.call(_nullObjectForFilter, args); if (filterData.uncaughtExceptions()) { @@ -182,8 +188,8 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { qDebug() << "script request completed for entity " << entityID; auto scriptRequest = qobject_cast(sender()); - const QString urlString = scriptRequest->getUrl().toString(); if (scriptRequest && scriptRequest->getResult() == ResourceRequest::Success) { + const QString urlString = scriptRequest->getUrl().toString(); auto scriptContents = scriptRequest->getData(); qInfo() << "Downloaded script:" << scriptContents; QScriptProgram program(scriptContents, urlString); @@ -227,6 +233,7 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { } } } else if (scriptRequest) { + const QString urlString = scriptRequest->getUrl().toString(); qCritical() << "Failed to download script at" << urlString; // See HTTPResourceRequest::onRequestFinished for interpretation of codes. For example, a 404 is code 6 and 403 is 3. A timeout is 2. Go figure. qCritical() << "ResourceRequest error was" << scriptRequest->getResult(); diff --git a/libraries/entities/src/EntityEditFilters.h b/libraries/entities/src/EntityEditFilters.h index 6aeb347603..f25c3c092e 100644 --- a/libraries/entities/src/EntityEditFilters.h +++ b/libraries/entities/src/EntityEditFilters.h @@ -43,7 +43,7 @@ public: void removeFilter(EntityItemID entityID); bool filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, - EntityTree::FilterType filterType, EntityItemID& entityID); + EntityTree::FilterType filterType, EntityItemID& entityID, EntityItemPointer& existingEntity); signals: void filterAdded(EntityItemID id, bool success); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e62399ce95..aa7215f53f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1099,7 +1099,7 @@ bool EntityTree::filterProperties(EntityItemPointer& existingEntity, EntityItemP if (entityEditFilters) { auto position = existingEntity ? existingEntity->getWorldPosition() : propertiesIn.getPosition(); auto entityID = existingEntity ? existingEntity->getEntityItemID() : EntityItemID(); - accepted = entityEditFilters->filter(position, propertiesIn, propertiesOut, wasChanged, filterType, entityID); + accepted = entityEditFilters->filter(position, propertiesIn, propertiesOut, wasChanged, filterType, entityID, existingEntity); } return accepted; diff --git a/scripts/tutorials/entity_edit_filters/postition-example.js b/scripts/tutorials/entity_edit_filters/postition-example.js new file mode 100644 index 0000000000..00f7391809 --- /dev/null +++ b/scripts/tutorials/entity_edit_filters/postition-example.js @@ -0,0 +1,36 @@ +function filter(properties, type, originalProperties) { + + /* Clamp position changes.*/ + var maxChange = 5; + function sign(val) { + if (val > 0) { + return 1; + } else if (val < 0) { + return -1; + } else { + return 0; + } + } + + function clamp(val, min, max) { + if (val > max) { + val = max; + } else if (val < min) { + val = min; + } + return val; + } + + + if (properties.position) { + /* Random near-zero value used as "zero" to prevent two sequential updates from being + exactly the same (which would cause them to be ignored) */ + var nearZero = 0.0001 * Math.random() + 0.001; + var maxFudgeChange = (maxChange + nearZero); + properties.position.x = clamp(properties.position.x, originalProperties.position.x - maxFudgeChange, originalProperties.position.x + maxFudgeChange); + properties.position.y = clamp(properties.position.y, originalProperties.position.y - maxFudgeChange, originalProperties.position.y + maxFudgeChange); + properties.position.z = clamp(properties.position.z, originalProperties.position.z - maxFudgeChange, originalProperties.position.z + maxFudgeChange); + } + + return properties; +} \ No newline at end of file From 17288386aeddbad5ce953087903a744e23d08105 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 14 Dec 2017 12:20:03 -0800 Subject: [PATCH 002/381] implement support for zone properties in filters --- libraries/entities/src/EntityEditFilters.cpp | 25 +++++++++++++++++++ .../keep-in-zone-example.js | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 scripts/tutorials/entity_edit_filters/keep-in-zone-example.js diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 09c165e9cb..13be7ebc7d 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -73,12 +73,37 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper auto currentProperties = existingEntity ? existingEntity->getProperties() : EntityItemProperties(); QScriptValue currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true); + // get the zone properties + auto zoneEntity = _tree->findEntityByEntityItemID(id); + auto zoneProperties = zoneEntity ? zoneEntity->getProperties() : EntityItemProperties(); + QScriptValue zoneValues = zoneProperties.copyToScriptValue(filterData.engine, false, true, true); + + if (zoneEntity) { + bool success = true; + AABox aaBox = zoneEntity->getAABox(success); + + if (success) { + QScriptValue boundingBox = filterData.engine->newObject(); + QScriptValue bottomRightNear = vec3toScriptValue(filterData.engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3toScriptValue(filterData.engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3toScriptValue(filterData.engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3toScriptValue(filterData.engine, aaBox.getDimensions()); + boundingBox.setProperty("brn", bottomRightNear); + boundingBox.setProperty("tfl", topFarLeft); + boundingBox.setProperty("center", center); + boundingBox.setProperty("dimensions", boundingBoxDimensions); + zoneValues.setProperty("boundingBox", boundingBox); + } + } + QScriptValueList args; args << inputValues; args << filterType; args << currentValues; + args << zoneValues; QScriptValue result = filterData.filterFn.call(_nullObjectForFilter, args); + if (filterData.uncaughtExceptions()) { return false; } diff --git a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js new file mode 100644 index 0000000000..13355129f5 --- /dev/null +++ b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js @@ -0,0 +1,24 @@ +function filter(properties, type, originalProperties, zoneProperties) { + + var nearZero = 0.0001 * Math.random() + 0.001; + + /* Clamp position changes to bounding box of zone.*/ + function clamp(val, min, max) { + /* Random near-zero value used as "zero" to prevent two sequential updates from being + exactly the same (which would cause them to be ignored) */ + if (val > max) { + val = max - nearZero; + } else if (val < min) { + val = min + nearZero; + } + return val; + } + + if (properties.position) { + properties.position.x = clamp(properties.position.x, zoneProperties.boundingBox.brn.x, zoneProperties.boundingBox.tfl.x); + properties.position.y = clamp(properties.position.y, zoneProperties.boundingBox.brn.y, zoneProperties.boundingBox.tfl.y); + properties.position.z = clamp(properties.position.z, zoneProperties.boundingBox.brn.z, zoneProperties.boundingBox.tfl.z); + } + + return properties; +} \ No newline at end of file From 32d05bc163c16303ef5625e65df73f756228080d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 19 Dec 2017 07:34:56 -0800 Subject: [PATCH 003/381] CR feedback --- scripts/tutorials/entity_edit_filters/keep-in-zone-example.js | 2 +- .../{postition-example.js => position-example.js} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename scripts/tutorials/entity_edit_filters/{postition-example.js => position-example.js} (99%) diff --git a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js index 13355129f5..9013edda96 100644 --- a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js +++ b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js @@ -21,4 +21,4 @@ function filter(properties, type, originalProperties, zoneProperties) { } return properties; -} \ No newline at end of file +} diff --git a/scripts/tutorials/entity_edit_filters/postition-example.js b/scripts/tutorials/entity_edit_filters/position-example.js similarity index 99% rename from scripts/tutorials/entity_edit_filters/postition-example.js rename to scripts/tutorials/entity_edit_filters/position-example.js index 00f7391809..5f17f04542 100644 --- a/scripts/tutorials/entity_edit_filters/postition-example.js +++ b/scripts/tutorials/entity_edit_filters/position-example.js @@ -33,4 +33,4 @@ function filter(properties, type, originalProperties) { } return properties; -} \ No newline at end of file +} From c3801fee7e5093405a460be2b5df19f9625baa4b Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 27 Dec 2017 11:58:00 -0800 Subject: [PATCH 004/381] ka --- libraries/fbx/src/OBJReader.cpp | 20 +++++++++++++------- libraries/fbx/src/OBJReader.h | 4 +++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 315c6a86d2..bd4158bcad 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -256,7 +256,13 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { default: materials[matName] = currentMaterial; #ifdef WANT_DEBUG - 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; + qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << + currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << + " specular color:" << currentMaterial.specularColor << " emissive color:" << + currentMaterial.emissiveColor << " diffuse texture:" << + currentMaterial.diffuseTextureFilename << " specular texture:" << + currentMaterial.specularTextureFilename << " emissive texture:" << + currentMaterial.emissiveTextureFilename; #endif return; } @@ -277,14 +283,12 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if ((token == "d") || (token == "Tr")) { currentMaterial.opacity = tokenizer.getFloat(); } else if (token == "Ka") { - #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); - #endif + currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Kd") { currentMaterial.diffuseColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if ((token == "map_Kd") || (token == "map_Ks")) { + } else if ((token == "map_Ka") || (token == "map_Kd") || (token == "map_Ks")) { QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); if (filename.endsWith(".tga")) { #ifdef WANT_DEBUG @@ -292,7 +296,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { #endif break; } - if (token == "map_Kd") { + if (token == "map_Ka") { + currentMaterial.emissiveTextureFilename = filename; + } else if (token == "map_Kd") { currentMaterial.diffuseTextureFilename = filename; } else if( token == "map_Ks" ) { currentMaterial.specularTextureFilename = filename; @@ -725,7 +731,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, } geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, objMaterial.specularColor, - glm::vec3(0.0f), + objMaterial.emissiveColor, objMaterial.shininess, objMaterial.opacity); FBXMaterial& fbxMaterial = geometry.materials[materialID]; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 45e3f79480..e25cd7c5e9 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -56,11 +56,13 @@ public: float opacity; glm::vec3 diffuseColor; glm::vec3 specularColor; + glm::vec3 emissiveColor; QByteArray diffuseTextureFilename; QByteArray specularTextureFilename; + QByteArray emissiveTextureFilename; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.9f) {} }; class OBJReader: public QObject { // QObject so we can make network requests. From 5e45ee456c610733a5b598780aab45573dd7cf73 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 27 Dec 2017 16:55:45 -0800 Subject: [PATCH 005/381] more ka --- interface/resources/qml/js/Utils.jsc | Bin 6596 -> 6516 bytes libraries/fbx/src/OBJReader.cpp | 5 +++++ 2 files changed, 5 insertions(+) diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc index ab20e996b9469915ac6a89901da175143e6b5024..8da68e4e192018ee2b4f3d835ec91f36f0161eca 100644 GIT binary patch delta 522 zcmX?N{KZJ8u*@VmC9xz?kb!}Lk&~5STcM<00wV*14l4u0rhn_zD^;ej?k>4g!mi%G zd85u0My4%nlMgYvF+C98%*ABHq-@E+z>vm}$&ka4%3#Hy4~8yaRtiHBLn1>mL-yoY zme9>xSavcq8f?zvxX;LFu-T8xhB-~cvzy7I+abZT(?w;5Z?~9lcMw!410*y7MW_HI z)PW*Y0TOCJ7HViFnI&N2cysApZo@sr}K+4E}OiT-VC&A*2iV;C72bXXY}mb_f{tI)LMb*EnX9J{G= zWjE?fVPxWFpL~eXjmbb{GZ&K)lcEI!149}^CPNNGDuWe+J{UTKSV!qu$P@xQv&;%5r0+3J# zickeer~z51p@D&c!LzePrQ-kp|4@m^A9y8vAm%nqfJ$c|q`_`zm;seYK$lnmm56{y zI39Phm>j})kdv7eWRZ)?jLG}?J(xDJOx`D`JXuG8Z}JL$VaC6c_wai&DZm7I_&)Iq z-167~3SS0>5B8H41jOnE9)IGGd+4!a)=UuiXrJc6pLZgSU+*D*n>GUj15EV<2e8B^ z{2Kn8s9?FPGD3MA9~ zMq$_Fat`Uu69ftv87(F=3MGm|!vW+*kU5~R2l;h#rBDLnsetEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); From 38d2266d28bef0d418525acf251d6d76e201708c Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 27 Dec 2017 17:14:45 -0800 Subject: [PATCH 006/381] Tr --- libraries/fbx/src/OBJReader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 0395173c68..de60efbc1d 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -282,8 +282,10 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { //currentMaterial.specularTextureFilename = ""; } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); - } else if ((token == "d") || (token == "Tr")) { + } else if (token == "d") { currentMaterial.opacity = tokenizer.getFloat(); + } else if (token == "Tr") { + currentMaterial.opacity = 1.0f - tokenizer.getFloat(); } else if (token == "Ka") { currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Kd") { From 188e476f2f203bc5e832fb18afd9192a759ef260 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 28 Dec 2017 13:57:12 -0800 Subject: [PATCH 007/381] temp obj import fixes --- libraries/fbx/src/OBJReader.cpp | 24 ++++++++++++++++++++++-- libraries/render-utils/src/Model.cpp | 2 +- libraries/shared/src/Transform.cpp | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index de60efbc1d..51e7c3cafb 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -467,14 +467,34 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi // vertex-index/texture-index // vertex-index/texture-index/surface-normal-index QByteArray token = tokenizer.getDatum(); - if (!isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; + auto firstChar = token[0]; + // Tokenizer treats line endings as whitespace. Non-digit and non-negative sign indicates done; + if (!isdigit(firstChar) && firstChar != '-') { tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); break; } QList parts = token.split('/'); assert(parts.count() >= 1); assert(parts.count() <= 3); - const QByteArray noData {}; + // If indices are negative relative indices then adjust them to absolute indices based on current vector sizes + // Also add 1 to each index as 1 will be subtracted later on from each index in OBJFace::add + int part0 = parts[0].toInt(); + if (part0 < 0) { + parts[0].setNum(vertices.size() - abs(part0) + 1); + } + if (parts.count() > 1) { + int part1 = parts[1].toInt(); + if (part1 < 0) { + parts[1].setNum(textureUVs.size() - abs(part1) + 1); + } + if (parts.count() > 2) { + int part2 = parts[2].toInt(); + if (part2 < 0) { + parts[2].setNum(normals.size() - abs(part2) + 1); + } + } + } + const QByteArray noData{}; face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices, vertexColors); face.groupName = currentGroup; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7717ceda6f..fd9f753a75 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -163,7 +163,7 @@ void Model::setScale(const glm::vec3& scale) { _scaledToFit = false; } -const float SCALE_CHANGE_EPSILON = 0.001f; +const float SCALE_CHANGE_EPSILON = 0.0000001f; void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) { diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index 3e29c38add..eeb726ba72 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -19,7 +19,7 @@ #include "shared/JSONHelpers.h" void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) { - const float ACCURACY_THREASHOLD = 0.00001f; + const float ACCURACY_THREASHOLD = 0.000001f; // Following technique taken from: // http://callumhay.blogspot.com/2010/10/decomposing-affine-transforms.html From dee06e72f6c627da15e97dc1c67ac8873b5cab94 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 28 Dec 2017 16:08:53 -0800 Subject: [PATCH 008/381] ke is the new ka --- libraries/fbx/src/OBJReader.cpp | 18 +++++++++++------- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 51e7c3cafb..07d445fb3e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -277,9 +277,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName; #endif - //currentMaterial.emissiveTextureFilename = ""; currentMaterial.diffuseTextureFilename = ""; - //currentMaterial.specularTextureFilename = ""; + currentMaterial.emissiveTextureFilename = ""; + currentMaterial.specularTextureFilename = ""; } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); } else if (token == "d") { @@ -287,12 +287,16 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "Tr") { currentMaterial.opacity = 1.0f - tokenizer.getFloat(); } else if (token == "Ka") { - currentMaterial.emissiveColor = tokenizer.getVec3(); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); + #endif } else if (token == "Kd") { currentMaterial.diffuseColor = tokenizer.getVec3(); + } else if (token == "Ke") { + currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if ((token == "map_Ka") || (token == "map_Kd") || (token == "map_Ks")) { + } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks")) { QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); if (filename.endsWith(".tga")) { #ifdef WANT_DEBUG @@ -300,10 +304,10 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { #endif break; } - if (token == "map_Ka") { - currentMaterial.emissiveTextureFilename = filename; - } else if (token == "map_Kd") { + if (token == "map_Kd") { currentMaterial.diffuseTextureFilename = filename; + } else if (token == "map_Ke") { + currentMaterial.emissiveTextureFilename = filename; } else if( token == "map_Ks" ) { currentMaterial.specularTextureFilename = filename; } diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index e25cd7c5e9..f0852c9c22 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -62,7 +62,7 @@ public: QByteArray emissiveTextureFilename; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.9f) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f) {} }; class OBJReader: public QObject { // QObject so we can make network requests. From a855916eb89c1ef3b8d744e2ef9b7f477715f29d Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 28 Dec 2017 16:39:02 -0800 Subject: [PATCH 009/381] fix relative paths --- 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 07d445fb3e..3b4eed6746 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -297,7 +297,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks")) { - QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); + QByteArray filename = QString(tokenizer.getLineAsDatum()).toUtf8(); if (filename.endsWith(".tga")) { #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url; From cee6bd270079125729dd9748665e4714efb0f32f Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 28 Dec 2017 17:20:16 -0800 Subject: [PATCH 010/381] ignore Ni --- libraries/fbx/src/OBJReader.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 3b4eed6746..d6955a6945 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -282,6 +282,10 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.specularTextureFilename = ""; } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); + } else if (token == "Ni") { + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << tokenizer.getFloat(); + #endif } else if (token == "d") { currentMaterial.opacity = tokenizer.getFloat(); } else if (token == "Tr") { From 7531a2ef3b9e134e2000ed79c507bed7b1acafca Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Dec 2017 18:00:07 -0800 Subject: [PATCH 011/381] map_bump wip --- interface/resources/qml/js/Utils.jsc | Bin 6516 -> 6548 bytes libraries/fbx/src/OBJReader.cpp | 15 ++++++++++++--- libraries/fbx/src/OBJReader.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc index 8da68e4e192018ee2b4f3d835ec91f36f0161eca..0e4b04d46e53fb0fdea77cfc8b85627fa9276aad 100644 GIT binary patch delta 180 zcmW;8JqrPG9KiAK@A_XIoALs@0CnO@Nl`9F7A%U(LtQK$x|L+t^_Lf5mJ%uN!D2Uf z0Y)2x(r5X+)0<3k95~&cH}hhEL`*t~bR|LRQXGhvF2i${P@ W51R@;Gu>8oK_AsT%v-0F>BfHt$1Yz0 delta 155 zcmbPY{KZJ8u*@VmC9xz?kb!}Lk&~5STcM<00wV*14l4u0rhn_zD^;ej?k>4g!mi%G zd7@4Wqrt?D;Y<(MCv!1|F&>zl$QaJ3FnK4VKhp!@$y`j^1h=p-FdSiFV7S7setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index f0852c9c22..d0bbd539e9 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -60,6 +60,7 @@ public: QByteArray diffuseTextureFilename; QByteArray specularTextureFilename; QByteArray emissiveTextureFilename; + QByteArray bumpTextureFilename; bool used { false }; bool userSpecifiesUV { false }; OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f) {} From a44e00142dfb867e960596397689e7c3f617210c Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 2 Jan 2018 15:41:14 -0800 Subject: [PATCH 012/381] texture line parse wip --- libraries/fbx/src/OBJReader.cpp | 31 ++++++++++++++++++++++++++++--- libraries/fbx/src/OBJReader.h | 7 +++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 73c0f07fa6..d5eeec51d0 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -303,8 +303,10 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump")) { - QByteArray textureLine = QString(tokenizer.getLineAsDatum()).toUtf8(); - QByteArray filename = textureLine; // TODO: parse texture options and filename from line + const QByteArray textureLine = tokenizer.getLineAsDatum(); + QByteArray filename; + OBJMaterialTextureOptions textureOptions; + parseTextureLine(textureLine, filename, textureOptions); if (filename.endsWith(".tga")) { #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url; @@ -315,13 +317,36 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.diffuseTextureFilename = filename; } else if (token == "map_Ke") { currentMaterial.emissiveTextureFilename = filename; - } else if( token == "map_Ks" ) { + } else if (token == "map_Ks" ) { currentMaterial.specularTextureFilename = filename; } else if (token == "map_bump") { currentMaterial.bumpTextureFilename = filename; } } } +} + +void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions) { + QString parser = textureLine; + while (parser.length() > 0) { + if (parser.startsWith("-bm")) { + parser.remove(0, 4); + int multiplierEnd = parser.indexOf(' '); + if (multiplierEnd < 0) { + multiplierEnd = parser.length(); + } + QString multiplier = parser.left(multiplierEnd); + textureOptions.bumpMultiplier = std::stof(multiplier.toStdString()); + parser.remove(0, multiplier.length() + 1); + } else { + int fileEnd = parser.indexOf(' '); + if (fileEnd < 0) { + fileEnd = parser.length(); + } + filename = parser.left(fileEnd).toUtf8(); + parser.remove(0, filename.length() + 1); + } + } } std::tuple requestData(QUrl& url) { diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index d0bbd539e9..2c1db7ccf7 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -66,6 +66,12 @@ public: OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f) {} }; +class OBJMaterialTextureOptions { +public: + float bumpMultiplier; + OBJMaterialTextureOptions() : bumpMultiplier(1.0f) {} +}; + class OBJReader: public QObject { // QObject so we can make network requests. Q_OBJECT public: @@ -87,6 +93,7 @@ private: bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); + void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format. int _partCounter { 0 }; From 88b034aa78ddb3be7c95190bf6bf38742609b616 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 2 Jan 2018 16:35:43 -0800 Subject: [PATCH 013/381] bump multiplier --- libraries/fbx/src/FBX.h | 2 ++ libraries/fbx/src/OBJReader.cpp | 8 ++++++-- libraries/fbx/src/OBJReader.h | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 7d3328a2dd..70344512ca 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -178,6 +178,8 @@ public: float emissiveIntensity{ 1.0f }; float ambientFactor{ 1.0f }; + float bumpMultiplier{ 1.0f }; + QString materialID; QString name; QString shadingModel; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index d5eeec51d0..6127b454b8 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -302,7 +302,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump")) { + } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) { const QByteArray textureLine = tokenizer.getLineAsDatum(); QByteArray filename; OBJMaterialTextureOptions textureOptions; @@ -319,8 +319,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.emissiveTextureFilename = filename; } else if (token == "map_Ks" ) { currentMaterial.specularTextureFilename = filename; - } else if (token == "map_bump") { + } else if ((token == "map_bump") || (token == "bump")) { currentMaterial.bumpTextureFilename = filename; + currentMaterial.bumpMultiplier = textureOptions.bumpMultiplier; } } } @@ -338,6 +339,8 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file QString multiplier = parser.left(multiplierEnd); textureOptions.bumpMultiplier = std::stof(multiplier.toStdString()); parser.remove(0, multiplier.length() + 1); + } else if (parser[0] == '-') { + // TO DO } else { int fileEnd = parser.indexOf(' '); if (fileEnd < 0) { @@ -813,6 +816,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.bumpTextureFilename.isEmpty()) { fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; fbxMaterial.normalTexture.isBumpmap = true; + fbxMaterial.bumpMultiplier = objMaterial.bumpMultiplier; } modelMaterial->setEmissive(fbxMaterial.emissiveColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 2c1db7ccf7..ab2fd2fd78 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -61,9 +61,10 @@ public: QByteArray specularTextureFilename; QByteArray emissiveTextureFilename; QByteArray bumpTextureFilename; + float bumpMultiplier; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), bumpMultiplier(1.0f) {} }; class OBJMaterialTextureOptions { From dfb9d9ce6b3d82bc404f4683c6638dd11023477b Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 3 Jan 2018 13:58:27 -0800 Subject: [PATCH 014/381] illum model wip --- libraries/fbx/src/OBJReader.cpp | 53 ++++++++++++++++++++++++++++----- libraries/fbx/src/OBJReader.h | 1 + 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 6127b454b8..1e31371178 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -256,12 +256,12 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { default: materials[matName] = currentMaterial; #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << - currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << - " specular color:" << currentMaterial.specularColor << " emissive color:" << - currentMaterial.emissiveColor << " diffuse texture:" << - currentMaterial.diffuseTextureFilename << " specular texture:" << - currentMaterial.specularTextureFilename << " emissive texture:" << + qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel << + " shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << + " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << + currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor << + " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << + currentMaterial.specularTextureFilename << " emissive texture:" << currentMaterial.emissiveTextureFilename << " bump texture:" << currentMaterial.bumpTextureFilename; #endif @@ -302,6 +302,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); + } else if (token == "illum") { + currentMaterial.illuminationModel = tokenizer.getFloat(); } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) { const QByteArray textureLine = tokenizer.getLineAsDatum(); QByteArray filename; @@ -824,8 +826,45 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); modelMaterial->setRoughness(model::Material::shininessToRoughness(fbxMaterial.shininess)); + // Illumination model reference http://paulbourke.net/dataformats/mtl/ + switch (objMaterial.illuminationModel) { + case 0: // Color on and Ambient off + fbxMaterial.ambientFactor = 0.0f; + break; + case 1: // Color on and Ambient on + fbxMaterial.ambientFactor = 1.0f; + break; + case 2: // Highlight on + fbxMaterial.specularFactor = 1.0f; + break; + case 3: // Reflection on and Ray trace on + break; + case 4: // Transparency: Glass on and Reflection: Ray trace on + fbxMaterial.opacity = 0.0f; + break; + case 5: // Reflection: Fresnel on and Ray trace on + modelMaterial->setFresnel(glm::vec3(1.0f)); + break; + case 6: // Transparency: Refraction on and Reflection: Fresnel off and Ray trace on + fbxMaterial.opacity = 0.0f; + modelMaterial->setFresnel(glm::vec3(0.0f)); + break; + case 7: // Transparency: Refraction on and Reflection: Fresnel on and Ray trace on + fbxMaterial.opacity = 0.0f; + modelMaterial->setFresnel(glm::vec3(1.0f)); + break; + case 8: // Reflection on and Ray trace off + break; + case 9: // Transparency: Glass on and Reflection: Ray trace off + fbxMaterial.opacity = 0.0f; + break; + case 10: // Casts shadows onto invisible surfaces + // Do nothing? + break; + } + if (fbxMaterial.opacity <= 0.0f) { - modelMaterial->setOpacity(1.0f); + modelMaterial->setOpacity(0.0f); // previous was 1.0f? } else { modelMaterial->setOpacity(fbxMaterial.opacity); } diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index ab2fd2fd78..eb7376c64f 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -62,6 +62,7 @@ public: QByteArray emissiveTextureFilename; QByteArray bumpTextureFilename; float bumpMultiplier; + int illuminationModel; bool used { false }; bool userSpecifiesUV { false }; OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), bumpMultiplier(1.0f) {} From cbaba8688171d3530859f659038a4131afd604d1 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 3 Jan 2018 15:50:24 -0800 Subject: [PATCH 015/381] more texture options --- interface/resources/qml/js/Utils.jsc | Bin 6548 -> 6516 bytes libraries/fbx/src/OBJReader.cpp | 131 +++++++++++++++++++++++---- libraries/fbx/src/OBJReader.h | 18 ++-- 3 files changed, 121 insertions(+), 28 deletions(-) diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc index 0e4b04d46e53fb0fdea77cfc8b85627fa9276aad..8da68e4e192018ee2b4f3d835ec91f36f0161eca 100644 GIT binary patch delta 155 zcmbPY{KZJ8u*@VmC9xz?kb!}Lk&~5STcM<00wV*14l4u0rhn_zD^;ej?k>4g!mi%G zd7@4Wqrt?D;Y<(MCv!1|F&>zl$QaJ3FnK4VKhp!@$y`j^1h=p-FdSiFV7S7R|LRQXGhvF2i${P@ W51R@;Gu>8oK_AsT%v-0F>BfHt$1Yz0 diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 1e31371178..2e4fff700c 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -323,33 +323,124 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.specularTextureFilename = filename; } else if ((token == "map_bump") || (token == "bump")) { currentMaterial.bumpTextureFilename = filename; - currentMaterial.bumpMultiplier = textureOptions.bumpMultiplier; + currentMaterial.bumpTextureOptions = textureOptions; } } } } +bool OBJReader::parseTextureLineFloat(QString& parser, float& result) { + if (parser.length() == 0) { + return false; + } + auto firstChar = parser[0]; + if ((firstChar < '0' || firstChar > '9') && firstChar != '-') { + return false; + } + int floatEnd = parser.indexOf(' '); + if (floatEnd < 0) { + floatEnd = parser.length(); + } + QString floatStr = parser.left(floatEnd); + try + { + result = std::stof(floatStr.toStdString()); + parser.remove(0, floatStr.length() + 1); + return true; + } + catch (const std::exception& /*e*/) + { + return false; + } +} + +bool OBJReader::parseTextureLineString(QString& parser, QByteArray& result) { + if (parser.length() == 0) { + return false; + } + int stringEnd = parser.indexOf(' '); + if (stringEnd < 0) { + stringEnd = parser.length(); + } + result = parser.left(stringEnd).toUtf8(); + parser.remove(0, result.length() + 1); + return true; +} + void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions) { + // Texture options reference http://paulbourke.net/dataformats/mtl/ + // and https://wikivisually.com/wiki/Material_Template_Library QString parser = textureLine; while (parser.length() > 0) { - if (parser.startsWith("-bm")) { - parser.remove(0, 4); - int multiplierEnd = parser.indexOf(' '); - if (multiplierEnd < 0) { - multiplierEnd = parser.length(); - } - QString multiplier = parser.left(multiplierEnd); - textureOptions.bumpMultiplier = std::stof(multiplier.toStdString()); - parser.remove(0, multiplier.length() + 1); - } else if (parser[0] == '-') { - // TO DO - } else { - int fileEnd = parser.indexOf(' '); - if (fileEnd < 0) { - fileEnd = parser.length(); - } - filename = parser.left(fileEnd).toUtf8(); - parser.remove(0, filename.length() + 1); + if (parser.startsWith("-blendu") || parser.startsWith("-blendv")) { + parser.remove(0, 11); // remove through "-blendu on " or "-blendu off" + if (parser[0] == ' ') // extra character for space after off + parser.remove(0, 1); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -blendu/-blendv"; + #endif + } else if (parser.startsWith("-bm")) { + parser.remove(0, 4); // remove through "-bm " + parseTextureLineFloat(parser, textureOptions.bumpMultiplier); + } else if (parser.startsWith("-boost")) { + parser.remove(0, 7); // remove through "-boost " + float ignore; + parseTextureLineFloat(parser, ignore); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -boost"; + #endif + } else if (parser.startsWith("-cc")) { + parser.remove(0, 7); // remove through "-cc on " or "-cc off" + if (parser[0] == ' ') // extra character for space after off + parser.remove(0, 1); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -cc"; + #endif + } else if (parser.startsWith("-clamp")) { + parser.remove(0, 10); // remove through "-clamp on " or "-clamp off" + if (parser[0] == ' ') // extra character for space after off + parser.remove(0, 1); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -clamp"; + #endif + } else if (parser.startsWith("-imfchan")) { + parser.remove(0, 11); // remove through "-imfchan X " + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -imfchan"; + #endif + } else if (parser.startsWith("-mm")) { + parser.remove(0, 4); // remove through "-mm " + float ignore; + parseTextureLineFloat(parser, ignore); + parseTextureLineFloat(parser, ignore); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -mm"; + #endif + } else if (parser.startsWith("-o") || parser.startsWith("-s") || parser.startsWith("-t")) { + parser.remove(0, 3); // remove through "-o " + float ignore; + parseTextureLineFloat(parser, ignore); + parseTextureLineFloat(parser, ignore); + parseTextureLineFloat(parser, ignore); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -o/-s/-t"; + #endif + } else if (parser.startsWith("-texres")) { + parser.remove(0, 8); // remove through "-texres " + float ignore; + parseTextureLineFloat(parser, ignore); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -texres"; + #endif + } else if (parser.startsWith("-type")) { + parser.remove(0, 6); // remove through "-type " + QByteArray ignore; + parseTextureLineString(parser, ignore); + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -type"; + #endif + } else { // assume filename + parseTextureLineString(parser, filename); } } } @@ -818,7 +909,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.bumpTextureFilename.isEmpty()) { fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; fbxMaterial.normalTexture.isBumpmap = true; - fbxMaterial.bumpMultiplier = objMaterial.bumpMultiplier; + fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; } modelMaterial->setEmissive(fbxMaterial.emissiveColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index eb7376c64f..a4582d6ef4 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -48,6 +48,12 @@ private: void addFrom(const OBJFace* face, int index); }; +class OBJMaterialTextureOptions { +public: + float bumpMultiplier; + OBJMaterialTextureOptions() : bumpMultiplier(1.0f) {} +} +; // 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 { @@ -61,17 +67,11 @@ public: QByteArray specularTextureFilename; QByteArray emissiveTextureFilename; QByteArray bumpTextureFilename; - float bumpMultiplier; + OBJMaterialTextureOptions bumpTextureOptions; int illuminationModel; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), bumpMultiplier(1.0f) {} -}; - -class OBJMaterialTextureOptions { -public: - float bumpMultiplier; - OBJMaterialTextureOptions() : bumpMultiplier(1.0f) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), illuminationModel(0) {} }; class OBJReader: public QObject { // QObject so we can make network requests. @@ -95,6 +95,8 @@ private: bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); + bool parseTextureLineFloat(QString& parser, float& result); + bool parseTextureLineString(QString& parser, QByteArray& result); void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format. From e9f30549f487604fb8f5384770f7da7269de19b8 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 3 Jan 2018 18:20:07 -0800 Subject: [PATCH 016/381] ignore Tf, disable illum test --- libraries/fbx/src/OBJReader.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 2e4fff700c..ca69ccb64d 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -292,6 +292,12 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.opacity = tokenizer.getFloat(); } else if (token == "Tr") { currentMaterial.opacity = 1.0f - tokenizer.getFloat(); + } else if (token == "illum") { + currentMaterial.illuminationModel = tokenizer.getFloat(); + } else if (token == "Tf") { + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << tokenizer.getVec3(); + #endif } else if (token == "Ka") { #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); @@ -302,8 +308,6 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if (token == "illum") { - currentMaterial.illuminationModel = tokenizer.getFloat(); } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) { const QByteArray textureLine = tokenizer.getLineAsDatum(); QByteArray filename; @@ -917,6 +921,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); modelMaterial->setRoughness(model::Material::shininessToRoughness(fbxMaterial.shininess)); + /* // Illumination model reference http://paulbourke.net/dataformats/mtl/ switch (objMaterial.illuminationModel) { case 0: // Color on and Ambient off @@ -953,12 +958,9 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, // Do nothing? break; } + */ - if (fbxMaterial.opacity <= 0.0f) { - modelMaterial->setOpacity(0.0f); // previous was 1.0f? - } else { - modelMaterial->setOpacity(fbxMaterial.opacity); - } + modelMaterial->setOpacity(fbxMaterial.opacity); } return geometryPtr; From 2c5a433ea1f695cf6a3cb46d1f46e7cc55565b31 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 4 Jan 2018 12:56:57 -0800 Subject: [PATCH 017/381] fix datum reset with comment token --- libraries/fbx/src/OBJReader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index ca69ccb64d..d4cb5de497 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -70,6 +70,7 @@ int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) { } switch (ch) { case '#': { + _datum = ""; _comment = _device->readLine(); // stash comment for a future call to getComment return COMMENT_TOKEN; } From fcac489efb92c9da28742e6ed80a465a530cb1a1 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 4 Jan 2018 16:39:44 -0800 Subject: [PATCH 018/381] illum mode adjustments, small tweaks --- libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/OBJReader.cpp | 72 +++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 70344512ca..c1842dbee4 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -178,7 +178,7 @@ public: float emissiveIntensity{ 1.0f }; float ambientFactor{ 1.0f }; - float bumpMultiplier{ 1.0f }; + float bumpMultiplier{ 1.0f }; // TODO: to be implemented QString materialID; QString name; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index d4cb5de497..8fc683b0d5 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -286,8 +286,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); } else if (token == "Ni") { + float ignore = tokenizer.getFloat(); #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << tokenizer.getFloat(); + qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << ignore; #endif } else if (token == "d") { currentMaterial.opacity = tokenizer.getFloat(); @@ -296,12 +297,14 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "illum") { currentMaterial.illuminationModel = tokenizer.getFloat(); } else if (token == "Tf") { + glm::vec3 ignore = tokenizer.getVec3(); #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << tokenizer.getVec3(); + qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << ignore; #endif } else if (token == "Ka") { + glm::vec3 ignore = tokenizer.getVec3(); #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); + qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << ignore; #endif } else if (token == "Kd") { currentMaterial.diffuseColor = tokenizer.getVec3(); @@ -379,8 +382,9 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file while (parser.length() > 0) { if (parser.startsWith("-blendu") || parser.startsWith("-blendv")) { parser.remove(0, 11); // remove through "-blendu on " or "-blendu off" - if (parser[0] == ' ') // extra character for space after off + if (parser[0] == ' ') { // extra character for space after off parser.remove(0, 1); + } #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -blendu/-blendv"; #endif @@ -396,15 +400,17 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file #endif } else if (parser.startsWith("-cc")) { parser.remove(0, 7); // remove through "-cc on " or "-cc off" - if (parser[0] == ' ') // extra character for space after off + if (parser[0] == ' ') { // extra character for space after off parser.remove(0, 1); + } #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -cc"; #endif } else if (parser.startsWith("-clamp")) { parser.remove(0, 10); // remove through "-clamp on " or "-clamp off" - if (parser[0] == ' ') // extra character for space after off + if (parser[0] == ' ') { // extra character for space after off parser.remove(0, 1); + } #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -clamp"; #endif @@ -922,44 +928,76 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); modelMaterial->setRoughness(model::Material::shininessToRoughness(fbxMaterial.shininess)); - /* + bool applyTransparency = false; + bool applyShininess = false; + bool applyRoughness = false; + bool applyNonMetallic = false; + bool fresnelOn = false; + bool fresnelOff = false; + // Illumination model reference http://paulbourke.net/dataformats/mtl/ switch (objMaterial.illuminationModel) { case 0: // Color on and Ambient off - fbxMaterial.ambientFactor = 0.0f; + // We don't support ambient - do nothing? break; case 1: // Color on and Ambient on - fbxMaterial.ambientFactor = 1.0f; + // We don't support ambient - do nothing? break; case 2: // Highlight on fbxMaterial.specularFactor = 1.0f; break; case 3: // Reflection on and Ray trace on + applyShininess = true; break; case 4: // Transparency: Glass on and Reflection: Ray trace on - fbxMaterial.opacity = 0.0f; + applyTransparency = true; + applyShininess = true; break; case 5: // Reflection: Fresnel on and Ray trace on - modelMaterial->setFresnel(glm::vec3(1.0f)); + applyShininess = true; + fresnelOn = true; break; case 6: // Transparency: Refraction on and Reflection: Fresnel off and Ray trace on - fbxMaterial.opacity = 0.0f; - modelMaterial->setFresnel(glm::vec3(0.0f)); + applyTransparency = true; + applyNonMetallic = true; + applyShininess = true; + fresnelOff = true; break; case 7: // Transparency: Refraction on and Reflection: Fresnel on and Ray trace on - fbxMaterial.opacity = 0.0f; - modelMaterial->setFresnel(glm::vec3(1.0f)); + applyTransparency = true; + applyNonMetallic = true; + applyShininess = true; + fresnelOn = true; break; case 8: // Reflection on and Ray trace off + applyShininess = true; break; case 9: // Transparency: Glass on and Reflection: Ray trace off - fbxMaterial.opacity = 0.0f; + applyTransparency = true; + applyNonMetallic = true; + applyRoughness = true; break; case 10: // Casts shadows onto invisible surfaces // Do nothing? break; + } + + if (applyTransparency && fbxMaterial.opacity <= 0.1f) { + fbxMaterial.opacity = 0.1f; + } + if (applyShininess) { + modelMaterial->setRoughness(0.25f); + } else if (applyRoughness) { + modelMaterial->setRoughness(1.0f); + } + if (applyNonMetallic) { + modelMaterial->setMetallic(0.0f); + } + if (fresnelOn) { + modelMaterial->setFresnel(glm::vec3(1.0f)); + } else if (fresnelOff) { + modelMaterial->setFresnel(glm::vec3(0.0f)); } - */ modelMaterial->setOpacity(fbxMaterial.opacity); } From 7c99c85ecae4eb7066c7bb7a679cc542afc4c9d9 Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 5 Jan 2018 13:54:16 -0800 Subject: [PATCH 019/381] Revert "temp obj import fixes" This reverts commit 188e476f2f203bc5e832fb18afd9192a759ef260. --- libraries/fbx/src/OBJReader.cpp | 24 ++---------------------- libraries/render-utils/src/Model.cpp | 2 +- libraries/shared/src/Transform.cpp | 2 +- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 8fc683b0d5..2c09b2021d 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -612,34 +612,14 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi // vertex-index/texture-index // vertex-index/texture-index/surface-normal-index QByteArray token = tokenizer.getDatum(); - auto firstChar = token[0]; - // Tokenizer treats line endings as whitespace. Non-digit and non-negative sign indicates done; - if (!isdigit(firstChar) && firstChar != '-') { + if (!isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); break; } QList parts = token.split('/'); assert(parts.count() >= 1); assert(parts.count() <= 3); - // If indices are negative relative indices then adjust them to absolute indices based on current vector sizes - // Also add 1 to each index as 1 will be subtracted later on from each index in OBJFace::add - int part0 = parts[0].toInt(); - if (part0 < 0) { - parts[0].setNum(vertices.size() - abs(part0) + 1); - } - if (parts.count() > 1) { - int part1 = parts[1].toInt(); - if (part1 < 0) { - parts[1].setNum(textureUVs.size() - abs(part1) + 1); - } - if (parts.count() > 2) { - int part2 = parts[2].toInt(); - if (part2 < 0) { - parts[2].setNum(normals.size() - abs(part2) + 1); - } - } - } - const QByteArray noData{}; + const QByteArray noData {}; face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices, vertexColors); face.groupName = currentGroup; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 536e76e44e..b91f4dd405 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -163,7 +163,7 @@ void Model::setScale(const glm::vec3& scale) { _scaledToFit = false; } -const float SCALE_CHANGE_EPSILON = 0.0000001f; +const float SCALE_CHANGE_EPSILON = 0.001f; void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) { diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index eeb726ba72..3e29c38add 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -19,7 +19,7 @@ #include "shared/JSONHelpers.h" void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) { - const float ACCURACY_THREASHOLD = 0.000001f; + const float ACCURACY_THREASHOLD = 0.00001f; // Following technique taken from: // http://callumhay.blogspot.com/2010/10/decomposing-affine-transforms.html From eaee9b387273e1fef7185551d57a8c315e4ac795 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 8 Jan 2018 16:41:51 -0800 Subject: [PATCH 020/381] constants, pre-PR tweaks --- libraries/fbx/src/OBJReader.cpp | 33 +++++++++++++++++++-------------- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index a248f6fb68..e5d4fb7bd4 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -35,6 +35,11 @@ QHash COMMENT_SCALE_HINTS = {{"This file uses centimeters as uni const QString SMART_DEFAULT_MATERIAL_NAME = "High Fidelity smart default material name"; +const float ILLUMINATION_MODEL_MIN_OPACITY = 0.1f; +const float ILLUMINATION_MODEL_APPLY_SHININESS = 0.25f; +const float ILLUMINATION_MODEL_APPLY_ROUGHNESS = 1.0f; +const float ILLUMINATION_MODEL_APPLY_NON_METALLIC = 0.0f; + namespace { template T& checked_at(QVector& vector, int i) { @@ -380,7 +385,7 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file // and https://wikivisually.com/wiki/Material_Template_Library QString parser = textureLine; while (parser.length() > 0) { - if (parser.startsWith("-blendu") || parser.startsWith("-blendv")) { + if (parser.startsWith("-blend")) { // -blendu/-blendv parser.remove(0, 11); // remove through "-blendu on " or "-blendu off" if (parser[0] == ' ') { // extra character for space after off parser.remove(0, 1); @@ -421,18 +426,18 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file #endif } else if (parser.startsWith("-mm")) { parser.remove(0, 4); // remove through "-mm " - float ignore; - parseTextureLineFloat(parser, ignore); + float ignore, ignore2; parseTextureLineFloat(parser, ignore); + parseTextureLineFloat(parser, ignore2); #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -mm"; #endif - } else if (parser.startsWith("-o") || parser.startsWith("-s") || parser.startsWith("-t")) { - parser.remove(0, 3); // remove through "-o " - float ignore; - parseTextureLineFloat(parser, ignore); - parseTextureLineFloat(parser, ignore); + } else if (parser.startsWith("-o ") || parser.startsWith("-s ") || parser.startsWith("-t ")) { + parser.remove(0, 3); // remove through "-o/-s/-t " + float ignore, ignore2, ignore3; parseTextureLineFloat(parser, ignore); + bool hasValue2 = parseTextureLineFloat(parser, ignore2); + bool hasValue3 = parseTextureLineFloat(parser, ignore3); #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -o/-s/-t"; #endif @@ -944,7 +949,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, // We don't support ambient - do nothing? break; case 2: // Highlight on - fbxMaterial.specularFactor = 1.0f; + // Change specular intensity? break; case 3: // Reflection on and Ray trace on applyShininess = true; @@ -982,16 +987,16 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, break; } - if (applyTransparency && fbxMaterial.opacity <= 0.1f) { - fbxMaterial.opacity = 0.1f; + if (applyTransparency && fbxMaterial.opacity <= ILLUMINATION_MODEL_MIN_OPACITY) { + fbxMaterial.opacity = ILLUMINATION_MODEL_MIN_OPACITY; } if (applyShininess) { - modelMaterial->setRoughness(0.25f); + modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS); } else if (applyRoughness) { - modelMaterial->setRoughness(1.0f); + modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_ROUGHNESS); } if (applyNonMetallic) { - modelMaterial->setMetallic(0.0f); + modelMaterial->setMetallic(ILLUMINATION_MODEL_APPLY_NON_METALLIC); } if (fresnelOn) { modelMaterial->setFresnel(glm::vec3(1.0f)); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index a4582d6ef4..44382e3603 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -71,7 +71,7 @@ public: int illuminationModel; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), illuminationModel(0) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), illuminationModel(-1) {} }; class OBJReader: public QObject { // QObject so we can make network requests. From 075bcba49f2fb88b542e2d5e688759a89093231c Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 9 Jan 2018 23:48:40 +0100 Subject: [PATCH 021/381] AddressBar trigger implemented. Fixed warnings on non existing context properties. Fixed warnings about non notifyable property --- interface/src/Menu.cpp | 2 +- interface/src/ui/AddressBarDialog.h | 2 +- interface/src/ui/DialogsManager.cpp | 22 ++++++++++++++++++++-- interface/src/ui/DialogsManager.h | 4 +++- interface/src/ui/overlays/Web3DOverlay.cpp | 7 +++++++ 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d295e96867..f7f9e5480a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -278,7 +278,7 @@ Menu::Menu() { // Navigate > Show Address Bar addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L, - dialogsManager.data(), SLOT(showAddressBar())); + dialogsManager.data(), SLOT(toggleAddressBar())); // Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here. auto locationBookmarks = DependencyManager::get(); diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 66f208ca90..7806436774 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -22,7 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog { Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged) - Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl) + Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl CONSTANT) public: AddressBarDialog(QQuickItem* parent = nullptr); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ff2d4868df..310a4cc1cd 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -58,7 +58,7 @@ void DialogsManager::showAddressBar() { hmd->openTablet(); } qApp->setKeyboardFocusOverlay(hmd->getCurrentTabletScreenID()); - emit addressBarShown(true); + setAddressBarVisible(true); } void DialogsManager::hideAddressBar() { @@ -71,7 +71,7 @@ void DialogsManager::hideAddressBar() { hmd->closeTablet(); } qApp->setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - emit addressBarShown(false); + setAddressBarVisible(false); } void DialogsManager::showFeed() { @@ -157,6 +157,24 @@ void DialogsManager::hmdToolsClosed() { } } +void DialogsManager::toggleAddressBar() { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + + const bool addressBarLoaded = tablet->isPathLoaded(TABLET_ADDRESS_DIALOG); + + if (_addressBarVisible || addressBarLoaded) { + hideAddressBar(); + } else { + showAddressBar(); + } +} + +void DialogsManager::setAddressBarVisible(bool addressBarVisible) { + _addressBarVisible = addressBarVisible; + emit addressBarShown(_addressBarVisible); +} + void DialogsManager::showTestingResults() { if (!_testingDialog) { _testingDialog = new TestingDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 3a40b15a3b..f17ac39a7e 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -39,6 +39,7 @@ public: QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } + void setAddressBarVisible(bool addressBarVisible); public slots: void showAddressBar(); @@ -52,6 +53,7 @@ public slots: void hmdTools(bool showTools); void showDomainConnectionDialog(); void showTestingResults(); + void toggleAddressBar(); // Application Update void showUpdateDialog(); @@ -78,7 +80,7 @@ private: QPointer _octreeStatsDialog; QPointer _testingDialog; QPointer _domainConnectionDialog; - bool _closeAddressBar { false }; + bool _addressBarVisible { false }; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index a5da5e99b6..cdf09ffe19 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -72,6 +72,13 @@ Web3DOverlay::Web3DOverlay() { connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface); connect(this, &Web3DOverlay::releaseWebSurface, this, &Web3DOverlay::destroyWebSurface); connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); + + //need to be intialized before Tablet 1st open + _webSurface = DependencyManager::get()->acquire(_url); + _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); + } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : From 8aa0525f4536b965a6b3bd1586291ca38df164a7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jan 2018 10:32:28 -0800 Subject: [PATCH 022/381] CR style feedback --- .../keep-in-zone-example.js | 15 +++++++- .../entity_edit_filters/position-example.js | 37 +++++++++++-------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js index 9013edda96..eb9cfbeb4d 100644 --- a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js +++ b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js @@ -1,8 +1,21 @@ +// +// keep-in-zone-example.js +// +// +// Created by Brad Hefta-Gaub to use Entities on Dec. 15, 2017 +// Copyright 2017 High Fidelity, Inc. +// +// This sample entity edit filter script will keep any entity inside the bounding box of the zone this filter is applied to. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + function filter(properties, type, originalProperties, zoneProperties) { var nearZero = 0.0001 * Math.random() + 0.001; - /* Clamp position changes to bounding box of zone.*/ + /* Clamp position changes to bounding box of zone.*/ function clamp(val, min, max) { /* Random near-zero value used as "zero" to prevent two sequential updates from being exactly the same (which would cause them to be ignored) */ diff --git a/scripts/tutorials/entity_edit_filters/position-example.js b/scripts/tutorials/entity_edit_filters/position-example.js index 5f17f04542..a6cafe6bcb 100644 --- a/scripts/tutorials/entity_edit_filters/position-example.js +++ b/scripts/tutorials/entity_edit_filters/position-example.js @@ -1,16 +1,20 @@ +// +// position-example.js +// +// +// Created by Brad Hefta-Gaub to use Entities on Dec. 15, 2017 +// Copyright 2017 High Fidelity, Inc. +// +// This sample entity edit filter script will only allow position to be changed by no more than 5 meters on any axis. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + function filter(properties, type, originalProperties) { - - /* Clamp position changes.*/ - var maxChange = 5; - function sign(val) { - if (val > 0) { - return 1; - } else if (val < 0) { - return -1; - } else { - return 0; - } - } + + /* Clamp position changes.*/ + var maxChange = 5; function clamp(val, min, max) { if (val > max) { @@ -27,9 +31,12 @@ function filter(properties, type, originalProperties) { exactly the same (which would cause them to be ignored) */ var nearZero = 0.0001 * Math.random() + 0.001; var maxFudgeChange = (maxChange + nearZero); - properties.position.x = clamp(properties.position.x, originalProperties.position.x - maxFudgeChange, originalProperties.position.x + maxFudgeChange); - properties.position.y = clamp(properties.position.y, originalProperties.position.y - maxFudgeChange, originalProperties.position.y + maxFudgeChange); - properties.position.z = clamp(properties.position.z, originalProperties.position.z - maxFudgeChange, originalProperties.position.z + maxFudgeChange); + properties.position.x = clamp(properties.position.x, originalProperties.position.x + - maxFudgeChange, originalProperties.position.x + maxFudgeChange); + properties.position.y = clamp(properties.position.y, originalProperties.position.y + - maxFudgeChange, originalProperties.position.y + maxFudgeChange); + properties.position.z = clamp(properties.position.z, originalProperties.position.z + - maxFudgeChange, originalProperties.position.z + maxFudgeChange); } return properties; From 24f1fc35460313449e37b0794af30b71c34e7cc3 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 10 Jan 2018 22:30:43 +0000 Subject: [PATCH 023/381] fix Ui keyboard navigation various fixes to allow some support for keyboard navigation in message boxes and dialogs --- .../resources/qml/controls-uit/Button.qml | 4 +- .../resources/qml/dialogs/FileDialog.qml | 2 +- .../resources/qml/dialogs/MessageDialog.qml | 42 ++++++++++++++----- .../resources/qml/dialogs/QueryDialog.qml | 38 +++++++++++++---- .../messageDialog/MessageDialogButton.qml | 17 ++++++-- .../scripting/WindowScriptingInterface.cpp | 6 +-- 6 files changed, 83 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index 02c6181952..dde07f45ea 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -57,7 +57,7 @@ Original.Button { hifi.buttons.disabledColorStart[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { + } else if (control.hovered || control.focus) { hifi.buttons.hoveredColor[control.color] } else { hifi.buttons.colorStart[control.color] @@ -71,7 +71,7 @@ Original.Button { hifi.buttons.disabledColorFinish[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { + } else if (control.hovered || control.focus) { hifi.buttons.hoveredColor[control.color] } else { hifi.buttons.colorFinish[control.color] diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index b9633104d5..581ac37a74 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -704,7 +704,7 @@ ModalWindow { KeyNavigation.up: selectionType KeyNavigation.left: openButton KeyNavigation.right: fileTableView.contentItem - Keys.onReturnPressed: { canceled(); root.enabled = false } + Keys.onReturnPressed: { cancelAction.trigger() } } } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 40c5a01e15..79d4b009db 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -37,6 +37,16 @@ ModalWindow { return OffscreenUi.waitForMessageBoxResult(root); } + Keys.onRightPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + yesButton.forceActiveFocus() + } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + okButton.forceActiveFocus() + } + Keys.onTabPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + yesButton.forceActiveFocus() + } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + okButton.forceActiveFocus() + } property alias detailedText: detailedText.text property alias text: mainTextContainer.text property alias informativeText: informativeTextContainer.text @@ -47,7 +57,6 @@ ModalWindow { onIconChanged: updateIcon(); property int defaultButton: OriginalDialogs.StandardButton.NoButton; property int clickedButton: OriginalDialogs.StandardButton.NoButton; - focus: defaultButton === OriginalDialogs.StandardButton.NoButton property int titleWidth: 0 onTitleWidthChanged: d.resize(); @@ -134,16 +143,35 @@ ModalWindow { MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; } MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; } MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; } - MessageDialogButton { dialog: root; text: qsTr("No"); button: OriginalDialogs.StandardButton.No; } + MessageDialogButton { + id: noButton + dialog: root + text: qsTr("No") + button: OriginalDialogs.StandardButton.No + KeyNavigation.left: yesButton + KeyNavigation.backtab: yesButton + } MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; } - MessageDialogButton { dialog: root; text: qsTr("Yes"); button: OriginalDialogs.StandardButton.Yes; } + MessageDialogButton { + id: yesButton + dialog: root + text: qsTr("Yes") + button: OriginalDialogs.StandardButton.Yes + KeyNavigation.right: noButton + KeyNavigation.tab: noButton + } MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; } MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; } MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; } MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; } MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; } MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; } - MessageDialogButton { dialog: root; text: qsTr("OK"); button: OriginalDialogs.StandardButton.Ok; } + MessageDialogButton { + id: okButton + dialog: root + text: qsTr("OK") + button: OriginalDialogs.StandardButton.Ok + } Button { id: moreButton @@ -230,12 +258,6 @@ ModalWindow { event.accepted = true root.click(OriginalDialogs.StandardButton.Cancel) break - - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - root.click(root.defaultButton) - break } } } diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 9a38c3f0d6..a5ec9828e2 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -95,19 +95,19 @@ ModalWindow { TextField { id: textResult label: root.label - focus: items ? false : true visible: items ? false : true anchors { left: parent.left; right: parent.right; bottom: parent.bottom } + KeyNavigation.down: acceptButton + KeyNavigation.tab: acceptButton } ComboBox { id: comboBox label: root.label - focus: true visible: items ? true : false anchors { left: parent.left @@ -115,6 +115,8 @@ ModalWindow { bottom: parent.bottom } model: items ? items : [] + KeyNavigation.down: acceptButton + KeyNavigation.tab: acceptButton } } @@ -135,7 +137,6 @@ ModalWindow { Flow { id: buttons - focus: true spacing: hifi.dimensions.contentSpacing.x onHeightChanged: d.resize(); onWidthChanged: d.resize(); layoutDirection: Qt.RightToLeft @@ -145,8 +146,21 @@ ModalWindow { margins: 0 bottomMargin: hifi.dimensions.contentSpacing.y } - Button { action: cancelAction } - Button { action: acceptAction } + Button { + id: cancelButton + action: cancelAction + KeyNavigation.left: acceptButton + KeyNavigation.up: items ? comboBox : textResult + KeyNavigation.backtab: acceptButton + } + Button { + id: acceptButton + action: acceptAction + KeyNavigation.right: cancelButton + KeyNavigation.up: items ? comboBox : textResult + KeyNavigation.tab: cancelButton + KeyNavigation.backtab: items ? comboBox : textResult + } } Action { @@ -184,7 +198,13 @@ ModalWindow { case Qt.Key_Return: case Qt.Key_Enter: - acceptAction.trigger() + if (acceptButton.focus) { + acceptAction.trigger() + } else if(cancelButton.focus) { + cancelAction.trigger() + } else if(comboBox.focus || comboBox.popup.focus) { + comboBox.showList() + } event.accepted = true; break; } @@ -194,6 +214,10 @@ ModalWindow { keyboardEnabled = HMD.active; updateIcon(); d.resize(); - textResult.forceActiveFocus(); + if (items) { + comboBox.forceActiveFocus() + } else { + textResult.forceActiveFocus() + } } } diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index b7ff9354eb..bdb42aba61 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -16,10 +16,21 @@ import "../../controls-uit" Button { property var dialog; - property int button: StandardButton.NoButton; + property int button: StandardButton.Ok; - color: dialog.defaultButton === button ? hifi.buttons.blue : hifi.buttons.white - focus: dialog.defaultButton === button + color: focus ? hifi.buttons.blue : hifi.buttons.white onClicked: dialog.click(button) visible: dialog.buttons & button + Keys.onPressed: { + if (!focus) { + return + } + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + dialog.click(button) + break + } + } } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 4b355653b6..300297a074 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -103,14 +103,14 @@ void WindowScriptingInterface::raiseMainWindow() { /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue void WindowScriptingInterface::alert(const QString& message) { - OffscreenUi::asyncWarning("", message); + OffscreenUi::asyncWarning("", message, QMessageBox::Ok, QMessageBox::Ok); } /// Display a confirmation box with the options 'Yes' and 'No' /// \param const QString& message message to display /// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise QScriptValue WindowScriptingInterface::confirm(const QString& message) { - return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No))); + return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); } /// Display a prompt with a text box @@ -353,7 +353,7 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); } -/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid +/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid /// directory the browser will start at the root directory. /// \param const QString& title title of the window /// \param const QString& directory directory to start the asset browser at From 612f9621b449f07a64425312d8cc16533b675cd0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 11 Jan 2018 10:00:49 -0800 Subject: [PATCH 024/381] adding filter properties to allow optimization --- libraries/entities/src/EntityEditFilters.cpp | 136 ++++++++++++++---- libraries/entities/src/EntityEditFilters.h | 6 + .../keep-in-zone-example.js | 4 + .../entity_edit_filters/position-example.js | 2 + 4 files changed, 117 insertions(+), 31 deletions(-) diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 13be7ebc7d..2b0dcc9f73 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -61,6 +61,7 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper if (filterData.rejectAll) { return false; } + auto oldProperties = propertiesIn.getDesiredProperties(); auto specifiedProperties = propertiesIn.getChangedProperties(); propertiesIn.setDesiredProperties(specifiedProperties); @@ -69,38 +70,44 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter. - // get the current properties for then entity and include them for the filter call - auto currentProperties = existingEntity ? existingEntity->getProperties() : EntityItemProperties(); - QScriptValue currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true); - - // get the zone properties - auto zoneEntity = _tree->findEntityByEntityItemID(id); - auto zoneProperties = zoneEntity ? zoneEntity->getProperties() : EntityItemProperties(); - QScriptValue zoneValues = zoneProperties.copyToScriptValue(filterData.engine, false, true, true); - - if (zoneEntity) { - bool success = true; - AABox aaBox = zoneEntity->getAABox(success); - - if (success) { - QScriptValue boundingBox = filterData.engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(filterData.engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(filterData.engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(filterData.engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(filterData.engine, aaBox.getDimensions()); - boundingBox.setProperty("brn", bottomRightNear); - boundingBox.setProperty("tfl", topFarLeft); - boundingBox.setProperty("center", center); - boundingBox.setProperty("dimensions", boundingBoxDimensions); - zoneValues.setProperty("boundingBox", boundingBox); - } - } - QScriptValueList args; args << inputValues; args << filterType; - args << currentValues; - args << zoneValues; + + // get the current properties for then entity and include them for the filter call + if (existingEntity && filterData.wantsOriginalProperties) { + auto currentProperties = existingEntity->getProperties(filterData.includedOriginalProperties); + QScriptValue currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true); + args << currentValues; + } + + + // get the zone properties + if (filterData.wantsZoneProperties) { + auto zoneEntity = _tree->findEntityByEntityItemID(id); + if (zoneEntity) { + auto zoneProperties = zoneEntity->getProperties(filterData.includedZoneProperties); + QScriptValue zoneValues = zoneProperties.copyToScriptValue(filterData.engine, false, true, true); + + if (filterData.wantsZoneBoundingBox) { + bool success = true; + AABox aaBox = zoneEntity->getAABox(success); + if (success) { + QScriptValue boundingBox = filterData.engine->newObject(); + QScriptValue bottomRightNear = vec3toScriptValue(filterData.engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3toScriptValue(filterData.engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3toScriptValue(filterData.engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3toScriptValue(filterData.engine, aaBox.getDimensions()); + boundingBox.setProperty("brn", bottomRightNear); + boundingBox.setProperty("tfl", topFarLeft); + boundingBox.setProperty("center", center); + boundingBox.setProperty("dimensions", boundingBoxDimensions); + zoneValues.setProperty("boundingBox", boundingBox); + } + } + args << zoneValues; + } + } QScriptValue result = filterData.filterFn.call(_nullObjectForFilter, args); @@ -245,8 +252,75 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { delete engine; filterData.rejectAll=true; } - - + + // check to see if the filterFn has properties asking for Original props + QScriptValue wantsOriginalPropertiesValue = filterData.filterFn.property("wantsOriginalProperties"); + // if the wantsOriginalProperties is a boolean, or a string, or list of strings, then evaluate as follows: + // - boolean - true - include all original properties + // false - no properties at all + // - string - empty - no properties at all + // any valid property - include just that property in the Original properties + // - list of strings - include only those properties in the Original properties + if (wantsOriginalPropertiesValue.isBool()) { + filterData.wantsOriginalProperties = wantsOriginalPropertiesValue.toBool(); + } else if (wantsOriginalPropertiesValue.isString()) { + auto stringValue = wantsOriginalPropertiesValue.toString(); + filterData.wantsOriginalProperties = !stringValue.isEmpty(); + if (filterData.wantsOriginalProperties) { + EntityPropertyFlagsFromScriptValue(wantsOriginalPropertiesValue, filterData.includedOriginalProperties); + } + } else if (wantsOriginalPropertiesValue.isArray()) { + auto length = wantsOriginalPropertiesValue.property("length").toInteger(); + for (int i; i < length; i++) { + auto stringValue = wantsOriginalPropertiesValue.property(i).toString(); + if (!stringValue.isEmpty()) { + filterData.wantsOriginalProperties = true; + break; + } + } + if (filterData.wantsOriginalProperties) { + EntityPropertyFlagsFromScriptValue(wantsOriginalPropertiesValue, filterData.includedOriginalProperties); + } + } + + // check to see if the filterFn has properties asking for Zone props + QScriptValue wantsZonePropertiesValue = filterData.filterFn.property("wantsZoneProperties"); + // if the wantsZoneProperties is a boolean, or a string, or list of strings, then evaluate as follows: + // - boolean - true - include all Zone properties + // false - no properties at all + // - string - empty - no properties at all + // any valid property - include just that property in the Zone properties + // - list of strings - include only those properties in the Zone properties + if (wantsZonePropertiesValue.isBool()) { + filterData.wantsZoneProperties = wantsZonePropertiesValue.toBool(); + filterData.wantsZoneBoundingBox = filterData.wantsZoneProperties; // include this too + } else if (wantsZonePropertiesValue.isString()) { + auto stringValue = wantsZonePropertiesValue.toString(); + filterData.wantsZoneProperties = !stringValue.isEmpty(); + if (filterData.wantsZoneProperties) { + EntityPropertyFlagsFromScriptValue(wantsZonePropertiesValue, filterData.includedZoneProperties); + } + } else if (wantsZonePropertiesValue.isArray()) { + auto length = wantsZonePropertiesValue.property("length").toInteger(); + for (int i; i < length; i++) { + auto stringValue = wantsZonePropertiesValue.property(i).toString(); + if (!stringValue.isEmpty()) { + filterData.wantsZoneProperties = true; + + // boundingBox is a special case since it's not a true EntityPropertyFlag, so we + // need to detect it here. + if (stringValue == "boundingBox") { + filterData.wantsZoneBoundingBox = true; + break; // we can break here, since there are no other special cases + } + + } + } + if (filterData.wantsZoneProperties) { + EntityPropertyFlagsFromScriptValue(wantsZonePropertiesValue, filterData.includedZoneProperties); + } + } + _lock.lockForWrite(); _filterDataMap.insert(entityID, filterData); _lock.unlock(); diff --git a/libraries/entities/src/EntityEditFilters.h b/libraries/entities/src/EntityEditFilters.h index f25c3c092e..be3df50d3f 100644 --- a/libraries/entities/src/EntityEditFilters.h +++ b/libraries/entities/src/EntityEditFilters.h @@ -28,6 +28,12 @@ class EntityEditFilters : public QObject, public Dependency { public: struct FilterData { QScriptValue filterFn; + bool wantsOriginalProperties { false }; + bool wantsZoneProperties { false }; + EntityPropertyFlags includedOriginalProperties; + EntityPropertyFlags includedZoneProperties; + bool wantsZoneBoundingBox { false }; + std::function uncaughtExceptions; QScriptEngine* engine; bool rejectAll; diff --git a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js index eb9cfbeb4d..6d83ce1410 100644 --- a/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js +++ b/scripts/tutorials/entity_edit_filters/keep-in-zone-example.js @@ -35,3 +35,7 @@ function filter(properties, type, originalProperties, zoneProperties) { return properties; } + +filter.wantsOriginalProperties = true; +filter.wantsZoneProperties = true; +filter; \ No newline at end of file diff --git a/scripts/tutorials/entity_edit_filters/position-example.js b/scripts/tutorials/entity_edit_filters/position-example.js index a6cafe6bcb..01eabee7db 100644 --- a/scripts/tutorials/entity_edit_filters/position-example.js +++ b/scripts/tutorials/entity_edit_filters/position-example.js @@ -41,3 +41,5 @@ function filter(properties, type, originalProperties) { return properties; } +filter.wantsOriginalProperties = true; +filter; \ No newline at end of file From 0145b770da39868ab0209a7cbed7aabbbf0d5ec3 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 11 Jan 2018 17:16:22 -0800 Subject: [PATCH 025/381] fix warnings --- libraries/fbx/src/OBJReader.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index e5d4fb7bd4..008b51b0be 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -291,9 +291,10 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); } else if (token == "Ni") { - float ignore = tokenizer.getFloat(); #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << ignore; + qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << tokenizer.getFloat(); + #else + tokenizer.getFloat(); #endif } else if (token == "d") { currentMaterial.opacity = tokenizer.getFloat(); @@ -302,14 +303,16 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if (token == "illum") { currentMaterial.illuminationModel = tokenizer.getFloat(); } else if (token == "Tf") { - glm::vec3 ignore = tokenizer.getVec3(); #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << ignore; + qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << tokenizer.getVec3(); + #else + tokenizer.getVec3(); #endif } else if (token == "Ka") { - glm::vec3 ignore = tokenizer.getVec3(); #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << ignore; + qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();; + #else + tokenizer.getVec3(); #endif } else if (token == "Kd") { currentMaterial.diffuseColor = tokenizer.getVec3(); @@ -436,8 +439,8 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file parser.remove(0, 3); // remove through "-o/-s/-t " float ignore, ignore2, ignore3; parseTextureLineFloat(parser, ignore); - bool hasValue2 = parseTextureLineFloat(parser, ignore2); - bool hasValue3 = parseTextureLineFloat(parser, ignore3); + parseTextureLineFloat(parser, ignore2); + parseTextureLineFloat(parser, ignore3); #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -o/-s/-t"; #endif From e7a3d98ae5ede6b5643029b3abb5f97963283273 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 11 Jan 2018 19:34:54 -0800 Subject: [PATCH 026/381] don't draw head-descendent entities when in 1st-person camera mode --- interface/src/avatar/MyAvatar.cpp | 19 +++++++++++++++++++ interface/src/avatar/MyAvatar.h | 2 ++ .../src/RenderableModelEntityItem.cpp | 5 +++-- libraries/entities/src/EntityItem.h | 4 ++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e863a58e14..b680925b3e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include "MyHead.h" #include "MySkeletonModel.h" @@ -501,11 +502,29 @@ void MyAvatar::updateEyeContactTarget(float deltaTime) { extern QByteArray avatarStateToFrame(const AvatarData* _avatar); extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); +void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->setCauterized(!_prevShouldDrawHead); + } +} + void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); animateScaleChanges(deltaTime); + const std::unordered_set& headBoneSet = _skeletonModel->getCauterizeBoneSet(); + forEachChild([&](SpatiallyNestablePointer object) { + bool isChildOfHead = headBoneSet.find(object->getParentJointIndex()) != headBoneSet.end(); + if (isChildOfHead) { + updateChildCauterization(object); + object->forEachDescendant([&](SpatiallyNestablePointer descendant) { + updateChildCauterization(descendant); + }); + } + }); + { PerformanceTimer perfTimer("transform"); bool stepAction = false; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ab74460d4e..7b82a64e23 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -841,6 +841,8 @@ private: // height of user in sensor space, when standing erect. ThreadSafeValueCache _userHeight { DEFAULT_AVATAR_HEIGHT }; + + void updateChildCauterization(SpatiallyNestablePointer object); }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 86237e75a4..a08cf23d7b 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1281,11 +1281,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); - if (model->isVisible() != _visible) { + bool shouldBeVisible = _visible && !entity->getCauterized(); + if (model->isVisible() != shouldBeVisible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(_visible, scene); + model->setVisibleInScene(shouldBeVisible, scene); } // TODO? early exit here when not visible? diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ecfb7b5dcd..68639e0d3a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -469,6 +469,9 @@ public: static QString _marketplacePublicKey; static void retrieveMarketplacePublicKey(); + void setCauterized(bool value) { _cauterized = value; } + bool getCauterized() const { return _cauterized; } + signals: void requestRenderUpdate(); @@ -622,6 +625,7 @@ protected: quint64 _lastUpdatedAccelerationTimestamp { 0 }; quint64 _lastUpdatedQueryAACubeTimestamp { 0 }; + bool _cauterized { false }; // it true, don't draw because it would obscure 1st-person camera }; #endif // hifi_EntityItem_h From 9d4ab13a10df7ffc139df39c5fc24b33f473b302 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 11 Jan 2018 19:45:15 -0800 Subject: [PATCH 027/381] cauterize all head-based entities rather than just models --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 2 +- .../entities-renderer/src/RenderableModelEntityItem.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index fb9aba636b..49bc4deac5 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -365,7 +365,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa } _moving = entity->isMovingRelativeToParent(); - _visible = entity->getVisible(); + _visible = entity->getVisible() && !entity->getCauterized(); _needsRenderUpdate = false; }); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a08cf23d7b..86237e75a4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1281,12 +1281,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); - bool shouldBeVisible = _visible && !entity->getCauterized(); - if (model->isVisible() != shouldBeVisible) { + if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(shouldBeVisible, scene); + model->setVisibleInScene(_visible, scene); } // TODO? early exit here when not visible? From 814de4ab81f335d550d09ee42b6d84b0b1dff196 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 12 Jan 2018 11:59:19 +0100 Subject: [PATCH 028/381] Scribe now outputs shaders as cpp files. --- cmake/macros/AutoScribeShader.cmake | 8 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 4 +- .../RenderableParticleEffectEntityItem.cpp | 4 +- .../src/RenderablePolyLineEntityItem.cpp | 8 +- .../src/RenderablePolyVoxEntityItem.cpp | 18 +-- .../src/RenderableShapeEntityItem.cpp | 4 +- libraries/gpu/src/gpu/Shader.h | 2 + libraries/gpu/src/gpu/StandardShaderLib.cpp | 24 ++-- libraries/model/src/model/Skybox.cpp | 4 +- .../procedural/src/procedural/Procedural.cpp | 2 +- .../src/procedural/ProceduralSkybox.cpp | 4 +- .../src/AmbientOcclusionEffect.cpp | 10 +- libraries/render-utils/src/AnimDebugDraw.cpp | 5 +- .../render-utils/src/AntialiasingEffect.cpp | 6 +- libraries/render-utils/src/BloomEffect.cpp | 4 +- .../render-utils/src/DebugDeferredBuffer.cpp | 4 +- .../src/DeferredLightingEffect.cpp | 18 +-- libraries/render-utils/src/DrawHaze.cpp | 2 +- libraries/render-utils/src/GeometryCache.cpp | 26 ++-- .../render-utils/src/HighlightEffect.cpp | 20 +-- libraries/render-utils/src/LightClusters.cpp | 12 +- .../render-utils/src/RenderForwardTask.cpp | 2 +- .../render-utils/src/RenderPipelines.cpp | 132 +++++++++--------- .../render-utils/src/StencilMaskPass.cpp | 2 +- .../render-utils/src/SubsurfaceScattering.cpp | 8 +- .../render-utils/src/SurfaceGeometryPass.cpp | 6 +- libraries/render-utils/src/TextRenderer3D.cpp | 4 +- .../render-utils/src/ToneMappingEffect.cpp | 2 +- libraries/render-utils/src/ZoneRenderer.cpp | 6 +- libraries/render-utils/src/text/Font.cpp | 6 +- libraries/render/src/render/BlurTask.cpp | 8 +- .../render/src/render/DrawSceneOctree.cpp | 10 +- libraries/render/src/render/DrawStatus.cpp | 8 +- libraries/render/src/render/DrawTask.cpp | 4 +- .../render/src/render/drawItemBounds.slf | 1 - tests/shaders/src/main.cpp | 94 ++++++------- tools/scribe/src/main.cpp | 8 +- 37 files changed, 245 insertions(+), 245 deletions(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index 1919ecf00a..d25e0e553f 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -39,11 +39,11 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE) get_filename_component(SHADER_EXT ${SHADER_FILE} EXT) if(SHADER_EXT STREQUAL .slv) - set(SHADER_TARGET ${SHADER_TARGET}_vert.h) + set(SHADER_TARGET ${SHADER_TARGET}_vert.cpp) elseif(${SHADER_EXT} STREQUAL .slf) - set(SHADER_TARGET ${SHADER_TARGET}_frag.h) + set(SHADER_TARGET ${SHADER_TARGET}_frag.cpp) elseif(${SHADER_EXT} STREQUAL .slg) - set(SHADER_TARGET ${SHADER_TARGET}_geom.h) + set(SHADER_TARGET ${SHADER_TARGET}_geom.cpp) endif() set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}") @@ -134,6 +134,6 @@ macro(AUTOSCRIBE_SHADER_LIB) list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC}) # Link library shaders, if they exist - include_directories("${SHADERS_DIR}") + #include_directories("${SHADERS_DIR}") endmacro() diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 1d7fee38eb..4f50b5ecd9 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -33,8 +33,8 @@ #include "../Logging.h" #include "../CompositorHelper.h" -#include "render-utils/hmd_ui_vert.h" -#include "render-utils/hmd_ui_frag.h" +INCLUDE_SHADER(hmd_ui_vert) +INCLUDE_SHADER(hmd_ui_frag) static const QString MONO_PREVIEW = "Mono Preview"; static const QString DISABLE_PREVIEW = "Disable Preview"; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a3e6cd4341..b8f979b7fd 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -14,8 +14,8 @@ #include -#include "textured_particle_vert.h" -#include "textured_particle_frag.h" +INCLUDE_SHADER(textured_particle_vert) +INCLUDE_SHADER(textured_particle_frag) using namespace render; using namespace render::entities; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index b362721cde..b85771f5b6 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -23,11 +23,11 @@ # include #endif -#include "paintStroke_vert.h" -#include "paintStroke_frag.h" +INCLUDE_SHADER(paintStroke_vert) +INCLUDE_SHADER(paintStroke_frag) -#include "paintStroke_fade_vert.h" -#include "paintStroke_fade_frag.h" +INCLUDE_SHADER(paintStroke_fade_vert) +INCLUDE_SHADER(paintStroke_fade_frag) using namespace render; using namespace render::entities; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index ad0202457e..b245ed7cda 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -27,10 +27,11 @@ #include #include "EntityTreeRenderer.h" -#include "polyvox_vert.h" -#include "polyvox_frag.h" -#include "polyvox_fade_vert.h" -#include "polyvox_fade_frag.h" + +INCLUDE_SHADER(polyvox_vert) +INCLUDE_SHADER(polyvox_frag) +INCLUDE_SHADER(polyvox_fade_vert) +INCLUDE_SHADER(polyvox_fade_frag) #ifdef POLYVOX_ENTITY_USE_FADE_EFFECT # include @@ -70,10 +71,11 @@ #include "StencilMaskPass.h" #include "EntityTreeRenderer.h" -#include "polyvox_vert.h" -#include "polyvox_frag.h" -#include "polyvox_fade_vert.h" -#include "polyvox_fade_frag.h" + +INCLUDE_SHADER(polyvox_vert) +INCLUDE_SHADER(polyvox_frag) +INCLUDE_SHADER(polyvox_fade_vert) +INCLUDE_SHADER(polyvox_fade_frag) #include "RenderablePolyVoxEntityItem.h" #include "EntityEditPacketSender.h" diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index cdee2c5ec9..cd02186440 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,8 +16,8 @@ #include #include -#include -#include +INCLUDE_SHADER(simple_vert) +INCLUDE_SHADER(simple_frag) //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 181c9b5e78..5cfdbc8bf4 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -17,6 +17,8 @@ #include #include + +#define INCLUDE_SHADER(source) extern const char source[]; namespace gpu { diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp index 0d8d131e0b..93c2228de6 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.cpp +++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp @@ -12,21 +12,21 @@ // #include "StandardShaderLib.h" -#include "DrawUnitQuadTexcoord_vert.h" -#include "DrawTransformUnitQuad_vert.h" -#include "DrawTexcoordRectTransformUnitQuad_vert.h" -#include "DrawViewportQuadTransformTexcoord_vert.h" -#include "DrawVertexPosition_vert.h" -#include "DrawTransformVertexPosition_vert.h" +INCLUDE_SHADER(DrawUnitQuadTexcoord_vert) +INCLUDE_SHADER(DrawTransformUnitQuad_vert) +INCLUDE_SHADER(DrawTexcoordRectTransformUnitQuad_vert) +INCLUDE_SHADER(DrawViewportQuadTransformTexcoord_vert) +INCLUDE_SHADER(DrawVertexPosition_vert) +INCLUDE_SHADER(DrawTransformVertexPosition_vert) const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple... -#include "DrawWhite_frag.h" -#include "DrawColor_frag.h" -#include "DrawTexture_frag.h" -#include "DrawTextureMirroredX_frag.h" -#include "DrawTextureOpaque_frag.h" -#include "DrawColoredTexture_frag.h" +INCLUDE_SHADER(DrawWhite_frag) +INCLUDE_SHADER(DrawColor_frag) +INCLUDE_SHADER(DrawTexture_frag) +INCLUDE_SHADER(DrawTextureMirroredX_frag) +INCLUDE_SHADER(DrawTextureOpaque_frag) +INCLUDE_SHADER(DrawColoredTexture_frag) using namespace gpu; diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index fd3061afa5..7e03890c85 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -15,8 +15,8 @@ #include #include -#include "skybox_vert.h" -#include "skybox_frag.h" +INCLUDE_SHADER(skybox_vert) +INCLUDE_SHADER(skybox_frag) using namespace model; diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 7b718515a8..c0ebdbb186 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -20,7 +20,7 @@ #include #include -#include "ProceduralCommon_frag.h" +INCLUDE_SHADER(ProceduralCommon_frag) #include "Logging.h" diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 9544759037..452d3f639a 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +INCLUDE_SHADER(skybox_vert) +INCLUDE_SHADER(skybox_frag) ProceduralSkybox::ProceduralSkybox() : model::Skybox() { _procedural._vertexSource = skybox_vert; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 83753131c8..f5b4f0f6bb 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -28,11 +28,11 @@ #include "DependencyManager.h" #include "ViewFrustum.h" -#include "ssao_makePyramid_frag.h" -#include "ssao_makeOcclusion_frag.h" -#include "ssao_debugOcclusion_frag.h" -#include "ssao_makeHorizontalBlur_frag.h" -#include "ssao_makeVerticalBlur_frag.h" +INCLUDE_SHADER(ssao_makePyramid_frag) +INCLUDE_SHADER(ssao_makeOcclusion_frag) +INCLUDE_SHADER(ssao_debugOcclusion_frag) +INCLUDE_SHADER(ssao_makeHorizontalBlur_frag) +INCLUDE_SHADER(ssao_makeVerticalBlur_frag) AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index c22e99cbbc..53ed3ea9a0 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -9,8 +9,6 @@ #include -#include "animdebugdraw_vert.h" -#include "animdebugdraw_frag.h" #include #include "AbstractViewStateInterface.h" #include "RenderUtilsLogging.h" @@ -19,6 +17,9 @@ #include "AnimDebugDraw.h" +INCLUDE_SHADER(animdebugdraw_vert) +INCLUDE_SHADER(animdebugdraw_frag) + class AnimDebugDrawData { public: diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 70c2e3b5ce..078e906fad 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -23,9 +23,9 @@ #include "ViewFrustum.h" #include "GeometryCache.h" -#include "fxaa_vert.h" -#include "fxaa_frag.h" -#include "fxaa_blend_frag.h" +INCLUDE_SHADER(fxaa_vert) +INCLUDE_SHADER(fxaa_frag) +INCLUDE_SHADER(fxaa_blend_frag) Antialiasing::Antialiasing() { diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 9d9367a6d5..ace347f99e 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -16,8 +16,8 @@ #include #include -#include "BloomThreshold_frag.h" -#include "BloomApply_frag.h" +INCLUDE_SHADER(BloomThreshold_frag) +INCLUDE_SHADER(BloomApply_frag) #define BLOOM_BLUR_LEVEL_COUNT 3 diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index fe03ead4e1..05cd6a6f0e 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -23,8 +23,8 @@ #include "TextureCache.h" #include "DeferredLightingEffect.h" -#include "debug_deferred_buffer_vert.h" -#include "debug_deferred_buffer_frag.h" +INCLUDE_SHADER(debug_deferred_buffer_vert) +INCLUDE_SHADER(debug_deferred_buffer_frag) using namespace render; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 81a33f17e3..8ec2c74353 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -24,18 +24,18 @@ #include "TextureCache.h" #include "FramebufferCache.h" -#include "deferred_light_vert.h" -#include "deferred_light_point_vert.h" -#include "deferred_light_spot_vert.h" +INCLUDE_SHADER(deferred_light_vert) +INCLUDE_SHADER(deferred_light_point_vert) +INCLUDE_SHADER(deferred_light_spot_vert) -#include "directional_ambient_light_frag.h" -#include "directional_skybox_light_frag.h" +INCLUDE_SHADER(directional_ambient_light_frag) +INCLUDE_SHADER(directional_skybox_light_frag) -#include "directional_ambient_light_shadow_frag.h" -#include "directional_skybox_light_shadow_frag.h" +INCLUDE_SHADER(directional_ambient_light_shadow_frag) +INCLUDE_SHADER(directional_skybox_light_shadow_frag) -#include "local_lights_shading_frag.h" -#include "local_lights_drawOutline_frag.h" +INCLUDE_SHADER(local_lights_shading_frag) +INCLUDE_SHADER(local_lights_drawOutline_frag) using namespace render; diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index da07f5bd9b..dc1893b347 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -19,7 +19,7 @@ #include "HazeStage.h" #include "LightStage.h" -#include "Haze_frag.h" +INCLUDE_SHADER(Haze_frag) void HazeConfig::setHazeColor(const glm::vec3 value) { hazeColor = value; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 2616d08600..ac8b300e49 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -34,21 +34,21 @@ #include "model/TextureMap.h" #include "render/Args.h" -#include "standardTransformPNTC_vert.h" -#include "standardDrawTexture_frag.h" +INCLUDE_SHADER(standardTransformPNTC_vert) +INCLUDE_SHADER(standardDrawTexture_frag) -#include "simple_vert.h" -#include "simple_textured_frag.h" -#include "simple_textured_unlit_frag.h" -#include "simple_fade_vert.h" -#include "simple_textured_fade_frag.h" -#include "simple_textured_unlit_fade_frag.h" -#include "simple_opaque_web_browser_frag.h" -#include "simple_transparent_web_browser_frag.h" -#include "glowLine_vert.h" -#include "glowLine_frag.h" +INCLUDE_SHADER(simple_vert) +INCLUDE_SHADER(simple_textured_frag) +INCLUDE_SHADER(simple_textured_unlit_frag) +INCLUDE_SHADER(simple_fade_vert) +INCLUDE_SHADER(simple_textured_fade_frag) +INCLUDE_SHADER(simple_textured_unlit_fade_frag) +INCLUDE_SHADER(simple_opaque_web_browser_frag) +INCLUDE_SHADER(simple_transparent_web_browser_frag) +INCLUDE_SHADER(glowLine_vert) +INCLUDE_SHADER(glowLine_frag) -#include "grid_frag.h" +INCLUDE_SHADER(grid_frag) //#define WANT_DEBUG diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index fee1f4a568..69feffb055 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -22,13 +22,13 @@ #include -#include "surfaceGeometry_copyDepth_frag.h" -#include "debug_deferred_buffer_vert.h" -#include "debug_deferred_buffer_frag.h" -#include "Highlight_frag.h" -#include "Highlight_filled_frag.h" -#include "Highlight_aabox_vert.h" -#include "nop_frag.h" +INCLUDE_SHADER(surfaceGeometry_copyDepth_frag) +INCLUDE_SHADER(debug_deferred_buffer_vert) +INCLUDE_SHADER(debug_deferred_buffer_frag) +INCLUDE_SHADER(Highlight_frag) +INCLUDE_SHADER(Highlight_filled_frag) +INCLUDE_SHADER(Highlight_aabox_vert) +INCLUDE_SHADER(nop_frag) using namespace render; @@ -547,10 +547,10 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const return task.addJob("TransparentSelection", selectItemInput); } -#include "model_shadow_vert.h" -#include "skin_model_shadow_vert.h" +INCLUDE_SHADER(model_shadow_vert) +INCLUDE_SHADER(skin_model_shadow_vert) -#include "model_shadow_frag.h" +INCLUDE_SHADER(model_shadow_frag) void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index d6ac7fd2e2..79d755683f 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -18,15 +18,15 @@ #include "StencilMaskPass.h" -#include "lightClusters_drawGrid_vert.h" -#include "lightClusters_drawGrid_frag.h" +INCLUDE_SHADER(lightClusters_drawGrid_vert) +INCLUDE_SHADER(lightClusters_drawGrid_frag) -//#include "lightClusters_drawClusterFromDepth_vert.h" -#include "lightClusters_drawClusterFromDepth_frag.h" +//INCLUDE_SHADER(lightClusters_drawClusterFromDepth_vert) +INCLUDE_SHADER(lightClusters_drawClusterFromDepth_frag) -#include "lightClusters_drawClusterContent_vert.h" -#include "lightClusters_drawClusterContent_frag.h" +INCLUDE_SHADER(lightClusters_drawClusterContent_vert) +INCLUDE_SHADER(lightClusters_drawClusterContent_frag) enum LightClusterGridShader_MapSlot { DEFERRED_BUFFER_LINEAR_DEPTH_UNIT = 0, diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index c83251c605..8d02281d05 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -24,7 +24,7 @@ #include -#include "nop_frag.h" +INCLUDE_SHADER(nop_frag) using namespace render; extern void initForwardPipelines(ShapePlumber& plumber); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 7f644add72..62252d3a0b 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -20,86 +20,86 @@ #include "TextureCache.h" #include "render/DrawTask.h" -#include "model_vert.h" -#include "model_normal_map_vert.h" -#include "model_lightmap_vert.h" -#include "model_lightmap_normal_map_vert.h" -#include "skin_model_vert.h" -#include "skin_model_normal_map_vert.h" +INCLUDE_SHADER(model_vert) +INCLUDE_SHADER(model_normal_map_vert) +INCLUDE_SHADER(model_lightmap_vert) +INCLUDE_SHADER(model_lightmap_normal_map_vert) +INCLUDE_SHADER(skin_model_vert) +INCLUDE_SHADER(skin_model_normal_map_vert) -#include "model_lightmap_fade_vert.h" -#include "model_lightmap_normal_map_fade_vert.h" -#include "skin_model_fade_vert.h" -#include "skin_model_normal_map_fade_vert.h" +INCLUDE_SHADER(model_lightmap_fade_vert) +INCLUDE_SHADER(model_lightmap_normal_map_fade_vert) +INCLUDE_SHADER(skin_model_fade_vert) +INCLUDE_SHADER(skin_model_normal_map_fade_vert) -#include "simple_vert.h" -#include "simple_textured_frag.h" -#include "simple_textured_unlit_frag.h" -#include "simple_transparent_textured_frag.h" -#include "simple_transparent_textured_unlit_frag.h" +INCLUDE_SHADER(simple_vert) +INCLUDE_SHADER(simple_textured_frag) +INCLUDE_SHADER(simple_textured_unlit_frag) +INCLUDE_SHADER(simple_transparent_textured_frag) +INCLUDE_SHADER(simple_transparent_textured_unlit_frag) -#include "simple_fade_vert.h" -#include "simple_textured_fade_frag.h" -#include "simple_textured_unlit_fade_frag.h" -#include "simple_transparent_textured_fade_frag.h" -#include "simple_transparent_textured_unlit_fade_frag.h" +INCLUDE_SHADER(simple_fade_vert) +INCLUDE_SHADER(simple_textured_fade_frag) +INCLUDE_SHADER(simple_textured_unlit_fade_frag) +INCLUDE_SHADER(simple_transparent_textured_fade_frag) +INCLUDE_SHADER(simple_transparent_textured_unlit_fade_frag) -#include "model_frag.h" -#include "model_unlit_frag.h" -#include "model_normal_map_frag.h" -#include "model_normal_specular_map_frag.h" -#include "model_specular_map_frag.h" +INCLUDE_SHADER(model_frag) +INCLUDE_SHADER(model_unlit_frag) +INCLUDE_SHADER(model_normal_map_frag) +INCLUDE_SHADER(model_normal_specular_map_frag) +INCLUDE_SHADER(model_specular_map_frag) -#include "model_fade_vert.h" -#include "model_normal_map_fade_vert.h" +INCLUDE_SHADER(model_fade_vert) +INCLUDE_SHADER(model_normal_map_fade_vert) -#include "model_fade_frag.h" -#include "model_unlit_fade_frag.h" -#include "model_normal_map_fade_frag.h" -#include "model_normal_specular_map_fade_frag.h" -#include "model_specular_map_fade_frag.h" +INCLUDE_SHADER(model_fade_frag) +INCLUDE_SHADER(model_unlit_fade_frag) +INCLUDE_SHADER(model_normal_map_fade_frag) +INCLUDE_SHADER(model_normal_specular_map_fade_frag) +INCLUDE_SHADER(model_specular_map_fade_frag) -#include "forward_model_frag.h" -#include "forward_model_unlit_frag.h" -#include "forward_model_normal_map_frag.h" -#include "forward_model_normal_specular_map_frag.h" -#include "forward_model_specular_map_frag.h" +INCLUDE_SHADER(forward_model_frag) +INCLUDE_SHADER(forward_model_unlit_frag) +INCLUDE_SHADER(forward_model_normal_map_frag) +INCLUDE_SHADER(forward_model_normal_specular_map_frag) +INCLUDE_SHADER(forward_model_specular_map_frag) -#include "model_lightmap_frag.h" -#include "model_lightmap_normal_map_frag.h" -#include "model_lightmap_normal_specular_map_frag.h" -#include "model_lightmap_specular_map_frag.h" -#include "model_translucent_frag.h" -#include "model_translucent_unlit_frag.h" +INCLUDE_SHADER(model_lightmap_frag) +INCLUDE_SHADER(model_lightmap_normal_map_frag) +INCLUDE_SHADER(model_lightmap_normal_specular_map_frag) +INCLUDE_SHADER(model_lightmap_specular_map_frag) +INCLUDE_SHADER(model_translucent_frag) +INCLUDE_SHADER(model_translucent_unlit_frag) -#include "model_lightmap_fade_frag.h" -#include "model_lightmap_normal_map_fade_frag.h" -#include "model_lightmap_normal_specular_map_fade_frag.h" -#include "model_lightmap_specular_map_fade_frag.h" -#include "model_translucent_fade_frag.h" -#include "model_translucent_unlit_fade_frag.h" +INCLUDE_SHADER(model_lightmap_fade_frag) +INCLUDE_SHADER(model_lightmap_normal_map_fade_frag) +INCLUDE_SHADER(model_lightmap_normal_specular_map_fade_frag) +INCLUDE_SHADER(model_lightmap_specular_map_fade_frag) +INCLUDE_SHADER(model_translucent_fade_frag) +INCLUDE_SHADER(model_translucent_unlit_fade_frag) -#include "overlay3D_vert.h" -#include "overlay3D_frag.h" -#include "overlay3D_model_frag.h" -#include "overlay3D_model_translucent_frag.h" -#include "overlay3D_translucent_frag.h" -#include "overlay3D_unlit_frag.h" -#include "overlay3D_translucent_unlit_frag.h" -#include "overlay3D_model_unlit_frag.h" -#include "overlay3D_model_translucent_unlit_frag.h" +INCLUDE_SHADER(overlay3D_vert) +INCLUDE_SHADER(overlay3D_frag) +INCLUDE_SHADER(overlay3D_model_frag) +INCLUDE_SHADER(overlay3D_model_translucent_frag) +INCLUDE_SHADER(overlay3D_translucent_frag) +INCLUDE_SHADER(overlay3D_unlit_frag) +INCLUDE_SHADER(overlay3D_translucent_unlit_frag) +INCLUDE_SHADER(overlay3D_model_unlit_frag) +INCLUDE_SHADER(overlay3D_model_translucent_unlit_frag) -#include "model_shadow_vert.h" -#include "skin_model_shadow_vert.h" +INCLUDE_SHADER(model_shadow_vert) +INCLUDE_SHADER(skin_model_shadow_vert) -#include "model_shadow_frag.h" -#include "skin_model_shadow_frag.h" +INCLUDE_SHADER(model_shadow_frag) +INCLUDE_SHADER(skin_model_shadow_frag) -#include "model_shadow_fade_vert.h" -#include "skin_model_shadow_fade_vert.h" +INCLUDE_SHADER(model_shadow_fade_vert) +INCLUDE_SHADER(skin_model_shadow_fade_vert) -#include "model_shadow_fade_frag.h" -#include "skin_model_shadow_fade_frag.h" +INCLUDE_SHADER(model_shadow_fade_frag) +INCLUDE_SHADER(skin_model_shadow_fade_frag) using namespace render; using namespace std::placeholders; diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index f71111b64e..e2a8b31f08 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -17,7 +17,7 @@ #include -#include "stencil_drawMask_frag.h" +INCLUDE_SHADER(stencil_drawMask_frag) using namespace render; diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 1786898e57..e8d7e23ec2 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -17,11 +17,11 @@ #include "DeferredLightingEffect.h" -#include "subsurfaceScattering_makeProfile_frag.h" -#include "subsurfaceScattering_makeLUT_frag.h" -#include "subsurfaceScattering_makeSpecularBeckmann_frag.h" +INCLUDE_SHADER(subsurfaceScattering_makeProfile_frag) +INCLUDE_SHADER(subsurfaceScattering_makeLUT_frag) +INCLUDE_SHADER(subsurfaceScattering_makeSpecularBeckmann_frag) -#include "subsurfaceScattering_drawScattering_frag.h" +INCLUDE_SHADER(subsurfaceScattering_drawScattering_frag) enum ScatteringShaderBufferSlots { ScatteringTask_FrameTransformSlot = 0, diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index c4eea7ce7f..6ad8dc6137 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -25,10 +25,10 @@ const int SurfaceGeometryPass_ParamsSlot = 1; const int SurfaceGeometryPass_DepthMapSlot = 0; const int SurfaceGeometryPass_NormalMapSlot = 1; -#include "surfaceGeometry_makeLinearDepth_frag.h" -#include "surfaceGeometry_downsampleDepthNormal_frag.h" +INCLUDE_SHADER(surfaceGeometry_makeLinearDepth_frag) +INCLUDE_SHADER(surfaceGeometry_downsampleDepthNormal_frag) -#include "surfaceGeometry_makeCurvature_frag.h" +INCLUDE_SHADER(surfaceGeometry_makeCurvature_frag) diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 9c85952107..06e041685a 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -25,8 +25,8 @@ #include "MatrixStack.h" #include "RenderUtilsLogging.h" -#include "sdf_text3D_vert.h" -#include "sdf_text3D_frag.h" +INCLUDE_SHADER(sdf_text3D_vert) +INCLUDE_SHADER(sdf_text3D_frag) #include "GeometryCache.h" diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 72d692c5b2..3d3a11f7b3 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -17,7 +17,7 @@ #include "StencilMaskPass.h" #include "FramebufferCache.h" -#include "toneMapping_frag.h" +INCLUDE_SHADER(toneMapping_frag) const int ToneMappingEffect_ParamsSlot = 0; const int ToneMappingEffect_LightingMapSlot = 0; diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index c0d01c2eaf..77b5c492d3 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -20,9 +20,9 @@ #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" -#include "zone_drawKeyLight_frag.h" -#include "zone_drawAmbient_frag.h" -#include "zone_drawSkybox_frag.h" +INCLUDE_SHADER(zone_drawKeyLight_frag) +INCLUDE_SHADER(zone_drawAmbient_frag) +INCLUDE_SHADER(zone_drawSkybox_frag) using namespace render; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 8449c58c7c..9e0fbd1522 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -7,9 +7,9 @@ #include -#include "sdf_text3D_vert.h" -#include "sdf_text3D_frag.h" -#include "sdf_text3D_transparent_frag.h" +INCLUDE_SHADER(sdf_text3D_vert) +INCLUDE_SHADER(sdf_text3D_frag) +INCLUDE_SHADER(sdf_text3D_transparent_frag) #include "../RenderUtilsLogging.h" #include "FontFamilies.h" diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 2be6f8fad2..4b5d6da8e3 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -13,11 +13,11 @@ #include #include -#include "blurGaussianV_frag.h" -#include "blurGaussianH_frag.h" +INCLUDE_SHADER(blurGaussianV_frag) +INCLUDE_SHADER(blurGaussianH_frag) -#include "blurGaussianDepthAwareV_frag.h" -#include "blurGaussianDepthAwareH_frag.h" +INCLUDE_SHADER(blurGaussianDepthAwareV_frag) +INCLUDE_SHADER(blurGaussianDepthAwareH_frag) using namespace render; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 36663a454a..7823d85dae 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -22,12 +22,12 @@ #include "Args.h" -#include "drawCellBounds_vert.h" -#include "drawCellBounds_frag.h" -#include "drawLODReticle_frag.h" +INCLUDE_SHADER(drawCellBounds_vert) +INCLUDE_SHADER(drawCellBounds_frag) +INCLUDE_SHADER(drawLODReticle_frag) -#include "drawItemBounds_vert.h" -#include "drawItemBounds_frag.h" +INCLUDE_SHADER(drawItemBounds_vert) +INCLUDE_SHADER(drawItemBounds_frag) using namespace render; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 148e104453..1c6d7749f8 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -21,10 +21,10 @@ #include "Args.h" -#include "drawItemBounds_vert.h" -#include "drawItemBounds_frag.h" -#include "drawItemStatus_vert.h" -#include "drawItemStatus_frag.h" +INCLUDE_SHADER(drawItemBounds_vert) +INCLUDE_SHADER(drawItemBounds_frag) +INCLUDE_SHADER(drawItemStatus_vert) +INCLUDE_SHADER(drawItemStatus_frag) using namespace render; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 629cc55ccb..a6d5072cca 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -22,8 +22,8 @@ #include #include -#include -#include +INCLUDE_SHADER(drawItemBounds_vert) +INCLUDE_SHADER(drawItemBounds_frag) using namespace render; diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index e01d1607bd..84c47d0933 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -15,7 +15,6 @@ in vec4 varColor; in vec2 varTexcoord; out vec4 outFragColor; - void main(void) { float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 7c6886ad93..e0babc8b47 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -23,65 +23,65 @@ #include -#include -#include -#include -#include +INCLUDE_SHADER(simple_vert) +INCLUDE_SHADER(simple_frag) +INCLUDE_SHADER(simple_textured_frag) +INCLUDE_SHADER(simple_textured_unlit_frag) -#include -#include -#include +INCLUDE_SHADER(deferred_light_vert) +INCLUDE_SHADER(deferred_light_point_vert) +INCLUDE_SHADER(deferred_light_spot_vert) -#include -#include +INCLUDE_SHADER(directional_ambient_light_frag) +INCLUDE_SHADER(directional_skybox_light_frag) -#include -#include +INCLUDE_SHADER(standardTransformPNTC_vert) +INCLUDE_SHADER(standardDrawTexture_frag) -#include -#include -#include -#include -#include -#include -#include -#include +INCLUDE_SHADER(model_vert) +INCLUDE_SHADER(model_shadow_vert) +INCLUDE_SHADER(model_normal_map_vert) +INCLUDE_SHADER(model_lightmap_vert) +INCLUDE_SHADER(model_lightmap_normal_map_vert) +INCLUDE_SHADER(skin_model_vert) +INCLUDE_SHADER(skin_model_shadow_vert) +INCLUDE_SHADER(skin_model_normal_map_vert) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +INCLUDE_SHADER(model_frag) +INCLUDE_SHADER(model_shadow_frag) +INCLUDE_SHADER(model_normal_map_frag) +INCLUDE_SHADER(model_normal_specular_map_frag) +INCLUDE_SHADER(model_specular_map_frag) +INCLUDE_SHADER(model_lightmap_frag) +INCLUDE_SHADER(model_lightmap_normal_map_frag) +INCLUDE_SHADER(model_lightmap_normal_specular_map_frag) +INCLUDE_SHADER(model_lightmap_specular_map_frag) +INCLUDE_SHADER(model_translucent_frag) -#include -#include +INCLUDE_SHADER(textured_particle_frag) +INCLUDE_SHADER(textured_particle_vert) -#include -#include +INCLUDE_SHADER(overlay3D_vert) +INCLUDE_SHADER(overlay3D_frag) -#include -#include +INCLUDE_SHADER(skybox_vert) +INCLUDE_SHADER(skybox_frag) -#include -#include -#include -#include -#include -#include +INCLUDE_SHADER(DrawTransformUnitQuad_vert) +INCLUDE_SHADER(DrawTexcoordRectTransformUnitQuad_vert) +INCLUDE_SHADER(DrawViewportQuadTransformTexcoord_vert) +INCLUDE_SHADER(DrawTexture_frag) +INCLUDE_SHADER(DrawTextureOpaque_frag) +INCLUDE_SHADER(DrawColoredTexture_frag) -#include -#include +INCLUDE_SHADER(sdf_text3D_vert) +INCLUDE_SHADER(sdf_text3D_frag) -#include -#include +INCLUDE_SHADER(paintStroke_vert) +INCLUDE_SHADER(paintStroke_frag) -#include -#include +INCLUDE_SHADER(polyvox_vert) +INCLUDE_SHADER(polyvox_frag) // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow { diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index 810f6c0f45..fef2ea0308 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -123,7 +123,7 @@ int main (int argc, char** argv) { cerr << " varname and varvalue must be made of alpha numerical characters with no spaces." << endl; cerr << " -listVars : Will list the vars name and value in the standard output." << endl; cerr << " -showParseTree : Draw the tree obtained while parsing the source" << endl; - cerr << " -c++ : Generate a c++ header file containing the output file stream stored as a char[] variable" << endl; + cerr << " -c++ : Generate a c++ source file containing the output file stream stored as a char[] variable" << endl; return 0; } @@ -209,9 +209,7 @@ int main (int argc, char** argv) { } targetStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; - targetStringStream << "#ifndef scribe_" << targetName << "_h" << std::endl; - targetStringStream << "#define scribe_" << targetName << "_h" << std::endl << std::endl; - + targetStringStream << "extern const char " << targetName << "[];\n"; targetStringStream << "const char " << targetName << "[] = \n"; // Write the pages content @@ -219,8 +217,6 @@ int main (int argc, char** argv) { targetStringStream << "R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\"\n"; } targetStringStream << ";\n" << std::endl << std::endl; - - targetStringStream << "#endif" << std::endl; } else { targetStringStream << destStringStream.str(); } From 420b58fd5b031bac28f11e281e006cd6d891f113 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 12 Jan 2018 15:13:55 +0100 Subject: [PATCH 029/381] Trying to fix Android build error --- tools/scribe/src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index fef2ea0308..b95d1df6ec 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -209,8 +209,7 @@ int main (int argc, char** argv) { } targetStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; - targetStringStream << "extern const char " << targetName << "[];\n"; - targetStringStream << "const char " << targetName << "[] = \n"; + targetStringStream << "extern const char " << targetName << "[] = \n"; // Write the pages content for (auto page : pages) { From 9bdb89df2ea40108f440df59f89feb2a8d92c144 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 12 Jan 2018 07:26:09 -0800 Subject: [PATCH 030/381] trying to get mirror to work --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 6 ++++-- libraries/entities-renderer/src/RenderableEntityItem.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 49bc4deac5..8d9cc326f3 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -185,7 +185,8 @@ void EntityRenderer::render(RenderArgs* args) { emit requestRenderUpdate(); } - if (_visible) { + bool defaultMode = args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; + if (_visible && (defaultMode || !_cauterized)) { doRender(args); } } @@ -365,7 +366,8 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa } _moving = entity->isMovingRelativeToParent(); - _visible = entity->getVisible() && !entity->getCauterized(); + _visible = entity->getVisible(); + _cauterized = entity->getCauterized(); _needsRenderUpdate = false; }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 8eb82e2c6e..f8685df5da 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -124,6 +124,7 @@ protected: bool _isFading{ _entitiesShouldFadeFunction() }; bool _prevIsTransparent { false }; bool _visible { false }; + bool _cauterized { false }; bool _moving { false }; bool _needsRenderUpdate { false }; // Only touched on the rendering thread From 98a47e56fc447f9ad3ad2fd1057ed1ee218ac146 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 12 Jan 2018 17:32:12 +0100 Subject: [PATCH 031/381] Added a hack to force referencing of shader source on Android --- tools/scribe/src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index b95d1df6ec..8e6ca31c71 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -127,7 +127,7 @@ int main (int argc, char** argv) { return 0; } - // Define targetName: if none, get destFilenmae, if none get srcFilename + // Define targetName: if none, get destFilename, if none get srcFilename if (targetName.empty()) { if (destFilename.empty()) { targetName = srcFilename; @@ -216,6 +216,8 @@ int main (int argc, char** argv) { targetStringStream << "R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\"\n"; } targetStringStream << ";\n" << std::endl << std::endl; + targetStringStream << "// Hack to fix Android link error by forcing a reference to global variable\n"; + targetStringStream << "class " << targetName << "_used {\npublic:\nstatic const char* get() { return " << targetName << "; }\n};\n" << std::endl; } else { targetStringStream << destStringStream.str(); } From 227dbd6f684b96755429f18b356a41cc607a0733 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 12 Jan 2018 08:52:07 -0800 Subject: [PATCH 032/381] put this back to a working state --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 3 +-- .../entities-renderer/src/RenderableModelEntityItem.cpp | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 8d9cc326f3..f2876da40a 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -185,8 +185,7 @@ void EntityRenderer::render(RenderArgs* args) { emit requestRenderUpdate(); } - bool defaultMode = args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; - if (_visible && (defaultMode || !_cauterized)) { + if (_visible) { doRender(args); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 86237e75a4..a6d34f5826 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1281,11 +1281,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); - if (model->isVisible() != _visible) { + bool visible = _visible && !_cauterized; + if (model->isVisible() != visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(_visible, scene); + model->setVisibleInScene(visible, scene); } // TODO? early exit here when not visible? From dbdc50ed244b7cc9a18c766d7afa2d5c439eee96 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 12 Jan 2018 08:56:06 -0800 Subject: [PATCH 033/381] also cauterize non-models that are children of head --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index f2876da40a..c51e1bccd7 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -185,7 +185,7 @@ void EntityRenderer::render(RenderArgs* args) { emit requestRenderUpdate(); } - if (_visible) { + if (_visible && !_cauterized) { doRender(args); } } From f81f50a6d282061fe2e5a271b4d852513f0513b4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 12 Jan 2018 09:08:52 -0800 Subject: [PATCH 034/381] non-models act correctly in mirror now' --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index c51e1bccd7..f3ef956146 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -185,7 +185,12 @@ void EntityRenderer::render(RenderArgs* args) { emit requestRenderUpdate(); } - if (_visible && !_cauterized) { + auto& renderMode = args->_renderMode; + bool cauterized = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && + renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && + _cauterized; + + if (_visible && !cauterized) { doRender(args); } } From 836b69ab1b65a4d1618ffe052a60871d5d669e74 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Jan 2018 17:42:18 -0800 Subject: [PATCH 035/381] exposing several visibility flags --- .../render-utils/src/RenderShadowTask.cpp | 2 +- libraries/render-utils/src/RenderViewTask.cpp | 4 +-- libraries/render-utils/src/RenderViewTask.h | 2 +- libraries/render/src/render/Item.h | 35 +++++++++++++++---- .../src/render/RenderFetchCullSortTask.cpp | 4 +-- .../src/render/RenderFetchCullSortTask.h | 2 +- 6 files changed, 35 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 2e5b7132e4..02f6d70e43 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -264,7 +264,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + output.edit1() = ItemFilter::Builder::visibleWorldItems(0x08).withTypeShape().withOpaque().withoutLayered(); globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index dc6c66e058..434e069d28 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -14,7 +14,7 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" -void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) { +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask) { // auto items = input.get(); // Shadows use an orthographic projection because they are linked to sunlights @@ -30,7 +30,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: return true; }); - const auto items = task.addJob("FetchCullSort", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor, visibilityMask); assert(items.canCast()); if (isDeferred) { diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index eb61f56eab..9c2bbe0281 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -23,7 +23,7 @@ public: RenderViewTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask = 0xFF); }; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 77f5910b9e..c2f1312ec2 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -46,7 +46,10 @@ public: VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item DEFORMED, // Deformed within bound, not solid - INVISIBLE, // Visible or not? could be just here to cast shadow + INVISIBLE0, // Visible or not in this mask index? + INVISIBLE1, // Visible or not in this mask index? + INVISIBLE2, // Visible or not in this mask index? + INVISIBLE3, // Visible or not in this mask index? SHADOW_CASTER, // Item cast shadows PICKABLE, // Item can be picked/selected LAYERED, // Item belongs to one of the layers different from the default layer @@ -57,6 +60,12 @@ public: }; typedef std::bitset Flags; + // VISIBLE MASK is defined from several bits in the Key. + // An Item can be visible in some mask bits and not other allowing for per view rendering + // Beware that the visibility mask is the oposite of what stored in the key vals. + const static uint8_t NUM_VISIBLE_MASK_INDICES{ 4 }; + const static uint8_t VISIBLE_MASK_ALL{ 0x0F }; + // The key is the Flags Flags _flags; @@ -82,7 +91,7 @@ public: Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } - Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } + Builder& withInvisible(uint8_t maskIndex = 0) { _flags.set(INVISIBLE0 + maskIndex); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } Builder& withPickable() { _flags.set(PICKABLE); return (*this); } Builder& withLayered() { _flags.set(LAYERED); return (*this); } @@ -111,8 +120,10 @@ public: bool isRigid() const { return !_flags[DEFORMED]; } bool isDeformed() const { return _flags[DEFORMED]; } - bool isVisible() const { return !_flags[INVISIBLE]; } - bool isInvisible() const { return _flags[INVISIBLE]; } + bool isVisible(uint8_t maskIndex) const { return !_flags[INVISIBLE0 + maskIndex]; } + bool isInvisible(uint8_t maskIndex) const { return _flags[INVISIBLE0 + maskIndex]; } + uint8_t getVisibleMask() const { return (~(_flags.to_ulong() >> INVISIBLE0) & VISIBLE_MASK_ALL);} + bool isVisibleMask(uint8_t mask) const { return getVisibleMask() & mask; } bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } @@ -171,8 +182,18 @@ public: Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } - Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } - Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } + Builder& withVisible(uint8_t maskIndex) { _value.reset(ItemKey::INVISIBLE0 + maskIndex); _mask.set(ItemKey::INVISIBLE0 + maskIndex); return (*this); } + Builder& withInvisible(uint8_t maskIndex) { _value.set(ItemKey::INVISIBLE0 + maskIndex); _mask.set(ItemKey::INVISIBLE0 + maskIndex); return (*this); } + Builder& withVisibilityMask(uint8_t mask) { + for (int i = 0; i < ItemKey::NUM_VISIBLE_MASK_INDICES; i++) { + if ((1 << i) && mask) { + withVisible(i); + } else { + withInvisible(i); + } + } + return (*this); + } Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } @@ -185,7 +206,7 @@ public: Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place - static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); } + static Builder visibleWorldItems(uint8_t visibilityMask) { return Builder().withVisibilityMask(visibilityMask).withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index d7294fa2bd..25c40ea7f7 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -17,12 +17,12 @@ using namespace render; -void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor) { +void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor, uint8_t visibilityMask) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // CPU jobs: // Fetch and cull the items from the scene - const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const ItemFilter filter = ItemFilter::Builder::visibleWorldItems(visibilityMask).withoutLayered(); const auto spatialFilter = render::Varying(filter); const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index b25480ae3a..3e5ad2bad8 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -36,7 +36,7 @@ public: RenderFetchCullSortTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t visibilityMask = 0xFF); }; #endif // hifi_RenderFetchCullSortTask_h From 241849139ffe1c0a28181c7cd32a0d65857379dd Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 15 Jan 2018 10:21:16 -0800 Subject: [PATCH 036/381] wip --- libraries/entities/CMakeLists.txt | 6 +- .../entities/src/EntityItemProperties.cpp | 43 +++++++ libraries/entities/src/EntityItemProperties.h | 37 ++++-- .../entities/src/EntityItemPropertiesMacros.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 7 + libraries/entities/src/EntityTypes.cpp | 2 + libraries/entities/src/EntityTypes.h | 3 +- libraries/entities/src/MaterialEntityItem.cpp | 120 ++++++++++++++++++ libraries/entities/src/MaterialEntityItem.h | 79 ++++++++++++ libraries/shared/src/MaterialMode.h | 18 +++ 10 files changed, 301 insertions(+), 15 deletions(-) create mode 100644 libraries/entities/src/MaterialEntityItem.cpp create mode 100644 libraries/entities/src/MaterialEntityItem.h create mode 100644 libraries/shared/src/MaterialMode.h diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index c23740654e..191e43ebbe 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,8 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") -link_hifi_libraries(shared networking octree avatars model) +include_hifi_library_headers(fbx) +include_hifi_library_headers(gpu) +include_hifi_library_headers(image) +include_hifi_library_headers(ktx) +link_hifi_libraries(shared networking octree avatars model model-networking) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 8bfce7e2d2..0edec88edf 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -330,6 +330,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); + CHECK_PROPERTY_CHANGE(PROP_MATERIAL_URL, materialURL); + CHECK_PROPERTY_CHANGE(PROP_MATERIAL_TYPE, materialMode); + CHECK_PROPERTY_CHANGE(PROP_MATERIAL_BLEND_FACTOR, blendFactor); + CHECK_PROPERTY_CHANGE(PROP_MATERIAL_PRIORITY, priority); + CHECK_PROPERTY_CHANGE(PROP_PARENT_SHAPE_ID, shapeID); + CHECK_PROPERTY_CHANGE(PROP_MATERIAL_BOUNDS, materialBounds); + // Certifiable Properties CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName); CHECK_PROPERTY_CHANGE(PROP_ITEM_DESCRIPTION, itemDescription); @@ -623,6 +630,16 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); } + // Materials + if (_type == EntityTypes::Material) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_URL, materialURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_TYPE, materialMode); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_BLEND_FACTOR, blendFactor); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_PRIORITY, priority); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_SHAPE_ID, shapeID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_BOUNDS, materialBounds); + } + if (!skipDefaults && !strictSemantics) { AABox aaBox = getAABox(); QScriptValue boundingBox = engine->newObject(); @@ -755,6 +772,12 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(materialURL, QString, setMaterialURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMode, MaterialMode, setMaterialMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE(blendFactor, float, setBlendFactor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(priority, int, setPriority); + COPY_PROPERTY_FROM_QSCRIPTVALUE(shapeID, int, setShapeID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(materialBounds, glmVec4, setMaterialBounds); // Certifiable Properties COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName); @@ -1490,6 +1513,16 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); } + // Materials + if (properties.getType() == EntityTypes::Material) { + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_URL, properties.getMaterialURL()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, (uint32_t)properties.getMaterialMode()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, properties.getBlendFactor()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, properties.getPriority()); + APPEND_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, properties.getShapeID()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BOUNDS, properties.getMaterialBounds()); + } + APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); @@ -1845,6 +1878,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape); } + // Materials + if (properties.getType() == EntityTypes::Material) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_URL, QString, setMaterialURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_TYPE, MaterialMode, setMaterialMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_BLEND_FACTOR, float, setBlendFactor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_PRIORITY, uint32_t, setPriority); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_SHAPE_ID, uint32_t, setShapeID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_BOUNDS, glmVec4, setMaterialBounds); + } + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c3d04dc7ad..3190d66b63 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -44,6 +44,8 @@ #include "TextEntityItem.h" #include "ZoneEntityItem.h" +#include "MaterialMode.h" + const quint64 UNKNOWN_CREATED_TIME = 0; using ComponentPair = std::pair; @@ -58,19 +60,21 @@ const std::array COMPONENT_MODES = { { /// set of entity item properties via JavaScript hashes/QScriptValues /// all units for SI units (meter, second, radian, etc) class EntityItemProperties { - friend class EntityItem; // TODO: consider removing this friend relationship and use public methods - friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class BoxEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class SphereEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class LightEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class ZoneEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class WebEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class ShapeEntityItem; // TODO: consider removing this friend relationship and use public methods + // TODO: consider removing these friend relationship and use public methods + friend class EntityItem; + friend class ModelEntityItem; + friend class BoxEntityItem; + friend class SphereEntityItem; + friend class LightEntityItem; + friend class TextEntityItem; + friend class ParticleEffectEntityItem; + friend class ZoneEntityItem; + friend class WebEntityItem; + friend class LineEntityItem; + friend class PolyVoxEntityItem; + friend class PolyLineEntityItem; + friend class ShapeEntityItem; + friend class MaterialEntityItem; public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; @@ -218,6 +222,13 @@ public: DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube()); DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere"); + DEFINE_PROPERTY_REF(PROP_MATERIAL_URL, MaterialURL, materialURL, QString, ""); + DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_TYPE, MaterialMode, materialMode, MaterialMode, UV); + DEFINE_PROPERTY_REF(PROP_MATERIAL_BLEND_FACTOR, BlendFactor, blendFactor, float, 1.0f); + DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, uint32_t, 0); + DEFINE_PROPERTY_REF(PROP_PARENT_SHAPE_ID, ShapeID, shapeID, uint32_t, 0); + DEFINE_PROPERTY_REF(PROP_MATERIAL_BOUNDS, MaterialBounds, materialBounds, glmVec4, glm::vec4(0, 0, 1, 1)); + // Certifiable Properties - related to Proof of Purchase certificates DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME); DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 278d945ab0..c8c8a658ce 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -184,6 +184,7 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu } typedef glm::vec3 glmVec3; +typedef glm::vec4 glmVec4; typedef glm::quat glmQuat; typedef QVector qVectorVec3; typedef QVector qVectorQuat; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 7fd72bf0ee..fc9b5cd709 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -226,6 +226,13 @@ enum EntityPropertyList { PROP_LOCAL_DIMENSIONS, // only used to convert values to and from scripts + PROP_MATERIAL_URL, + PROP_MATERIAL_TYPE, + PROP_MATERIAL_BLEND_FACTOR, + PROP_MATERIAL_PRIORITY, + PROP_PARENT_SHAPE_ID, + PROP_MATERIAL_BOUNDS, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index cb17c28fd7..307371c649 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,6 +29,7 @@ #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" #include "ShapeEntityItem.h" +#include "MaterialEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -50,6 +51,7 @@ REGISTER_ENTITY_TYPE(PolyLine) REGISTER_ENTITY_TYPE(Shape) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory) +REGISTER_ENTITY_TYPE(Material) const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 316bf2b813..62011c6e26 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -49,7 +49,8 @@ public: PolyVox, PolyLine, Shape, - LAST = Shape + Material, + LAST = Material } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp new file mode 100644 index 0000000000..19daa11490 --- /dev/null +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -0,0 +1,120 @@ +// +// Created by Sam Gondelman on 1/12/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MaterialEntityItem.h" + +#include "EntityItemProperties.h" + +EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + Pointer entity(new MaterialEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +MaterialEntityItem::MaterialEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Material; +} + +EntityItemProperties MaterialEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class + COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialURL, getMaterialURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialMode, getMaterialMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendFactor, getBlendFactor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(priority, getPriority); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeID, getShapeID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialBounds, getMaterialBounds); + return properties; +} + +bool MaterialEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialURL, setMaterialURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialMode, setMaterialMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendFactor, setBlendFactor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(priority, setPriority); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeID, setShapeID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialBounds, setMaterialBounds); + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "MaterialEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties.getLastEdited()); + } + return somethingChanged; +} + +int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_MATERIAL_URL, QString, setMaterialURL); + READ_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, MaterialMode, setMaterialMode); + READ_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, float, setBlendFactor); + READ_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, int, setPriority); + READ_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, int, setShapeID); + READ_ENTITY_PROPERTY(PROP_MATERIAL_BOUNDS, glm::vec4, setMaterialBounds); + + return bytesRead; +} + + +// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time +EntityPropertyFlags MaterialEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_MATERIAL_URL; + requestedProperties += PROP_MATERIAL_TYPE; + requestedProperties += PROP_MATERIAL_BLEND_FACTOR; + requestedProperties += PROP_MATERIAL_PRIORITY; + requestedProperties += PROP_PARENT_SHAPE_ID; + requestedProperties += PROP_MATERIAL_BOUNDS; + return requestedProperties; +} + +void MaterialEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_URL, getMaterialURL()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, getMaterialMode()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, getBlendFactor()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, getPriority()); + APPEND_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, getShapeID()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BOUNDS, getMaterialBounds()); +} + +void MaterialEntityItem::debugDump() const { + quint64 now = usecTimestampNow(); + qCDebug(entities) << "MATERIAL EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " url:" << _materialURL; + qCDebug(entities) << " material type:" << _materialMode; + qCDebug(entities) << " blend factor:" << _blendFactor; + qCDebug(entities) << " priority:" << _priority; + qCDebug(entities) << " parent shape ID:" << _shapeID; + qCDebug(entities) << " material bounds:" << _materialBounds; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; +} diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h new file mode 100644 index 0000000000..094c94f24c --- /dev/null +++ b/libraries/entities/src/MaterialEntityItem.h @@ -0,0 +1,79 @@ +// +// Created by Sam Gondelman on 1/12/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MaterialEntityItem_h +#define hifi_MaterialEntityItem_h + +#include "EntityItem.h" + +#include "MaterialMode.h" +#include + +class MaterialEntityItem : public EntityItem { + using Pointer = std::shared_ptr; +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + MaterialEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; + + // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + void debugDump() const override; + + const QString& getMaterialURL() { return _materialURL; } + void setMaterialURL(const QString& materialURL) { _materialURL = materialURL; } + + MaterialMode getMaterialType() { return _materialMode; } + void setMaterialMode(MaterialMode mode); + + float getBlendFactor() { return _blendFactor; } + void setBlendFactor(float blendFactor) { _blendFactor = blendFactor; } + + int getPriority() { return _priority; } + void setPriority(int priority) { _priority = priority; } + + int getShapeID() { return _shapeID; } + void setShapeID(int shapeID) { _shapeID = shapeID; } + + const glm::vec4& getMaterialBounds() { return _materialBounds; } + void setMaterialBounds(const glm::vec4& materialBounds) { _materialBounds = materialBounds; } + +protected: + QString _materialURL; + MaterialMode _materialMode { UV }; + float _blendFactor { 1.0f }; + int _priority { 0 }; + int _shapeID { 0 }; + glm::vec4 _materialBounds { 0, 0, 1, 1 }; + + //NetworkMaterial _material; + +}; + +#endif // hifi_MaterialEntityItem_h diff --git a/libraries/shared/src/MaterialMode.h b/libraries/shared/src/MaterialMode.h new file mode 100644 index 0000000000..05c6d295a5 --- /dev/null +++ b/libraries/shared/src/MaterialMode.h @@ -0,0 +1,18 @@ +// +// Created by Sam Gondelman on 1/12/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MaterialMode_h +#define hifi_MaterialMode_h + +enum MaterialMode { + UV = 0, + PROJECTED +}; + +#endif // hifi_MaterialMode_h + From 3911ce59cc94828ba6c418af947c993d73903632 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 16 Jan 2018 19:02:12 +0100 Subject: [PATCH 037/381] Scribe now outputs .h and .cpp. Need to change how shader source is referenced in C++ code --- cmake/macros/AutoScribeShader.cmake | 37 ++-- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 4 +- .../RenderableParticleEffectEntityItem.cpp | 4 +- .../src/RenderablePolyLineEntityItem.cpp | 8 +- .../src/RenderablePolyVoxEntityItem.cpp | 16 +- .../src/RenderableShapeEntityItem.cpp | 4 +- libraries/gpu/src/gpu/Shader.h | 2 - libraries/gpu/src/gpu/StandardShaderLib.cpp | 48 ++--- libraries/model/src/model/Skybox.cpp | 8 +- .../procedural/src/procedural/Procedural.cpp | 4 +- .../src/procedural/ProceduralSkybox.cpp | 4 +- .../src/AmbientOcclusionEffect.cpp | 10 +- libraries/render-utils/src/AnimDebugDraw.cpp | 4 +- .../render-utils/src/AntialiasingEffect.cpp | 6 +- libraries/render-utils/src/BloomEffect.cpp | 4 +- .../render-utils/src/DebugDeferredBuffer.cpp | 4 +- .../src/DeferredLightingEffect.cpp | 18 +- libraries/render-utils/src/DrawHaze.cpp | 2 +- libraries/render-utils/src/GeometryCache.cpp | 26 +-- .../render-utils/src/HighlightEffect.cpp | 20 +- libraries/render-utils/src/LightClusters.cpp | 12 +- .../render-utils/src/RenderForwardTask.cpp | 4 +- .../render-utils/src/RenderPipelines.cpp | 132 +++++++------- .../render-utils/src/StencilMaskPass.cpp | 4 +- .../render-utils/src/SubsurfaceScattering.cpp | 8 +- .../render-utils/src/SurfaceGeometryPass.cpp | 12 +- libraries/render-utils/src/TextRenderer3D.cpp | 4 +- .../render-utils/src/ToneMappingEffect.cpp | 4 +- libraries/render-utils/src/ZoneRenderer.cpp | 6 +- libraries/render-utils/src/text/Font.cpp | 6 +- libraries/render/src/render/BlurTask.cpp | 8 +- .../render/src/render/DrawSceneOctree.cpp | 10 +- libraries/render/src/render/DrawStatus.cpp | 8 +- libraries/render/src/render/DrawTask.cpp | 4 +- tests/shaders/src/main.cpp | 172 +++++++++--------- tools/scribe/src/main.cpp | 136 ++++++++++++-- 36 files changed, 433 insertions(+), 330 deletions(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index d25e0e553f..c92f7a3ffe 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -39,24 +39,28 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE) get_filename_component(SHADER_EXT ${SHADER_FILE} EXT) if(SHADER_EXT STREQUAL .slv) - set(SHADER_TARGET ${SHADER_TARGET}_vert.cpp) + set(SHADER_TYPE vert) elseif(${SHADER_EXT} STREQUAL .slf) - set(SHADER_TARGET ${SHADER_TARGET}_frag.cpp) + set(SHADER_TYPE frag) elseif(${SHADER_EXT} STREQUAL .slg) - set(SHADER_TARGET ${SHADER_TARGET}_geom.cpp) + set(SHADER_TYPE geom) endif() + set(SHADER_TARGET ${SHADER_TARGET}_${SHADER_TYPE}) set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}") + set(SHADER_TARGET_HEADER ${SHADER_TARGET}.h) + set(SHADER_TARGET_SOURCE ${SHADER_TARGET}.cpp) # Target dependant Custom rule on the SHADER_FILE if (APPLE) set(GLPROFILE MAC_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe) + add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (ANDROID) set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) # for an android build, we can't use the scribe that cmake would normally produce as a target, # since it's unrunnable by the cross-compiling build machine @@ -72,23 +76,25 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) ") endif () - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS}) + add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (UNIX) set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe) + add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) else () set(GLPROFILE PC_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) endif() #output the generated file name - set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET} PARENT_SCOPE) + set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} PARENT_SCOPE) - file(GLOB INCLUDE_FILES ${SHADER_TARGET}) + file(GLOB INCLUDE_FILES ${SHADER_TARGET_HEADER}) endfunction() @@ -134,6 +140,9 @@ macro(AUTOSCRIBE_SHADER_LIB) list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC}) # Link library shaders, if they exist - #include_directories("${SHADERS_DIR}") + include_directories("${SHADERS_DIR}") + + # Add search directory to find gpu/Shader.h + include_directories("${HIFI_LIBRARY_DIR}/gpu/src") endmacro() diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 4f50b5ecd9..f39203c89d 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -33,8 +33,8 @@ #include "../Logging.h" #include "../CompositorHelper.h" -INCLUDE_SHADER(hmd_ui_vert) -INCLUDE_SHADER(hmd_ui_frag) +#include "hmd_ui_vert.h" +#include "hmd_ui_frag.h" static const QString MONO_PREVIEW = "Mono Preview"; static const QString DISABLE_PREVIEW = "Disable Preview"; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index b8f979b7fd..a3e6cd4341 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -14,8 +14,8 @@ #include -INCLUDE_SHADER(textured_particle_vert) -INCLUDE_SHADER(textured_particle_frag) +#include "textured_particle_vert.h" +#include "textured_particle_frag.h" using namespace render; using namespace render::entities; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index b85771f5b6..b362721cde 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -23,11 +23,11 @@ # include #endif -INCLUDE_SHADER(paintStroke_vert) -INCLUDE_SHADER(paintStroke_frag) +#include "paintStroke_vert.h" +#include "paintStroke_frag.h" -INCLUDE_SHADER(paintStroke_fade_vert) -INCLUDE_SHADER(paintStroke_fade_frag) +#include "paintStroke_fade_vert.h" +#include "paintStroke_fade_frag.h" using namespace render; using namespace render::entities; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index b245ed7cda..cf12da86e9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -28,10 +28,10 @@ #include "EntityTreeRenderer.h" -INCLUDE_SHADER(polyvox_vert) -INCLUDE_SHADER(polyvox_frag) -INCLUDE_SHADER(polyvox_fade_vert) -INCLUDE_SHADER(polyvox_fade_frag) +#include "polyvox_vert.h" +#include "polyvox_frag.h" +#include "polyvox_fade_vert.h" +#include "polyvox_fade_frag.h" #ifdef POLYVOX_ENTITY_USE_FADE_EFFECT # include @@ -72,10 +72,10 @@ INCLUDE_SHADER(polyvox_fade_frag) #include "EntityTreeRenderer.h" -INCLUDE_SHADER(polyvox_vert) -INCLUDE_SHADER(polyvox_frag) -INCLUDE_SHADER(polyvox_fade_vert) -INCLUDE_SHADER(polyvox_fade_frag) +#include "polyvox_vert.h" +#include "polyvox_frag.h" +#include "polyvox_fade_vert.h" +#include "polyvox_fade_frag.h" #include "RenderablePolyVoxEntityItem.h" #include "EntityEditPacketSender.h" diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index cd02186440..eddde317fe 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,8 +16,8 @@ #include #include -INCLUDE_SHADER(simple_vert) -INCLUDE_SHADER(simple_frag) +#include "simple_vert.h" +#include "simple_frag.h" //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 5cfdbc8bf4..181c9b5e78 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -17,8 +17,6 @@ #include #include - -#define INCLUDE_SHADER(source) extern const char source[]; namespace gpu { diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp index 93c2228de6..0d829fb21f 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.cpp +++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp @@ -12,21 +12,21 @@ // #include "StandardShaderLib.h" -INCLUDE_SHADER(DrawUnitQuadTexcoord_vert) -INCLUDE_SHADER(DrawTransformUnitQuad_vert) -INCLUDE_SHADER(DrawTexcoordRectTransformUnitQuad_vert) -INCLUDE_SHADER(DrawViewportQuadTransformTexcoord_vert) -INCLUDE_SHADER(DrawVertexPosition_vert) -INCLUDE_SHADER(DrawTransformVertexPosition_vert) +#include "DrawUnitQuadTexcoord_vert.h" +#include "DrawTransformUnitQuad_vert.h" +#include "DrawTexcoordRectTransformUnitQuad_vert.h" +#include "DrawViewportQuadTransformTexcoord_vert.h" +#include "DrawVertexPosition_vert.h" +#include "DrawTransformVertexPosition_vert.h" const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple... -INCLUDE_SHADER(DrawWhite_frag) -INCLUDE_SHADER(DrawColor_frag) -INCLUDE_SHADER(DrawTexture_frag) -INCLUDE_SHADER(DrawTextureMirroredX_frag) -INCLUDE_SHADER(DrawTextureOpaque_frag) -INCLUDE_SHADER(DrawColoredTexture_frag) +#include "DrawWhite_frag.h" +#include "DrawColor_frag.h" +#include "DrawTexture_frag.h" +#include "DrawTextureMirroredX_frag.h" +#include "DrawTextureOpaque_frag.h" +#include "DrawColoredTexture_frag.h" using namespace gpu; @@ -73,42 +73,42 @@ ShaderPointer StandardShaderLib::getProgram(GetShader getVS, GetShader getPS) { ShaderPointer StandardShaderLib::getDrawUnitQuadTexcoordVS() { if (!_drawUnitQuadTexcoordVS) { - _drawUnitQuadTexcoordVS = gpu::Shader::createVertex(std::string(DrawUnitQuadTexcoord_vert)); + _drawUnitQuadTexcoordVS = DrawUnitQuadTexcoord_vert::getShader(); } return _drawUnitQuadTexcoordVS; } ShaderPointer StandardShaderLib::getDrawTransformUnitQuadVS() { if (!_drawTransformUnitQuadVS) { - _drawTransformUnitQuadVS = gpu::Shader::createVertex(std::string(DrawTransformUnitQuad_vert)); + _drawTransformUnitQuadVS = DrawTransformUnitQuad_vert::getShader(); } return _drawTransformUnitQuadVS; } ShaderPointer StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS() { if (!_drawTexcoordRectTransformUnitQuadVS) { - _drawTexcoordRectTransformUnitQuadVS = gpu::Shader::createVertex(std::string(DrawTexcoordRectTransformUnitQuad_vert)); + _drawTexcoordRectTransformUnitQuadVS = DrawTexcoordRectTransformUnitQuad_vert::getShader(); } return _drawTexcoordRectTransformUnitQuadVS; } ShaderPointer StandardShaderLib::getDrawViewportQuadTransformTexcoordVS() { if (!_drawViewportQuadTransformTexcoordVS) { - _drawViewportQuadTransformTexcoordVS = gpu::Shader::createVertex(std::string(DrawViewportQuadTransformTexcoord_vert)); + _drawViewportQuadTransformTexcoordVS = DrawViewportQuadTransformTexcoord_vert::getShader(); } return _drawViewportQuadTransformTexcoordVS; } ShaderPointer StandardShaderLib::getDrawVertexPositionVS() { if (!_drawVertexPositionVS) { - _drawVertexPositionVS = gpu::Shader::createVertex(std::string(DrawVertexPosition_vert)); + _drawVertexPositionVS = DrawVertexPosition_vert::getShader(); } return _drawVertexPositionVS; } ShaderPointer StandardShaderLib::getDrawTransformVertexPositionVS() { if (!_drawTransformVertexPositionVS) { - _drawTransformVertexPositionVS = gpu::Shader::createVertex(std::string(DrawTransformVertexPosition_vert)); + _drawTransformVertexPositionVS = DrawTransformVertexPosition_vert::getShader(); } return _drawTransformVertexPositionVS; } @@ -122,42 +122,42 @@ ShaderPointer StandardShaderLib::getDrawNadaPS() { ShaderPointer StandardShaderLib::getDrawWhitePS() { if (!_drawWhitePS) { - _drawWhitePS = gpu::Shader::createPixel(std::string(DrawWhite_frag)); + _drawWhitePS = DrawWhite_frag::getShader(); } return _drawWhitePS; } ShaderPointer StandardShaderLib::getDrawColorPS() { if (!_drawColorPS) { - _drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag)); + _drawColorPS = DrawColor_frag::getShader(); } return _drawColorPS; } ShaderPointer StandardShaderLib::getDrawTexturePS() { if (!_drawTexturePS) { - _drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag)); + _drawTexturePS = DrawTexture_frag::getShader(); } return _drawTexturePS; } ShaderPointer StandardShaderLib::getDrawTextureMirroredXPS() { if (!_drawTextureMirroredXPS) { - _drawTextureMirroredXPS = gpu::Shader::createPixel(std::string(DrawTextureMirroredX_frag)); + _drawTextureMirroredXPS = DrawTextureMirroredX_frag::getShader(); } return _drawTextureMirroredXPS; } ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() { if (!_drawTextureOpaquePS) { - _drawTextureOpaquePS = gpu::Shader::createPixel(std::string(DrawTextureOpaque_frag)); + _drawTextureOpaquePS = DrawTextureOpaque_frag::getShader(); } return _drawTextureOpaquePS; } ShaderPointer StandardShaderLib::getDrawColoredTexturePS() { if (!_drawColoredTexturePS) { - _drawColoredTexturePS = gpu::Shader::createPixel(std::string(DrawColoredTexture_frag)); + _drawColoredTexturePS = DrawColoredTexture_frag::getShader(); } return _drawColoredTexturePS; } diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 7e03890c85..e2877d4bb4 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -15,8 +15,8 @@ #include #include -INCLUDE_SHADER(skybox_vert) -INCLUDE_SHADER(skybox_frag) +#include "skybox_vert.h" +#include "skybox_frag.h" using namespace model; @@ -85,8 +85,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky static std::once_flag once; std::call_once(once, [&] { { - auto skyVS = gpu::Shader::createVertex(std::string(skybox_vert)); - auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag)); + auto skyVS = skybox_vert::getShader(); + auto skyFS = skybox_frag::getShader(); auto skyShader = gpu::Shader::createProgram(skyVS, skyFS); gpu::Shader::BindingSet bindings; diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index c0ebdbb186..ba29768f68 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -20,7 +20,7 @@ #include #include -INCLUDE_SHADER(ProceduralCommon_frag) +#include "ProceduralCommon_frag.h" #include "Logging.h" @@ -241,7 +241,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm std::string fragmentShaderSource = _fragmentSource; size_t replaceIndex = fragmentShaderSource.find(PROCEDURAL_COMMON_BLOCK); if (replaceIndex != std::string::npos) { - fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag); + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag.h"; } replaceIndex = fragmentShaderSource.find(PROCEDURAL_VERSION); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 452d3f639a..60fde7bd14 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -15,8 +15,8 @@ #include #include -INCLUDE_SHADER(skybox_vert) -INCLUDE_SHADER(skybox_frag) +#include "skybox_vert.h" +#include "skybox_frag.h" ProceduralSkybox::ProceduralSkybox() : model::Skybox() { _procedural._vertexSource = skybox_vert; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index f5b4f0f6bb..83753131c8 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -28,11 +28,11 @@ #include "DependencyManager.h" #include "ViewFrustum.h" -INCLUDE_SHADER(ssao_makePyramid_frag) -INCLUDE_SHADER(ssao_makeOcclusion_frag) -INCLUDE_SHADER(ssao_debugOcclusion_frag) -INCLUDE_SHADER(ssao_makeHorizontalBlur_frag) -INCLUDE_SHADER(ssao_makeVerticalBlur_frag) +#include "ssao_makePyramid_frag.h" +#include "ssao_makeOcclusion_frag.h" +#include "ssao_debugOcclusion_frag.h" +#include "ssao_makeHorizontalBlur_frag.h" +#include "ssao_makeVerticalBlur_frag.h" AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 53ed3ea9a0..382b4e2d93 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -17,8 +17,8 @@ #include "AnimDebugDraw.h" -INCLUDE_SHADER(animdebugdraw_vert) -INCLUDE_SHADER(animdebugdraw_frag) +#include "animdebugdraw_vert.h" +#include "animdebugdraw_frag.h" class AnimDebugDrawData { public: diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 078e906fad..70c2e3b5ce 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -23,9 +23,9 @@ #include "ViewFrustum.h" #include "GeometryCache.h" -INCLUDE_SHADER(fxaa_vert) -INCLUDE_SHADER(fxaa_frag) -INCLUDE_SHADER(fxaa_blend_frag) +#include "fxaa_vert.h" +#include "fxaa_frag.h" +#include "fxaa_blend_frag.h" Antialiasing::Antialiasing() { diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index ace347f99e..9d9367a6d5 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -16,8 +16,8 @@ #include #include -INCLUDE_SHADER(BloomThreshold_frag) -INCLUDE_SHADER(BloomApply_frag) +#include "BloomThreshold_frag.h" +#include "BloomApply_frag.h" #define BLOOM_BLUR_LEVEL_COUNT 3 diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 05cd6a6f0e..fe03ead4e1 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -23,8 +23,8 @@ #include "TextureCache.h" #include "DeferredLightingEffect.h" -INCLUDE_SHADER(debug_deferred_buffer_vert) -INCLUDE_SHADER(debug_deferred_buffer_frag) +#include "debug_deferred_buffer_vert.h" +#include "debug_deferred_buffer_frag.h" using namespace render; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8ec2c74353..81a33f17e3 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -24,18 +24,18 @@ #include "TextureCache.h" #include "FramebufferCache.h" -INCLUDE_SHADER(deferred_light_vert) -INCLUDE_SHADER(deferred_light_point_vert) -INCLUDE_SHADER(deferred_light_spot_vert) +#include "deferred_light_vert.h" +#include "deferred_light_point_vert.h" +#include "deferred_light_spot_vert.h" -INCLUDE_SHADER(directional_ambient_light_frag) -INCLUDE_SHADER(directional_skybox_light_frag) +#include "directional_ambient_light_frag.h" +#include "directional_skybox_light_frag.h" -INCLUDE_SHADER(directional_ambient_light_shadow_frag) -INCLUDE_SHADER(directional_skybox_light_shadow_frag) +#include "directional_ambient_light_shadow_frag.h" +#include "directional_skybox_light_shadow_frag.h" -INCLUDE_SHADER(local_lights_shading_frag) -INCLUDE_SHADER(local_lights_drawOutline_frag) +#include "local_lights_shading_frag.h" +#include "local_lights_drawOutline_frag.h" using namespace render; diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index dc1893b347..da07f5bd9b 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -19,7 +19,7 @@ #include "HazeStage.h" #include "LightStage.h" -INCLUDE_SHADER(Haze_frag) +#include "Haze_frag.h" void HazeConfig::setHazeColor(const glm::vec3 value) { hazeColor = value; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index ac8b300e49..2616d08600 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -34,21 +34,21 @@ #include "model/TextureMap.h" #include "render/Args.h" -INCLUDE_SHADER(standardTransformPNTC_vert) -INCLUDE_SHADER(standardDrawTexture_frag) +#include "standardTransformPNTC_vert.h" +#include "standardDrawTexture_frag.h" -INCLUDE_SHADER(simple_vert) -INCLUDE_SHADER(simple_textured_frag) -INCLUDE_SHADER(simple_textured_unlit_frag) -INCLUDE_SHADER(simple_fade_vert) -INCLUDE_SHADER(simple_textured_fade_frag) -INCLUDE_SHADER(simple_textured_unlit_fade_frag) -INCLUDE_SHADER(simple_opaque_web_browser_frag) -INCLUDE_SHADER(simple_transparent_web_browser_frag) -INCLUDE_SHADER(glowLine_vert) -INCLUDE_SHADER(glowLine_frag) +#include "simple_vert.h" +#include "simple_textured_frag.h" +#include "simple_textured_unlit_frag.h" +#include "simple_fade_vert.h" +#include "simple_textured_fade_frag.h" +#include "simple_textured_unlit_fade_frag.h" +#include "simple_opaque_web_browser_frag.h" +#include "simple_transparent_web_browser_frag.h" +#include "glowLine_vert.h" +#include "glowLine_frag.h" -INCLUDE_SHADER(grid_frag) +#include "grid_frag.h" //#define WANT_DEBUG diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 69feffb055..fee1f4a568 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -22,13 +22,13 @@ #include -INCLUDE_SHADER(surfaceGeometry_copyDepth_frag) -INCLUDE_SHADER(debug_deferred_buffer_vert) -INCLUDE_SHADER(debug_deferred_buffer_frag) -INCLUDE_SHADER(Highlight_frag) -INCLUDE_SHADER(Highlight_filled_frag) -INCLUDE_SHADER(Highlight_aabox_vert) -INCLUDE_SHADER(nop_frag) +#include "surfaceGeometry_copyDepth_frag.h" +#include "debug_deferred_buffer_vert.h" +#include "debug_deferred_buffer_frag.h" +#include "Highlight_frag.h" +#include "Highlight_filled_frag.h" +#include "Highlight_aabox_vert.h" +#include "nop_frag.h" using namespace render; @@ -547,10 +547,10 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const return task.addJob("TransparentSelection", selectItemInput); } -INCLUDE_SHADER(model_shadow_vert) -INCLUDE_SHADER(skin_model_shadow_vert) +#include "model_shadow_vert.h" +#include "skin_model_shadow_vert.h" -INCLUDE_SHADER(model_shadow_frag) +#include "model_shadow_frag.h" void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index 79d755683f..d6ac7fd2e2 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -18,15 +18,15 @@ #include "StencilMaskPass.h" -INCLUDE_SHADER(lightClusters_drawGrid_vert) -INCLUDE_SHADER(lightClusters_drawGrid_frag) +#include "lightClusters_drawGrid_vert.h" +#include "lightClusters_drawGrid_frag.h" -//INCLUDE_SHADER(lightClusters_drawClusterFromDepth_vert) -INCLUDE_SHADER(lightClusters_drawClusterFromDepth_frag) +//#include "lightClusters_drawClusterFromDepth_vert.h" +#include "lightClusters_drawClusterFromDepth_frag.h" -INCLUDE_SHADER(lightClusters_drawClusterContent_vert) -INCLUDE_SHADER(lightClusters_drawClusterContent_frag) +#include "lightClusters_drawClusterContent_vert.h" +#include "lightClusters_drawClusterContent_frag.h" enum LightClusterGridShader_MapSlot { DEFERRED_BUFFER_LINEAR_DEPTH_UNIT = 0, diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 8d02281d05..16e739f432 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -24,7 +24,7 @@ #include -INCLUDE_SHADER(nop_frag) +#include "nop_frag.h" using namespace render; extern void initForwardPipelines(ShapePlumber& plumber); @@ -125,7 +125,7 @@ void Draw::run(const RenderContextPointer& renderContext, const gpu::PipelinePointer Stencil::getPipeline() { if (!_stencilPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(nop_frag)); + auto ps = gpu::Shader::createPixel(std::string(nop_frag); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram(*program); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 62252d3a0b..7f644add72 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -20,86 +20,86 @@ #include "TextureCache.h" #include "render/DrawTask.h" -INCLUDE_SHADER(model_vert) -INCLUDE_SHADER(model_normal_map_vert) -INCLUDE_SHADER(model_lightmap_vert) -INCLUDE_SHADER(model_lightmap_normal_map_vert) -INCLUDE_SHADER(skin_model_vert) -INCLUDE_SHADER(skin_model_normal_map_vert) +#include "model_vert.h" +#include "model_normal_map_vert.h" +#include "model_lightmap_vert.h" +#include "model_lightmap_normal_map_vert.h" +#include "skin_model_vert.h" +#include "skin_model_normal_map_vert.h" -INCLUDE_SHADER(model_lightmap_fade_vert) -INCLUDE_SHADER(model_lightmap_normal_map_fade_vert) -INCLUDE_SHADER(skin_model_fade_vert) -INCLUDE_SHADER(skin_model_normal_map_fade_vert) +#include "model_lightmap_fade_vert.h" +#include "model_lightmap_normal_map_fade_vert.h" +#include "skin_model_fade_vert.h" +#include "skin_model_normal_map_fade_vert.h" -INCLUDE_SHADER(simple_vert) -INCLUDE_SHADER(simple_textured_frag) -INCLUDE_SHADER(simple_textured_unlit_frag) -INCLUDE_SHADER(simple_transparent_textured_frag) -INCLUDE_SHADER(simple_transparent_textured_unlit_frag) +#include "simple_vert.h" +#include "simple_textured_frag.h" +#include "simple_textured_unlit_frag.h" +#include "simple_transparent_textured_frag.h" +#include "simple_transparent_textured_unlit_frag.h" -INCLUDE_SHADER(simple_fade_vert) -INCLUDE_SHADER(simple_textured_fade_frag) -INCLUDE_SHADER(simple_textured_unlit_fade_frag) -INCLUDE_SHADER(simple_transparent_textured_fade_frag) -INCLUDE_SHADER(simple_transparent_textured_unlit_fade_frag) +#include "simple_fade_vert.h" +#include "simple_textured_fade_frag.h" +#include "simple_textured_unlit_fade_frag.h" +#include "simple_transparent_textured_fade_frag.h" +#include "simple_transparent_textured_unlit_fade_frag.h" -INCLUDE_SHADER(model_frag) -INCLUDE_SHADER(model_unlit_frag) -INCLUDE_SHADER(model_normal_map_frag) -INCLUDE_SHADER(model_normal_specular_map_frag) -INCLUDE_SHADER(model_specular_map_frag) +#include "model_frag.h" +#include "model_unlit_frag.h" +#include "model_normal_map_frag.h" +#include "model_normal_specular_map_frag.h" +#include "model_specular_map_frag.h" -INCLUDE_SHADER(model_fade_vert) -INCLUDE_SHADER(model_normal_map_fade_vert) +#include "model_fade_vert.h" +#include "model_normal_map_fade_vert.h" -INCLUDE_SHADER(model_fade_frag) -INCLUDE_SHADER(model_unlit_fade_frag) -INCLUDE_SHADER(model_normal_map_fade_frag) -INCLUDE_SHADER(model_normal_specular_map_fade_frag) -INCLUDE_SHADER(model_specular_map_fade_frag) +#include "model_fade_frag.h" +#include "model_unlit_fade_frag.h" +#include "model_normal_map_fade_frag.h" +#include "model_normal_specular_map_fade_frag.h" +#include "model_specular_map_fade_frag.h" -INCLUDE_SHADER(forward_model_frag) -INCLUDE_SHADER(forward_model_unlit_frag) -INCLUDE_SHADER(forward_model_normal_map_frag) -INCLUDE_SHADER(forward_model_normal_specular_map_frag) -INCLUDE_SHADER(forward_model_specular_map_frag) +#include "forward_model_frag.h" +#include "forward_model_unlit_frag.h" +#include "forward_model_normal_map_frag.h" +#include "forward_model_normal_specular_map_frag.h" +#include "forward_model_specular_map_frag.h" -INCLUDE_SHADER(model_lightmap_frag) -INCLUDE_SHADER(model_lightmap_normal_map_frag) -INCLUDE_SHADER(model_lightmap_normal_specular_map_frag) -INCLUDE_SHADER(model_lightmap_specular_map_frag) -INCLUDE_SHADER(model_translucent_frag) -INCLUDE_SHADER(model_translucent_unlit_frag) +#include "model_lightmap_frag.h" +#include "model_lightmap_normal_map_frag.h" +#include "model_lightmap_normal_specular_map_frag.h" +#include "model_lightmap_specular_map_frag.h" +#include "model_translucent_frag.h" +#include "model_translucent_unlit_frag.h" -INCLUDE_SHADER(model_lightmap_fade_frag) -INCLUDE_SHADER(model_lightmap_normal_map_fade_frag) -INCLUDE_SHADER(model_lightmap_normal_specular_map_fade_frag) -INCLUDE_SHADER(model_lightmap_specular_map_fade_frag) -INCLUDE_SHADER(model_translucent_fade_frag) -INCLUDE_SHADER(model_translucent_unlit_fade_frag) +#include "model_lightmap_fade_frag.h" +#include "model_lightmap_normal_map_fade_frag.h" +#include "model_lightmap_normal_specular_map_fade_frag.h" +#include "model_lightmap_specular_map_fade_frag.h" +#include "model_translucent_fade_frag.h" +#include "model_translucent_unlit_fade_frag.h" -INCLUDE_SHADER(overlay3D_vert) -INCLUDE_SHADER(overlay3D_frag) -INCLUDE_SHADER(overlay3D_model_frag) -INCLUDE_SHADER(overlay3D_model_translucent_frag) -INCLUDE_SHADER(overlay3D_translucent_frag) -INCLUDE_SHADER(overlay3D_unlit_frag) -INCLUDE_SHADER(overlay3D_translucent_unlit_frag) -INCLUDE_SHADER(overlay3D_model_unlit_frag) -INCLUDE_SHADER(overlay3D_model_translucent_unlit_frag) +#include "overlay3D_vert.h" +#include "overlay3D_frag.h" +#include "overlay3D_model_frag.h" +#include "overlay3D_model_translucent_frag.h" +#include "overlay3D_translucent_frag.h" +#include "overlay3D_unlit_frag.h" +#include "overlay3D_translucent_unlit_frag.h" +#include "overlay3D_model_unlit_frag.h" +#include "overlay3D_model_translucent_unlit_frag.h" -INCLUDE_SHADER(model_shadow_vert) -INCLUDE_SHADER(skin_model_shadow_vert) +#include "model_shadow_vert.h" +#include "skin_model_shadow_vert.h" -INCLUDE_SHADER(model_shadow_frag) -INCLUDE_SHADER(skin_model_shadow_frag) +#include "model_shadow_frag.h" +#include "skin_model_shadow_frag.h" -INCLUDE_SHADER(model_shadow_fade_vert) -INCLUDE_SHADER(skin_model_shadow_fade_vert) +#include "model_shadow_fade_vert.h" +#include "skin_model_shadow_fade_vert.h" -INCLUDE_SHADER(model_shadow_fade_frag) -INCLUDE_SHADER(skin_model_shadow_fade_frag) +#include "model_shadow_fade_frag.h" +#include "skin_model_shadow_fade_frag.h" using namespace render; using namespace std::placeholders; diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index e2a8b31f08..80c97cf29f 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -17,7 +17,7 @@ #include -INCLUDE_SHADER(stencil_drawMask_frag) +#include "stencil_drawMask_frag.h" using namespace render; @@ -60,7 +60,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() { gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() { if (!_paintStencilPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(stencil_drawMask_frag)); + auto ps = gpu::Shader::createPixel(std::string(stencil_drawMask_frag); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram((*program)); diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index e8d7e23ec2..1786898e57 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -17,11 +17,11 @@ #include "DeferredLightingEffect.h" -INCLUDE_SHADER(subsurfaceScattering_makeProfile_frag) -INCLUDE_SHADER(subsurfaceScattering_makeLUT_frag) -INCLUDE_SHADER(subsurfaceScattering_makeSpecularBeckmann_frag) +#include "subsurfaceScattering_makeProfile_frag.h" +#include "subsurfaceScattering_makeLUT_frag.h" +#include "subsurfaceScattering_makeSpecularBeckmann_frag.h" -INCLUDE_SHADER(subsurfaceScattering_drawScattering_frag) +#include "subsurfaceScattering_drawScattering_frag.h" enum ScatteringShaderBufferSlots { ScatteringTask_FrameTransformSlot = 0, diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 6ad8dc6137..af6ff09082 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -25,10 +25,10 @@ const int SurfaceGeometryPass_ParamsSlot = 1; const int SurfaceGeometryPass_DepthMapSlot = 0; const int SurfaceGeometryPass_NormalMapSlot = 1; -INCLUDE_SHADER(surfaceGeometry_makeLinearDepth_frag) -INCLUDE_SHADER(surfaceGeometry_downsampleDepthNormal_frag) +#include "surfaceGeometry_makeLinearDepth_frag.h" +#include "surfaceGeometry_downsampleDepthNormal_frag.h" -INCLUDE_SHADER(surfaceGeometry_makeCurvature_frag) +#include "surfaceGeometry_makeCurvature_frag.h" @@ -212,7 +212,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { if (!_linearDepthPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeLinearDepth_frag)); + auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeLinearDepth_frag); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -239,7 +239,7 @@ const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { const gpu::PipelinePointer& LinearDepthPass::getDownsamplePipeline() { if (!_downsamplePipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_downsampleDepthNormal_frag)); + auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_downsampleDepthNormal_frag); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -540,7 +540,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext, const gpu::PipelinePointer& SurfaceGeometryPass::getCurvaturePipeline() { if (!_curvaturePipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeCurvature_frag)); + auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeCurvature_frag); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 06e041685a..9c85952107 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -25,8 +25,8 @@ #include "MatrixStack.h" #include "RenderUtilsLogging.h" -INCLUDE_SHADER(sdf_text3D_vert) -INCLUDE_SHADER(sdf_text3D_frag) +#include "sdf_text3D_vert.h" +#include "sdf_text3D_frag.h" #include "GeometryCache.h" diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 3d3a11f7b3..6cb9541dae 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -17,7 +17,7 @@ #include "StencilMaskPass.h" #include "FramebufferCache.h" -INCLUDE_SHADER(toneMapping_frag) +#include "toneMapping_frag.h" const int ToneMappingEffect_ParamsSlot = 0; const int ToneMappingEffect_LightingMapSlot = 0; @@ -28,7 +28,7 @@ ToneMappingEffect::ToneMappingEffect() { } void ToneMappingEffect::init() { - auto blitPS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(toneMapping_frag))); + auto blitPS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(toneMapping_frag)); auto blitVS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(blitVS, blitPS)); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 77b5c492d3..c0d01c2eaf 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -20,9 +20,9 @@ #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" -INCLUDE_SHADER(zone_drawKeyLight_frag) -INCLUDE_SHADER(zone_drawAmbient_frag) -INCLUDE_SHADER(zone_drawSkybox_frag) +#include "zone_drawKeyLight_frag.h" +#include "zone_drawAmbient_frag.h" +#include "zone_drawSkybox_frag.h" using namespace render; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 9e0fbd1522..8449c58c7c 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -7,9 +7,9 @@ #include -INCLUDE_SHADER(sdf_text3D_vert) -INCLUDE_SHADER(sdf_text3D_frag) -INCLUDE_SHADER(sdf_text3D_transparent_frag) +#include "sdf_text3D_vert.h" +#include "sdf_text3D_frag.h" +#include "sdf_text3D_transparent_frag.h" #include "../RenderUtilsLogging.h" #include "FontFamilies.h" diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 4b5d6da8e3..2be6f8fad2 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -13,11 +13,11 @@ #include #include -INCLUDE_SHADER(blurGaussianV_frag) -INCLUDE_SHADER(blurGaussianH_frag) +#include "blurGaussianV_frag.h" +#include "blurGaussianH_frag.h" -INCLUDE_SHADER(blurGaussianDepthAwareV_frag) -INCLUDE_SHADER(blurGaussianDepthAwareH_frag) +#include "blurGaussianDepthAwareV_frag.h" +#include "blurGaussianDepthAwareH_frag.h" using namespace render; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 7823d85dae..36663a454a 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -22,12 +22,12 @@ #include "Args.h" -INCLUDE_SHADER(drawCellBounds_vert) -INCLUDE_SHADER(drawCellBounds_frag) -INCLUDE_SHADER(drawLODReticle_frag) +#include "drawCellBounds_vert.h" +#include "drawCellBounds_frag.h" +#include "drawLODReticle_frag.h" -INCLUDE_SHADER(drawItemBounds_vert) -INCLUDE_SHADER(drawItemBounds_frag) +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" using namespace render; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 1c6d7749f8..148e104453 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -21,10 +21,10 @@ #include "Args.h" -INCLUDE_SHADER(drawItemBounds_vert) -INCLUDE_SHADER(drawItemBounds_frag) -INCLUDE_SHADER(drawItemStatus_vert) -INCLUDE_SHADER(drawItemStatus_frag) +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" +#include "drawItemStatus_vert.h" +#include "drawItemStatus_frag.h" using namespace render; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index a6d5072cca..a60bf91062 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -22,8 +22,8 @@ #include #include -INCLUDE_SHADER(drawItemBounds_vert) -INCLUDE_SHADER(drawItemBounds_frag) +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" using namespace render; diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index e0babc8b47..de37c505a6 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -23,65 +23,65 @@ #include -INCLUDE_SHADER(simple_vert) -INCLUDE_SHADER(simple_frag) -INCLUDE_SHADER(simple_textured_frag) -INCLUDE_SHADER(simple_textured_unlit_frag) +#include "simple_vert.h" +#include "simple_frag.h" +#include "simple_textured_frag.h" +#include "simple_textured_unlit_frag.h" -INCLUDE_SHADER(deferred_light_vert) -INCLUDE_SHADER(deferred_light_point_vert) -INCLUDE_SHADER(deferred_light_spot_vert) +#include "deferred_light_vert.h" +#include "deferred_light_point_vert.h" +#include "deferred_light_spot_vert.h" -INCLUDE_SHADER(directional_ambient_light_frag) -INCLUDE_SHADER(directional_skybox_light_frag) +#include "directional_ambient_light_frag.h" +#include "directional_skybox_light_frag.h" -INCLUDE_SHADER(standardTransformPNTC_vert) -INCLUDE_SHADER(standardDrawTexture_frag) +#include "standardTransformPNTC_vert.h" +#include "standardDrawTexture_frag.h" -INCLUDE_SHADER(model_vert) -INCLUDE_SHADER(model_shadow_vert) -INCLUDE_SHADER(model_normal_map_vert) -INCLUDE_SHADER(model_lightmap_vert) -INCLUDE_SHADER(model_lightmap_normal_map_vert) -INCLUDE_SHADER(skin_model_vert) -INCLUDE_SHADER(skin_model_shadow_vert) -INCLUDE_SHADER(skin_model_normal_map_vert) +#include "model_vert.h" +#include "model_shadow_vert.h" +#include "model_normal_map_vert.h" +#include "model_lightmap_vert.h" +#include "model_lightmap_normal_map_vert.h" +#include "skin_model_vert.h" +#include "skin_model_shadow_vert.h" +#include "skin_model_normal_map_vert.h" -INCLUDE_SHADER(model_frag) -INCLUDE_SHADER(model_shadow_frag) -INCLUDE_SHADER(model_normal_map_frag) -INCLUDE_SHADER(model_normal_specular_map_frag) -INCLUDE_SHADER(model_specular_map_frag) -INCLUDE_SHADER(model_lightmap_frag) -INCLUDE_SHADER(model_lightmap_normal_map_frag) -INCLUDE_SHADER(model_lightmap_normal_specular_map_frag) -INCLUDE_SHADER(model_lightmap_specular_map_frag) -INCLUDE_SHADER(model_translucent_frag) +#include "model_frag.h" +#include "model_shadow_frag.h" +#include "model_normal_map_frag.h" +#include "model_normal_specular_map_frag.h" +#include "model_specular_map_frag.h" +#include "model_lightmap_frag.h" +#include "model_lightmap_normal_map_frag.h" +#include "model_lightmap_normal_specular_map_frag.h" +#include "model_lightmap_specular_map_frag.h" +#include "model_translucent_frag.h" -INCLUDE_SHADER(textured_particle_frag) -INCLUDE_SHADER(textured_particle_vert) +#include "textured_particle_frag.h" +#include "textured_particle_vert.h" -INCLUDE_SHADER(overlay3D_vert) -INCLUDE_SHADER(overlay3D_frag) +#include "overlay3D_vert.h" +#include "overlay3D_frag.h" -INCLUDE_SHADER(skybox_vert) -INCLUDE_SHADER(skybox_frag) +#include "skybox_vert.h" +#include "skybox_frag.h" -INCLUDE_SHADER(DrawTransformUnitQuad_vert) -INCLUDE_SHADER(DrawTexcoordRectTransformUnitQuad_vert) -INCLUDE_SHADER(DrawViewportQuadTransformTexcoord_vert) -INCLUDE_SHADER(DrawTexture_frag) -INCLUDE_SHADER(DrawTextureOpaque_frag) -INCLUDE_SHADER(DrawColoredTexture_frag) +#include "DrawTransformUnitQuad_vert.h" +#include "DrawTexcoordRectTransformUnitQuad_vert.h" +#include "DrawViewportQuadTransformTexcoord_vert.h" +#include "DrawTexture_frag.h" +#include "DrawTextureOpaque_frag.h" +#include "DrawColoredTexture_frag.h" -INCLUDE_SHADER(sdf_text3D_vert) -INCLUDE_SHADER(sdf_text3D_frag) +#include "sdf_text3D_vert.h" +#include "sdf_text3D_frag.h" -INCLUDE_SHADER(paintStroke_vert) -INCLUDE_SHADER(paintStroke_frag) +#include "paintStroke_vert.h" +#include "paintStroke_frag.h" -INCLUDE_SHADER(polyvox_vert) -INCLUDE_SHADER(polyvox_frag) +#include "polyvox_vert.h" +#include "polyvox_frag.h" // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow { @@ -159,54 +159,54 @@ void QTestWindow::draw() { static std::once_flag once; std::call_once(once, [&]{ - testShaderBuild(sdf_text3D_vert, sdf_text3D_frag); + testShaderBuild(sdf_text3D_vert, sdf_text3D_frag.h"; - testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag); - testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag); - testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag); - testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag); - testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag); + testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag.h"; + testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag.h"; + testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag.h"; + testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag.h"; + testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag.h"; - testShaderBuild(skybox_vert, skybox_frag); - testShaderBuild(simple_vert, simple_frag); - testShaderBuild(simple_vert, simple_textured_frag); - testShaderBuild(simple_vert, simple_textured_unlit_frag); - testShaderBuild(deferred_light_vert, directional_ambient_light_frag); - testShaderBuild(deferred_light_vert, directional_skybox_light_frag); - testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag); - testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag); + testShaderBuild(skybox_vert, skybox_frag.h"; + testShaderBuild(simple_vert, simple_frag.h"; + testShaderBuild(simple_vert, simple_textured_frag.h"; + testShaderBuild(simple_vert, simple_textured_unlit_frag.h"; + testShaderBuild(deferred_light_vert, directional_ambient_light_frag.h"; + testShaderBuild(deferred_light_vert, directional_skybox_light_frag.h"; + testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag.h"; + testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag.h"; - testShaderBuild(model_vert, model_frag); - testShaderBuild(model_normal_map_vert, model_normal_map_frag); - testShaderBuild(model_vert, model_specular_map_frag); - testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag); - testShaderBuild(model_vert, model_translucent_frag); - testShaderBuild(model_normal_map_vert, model_translucent_frag); - testShaderBuild(model_lightmap_vert, model_lightmap_frag); - testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag); - testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag); - testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag); + testShaderBuild(model_vert, model_frag.h"; + testShaderBuild(model_normal_map_vert, model_normal_map_frag.h"; + testShaderBuild(model_vert, model_specular_map_frag.h"; + testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag.h"; + testShaderBuild(model_vert, model_translucent_frag.h"; + testShaderBuild(model_normal_map_vert, model_translucent_frag.h"; + testShaderBuild(model_lightmap_vert, model_lightmap_frag.h"; + testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag.h"; + testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag.h"; + testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag.h"; - testShaderBuild(skin_model_vert, model_frag); - testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag); - testShaderBuild(skin_model_vert, model_specular_map_frag); - testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag); - testShaderBuild(skin_model_vert, model_translucent_frag); - testShaderBuild(skin_model_normal_map_vert, model_translucent_frag); + testShaderBuild(skin_model_vert, model_frag.h"; + testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag.h"; + testShaderBuild(skin_model_vert, model_specular_map_frag.h"; + testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag.h"; + testShaderBuild(skin_model_vert, model_translucent_frag.h"; + testShaderBuild(skin_model_normal_map_vert, model_translucent_frag.h"; - testShaderBuild(model_shadow_vert, model_shadow_frag); - testShaderBuild(textured_particle_vert, textured_particle_frag); + testShaderBuild(model_shadow_vert, model_shadow_frag.h"; + testShaderBuild(textured_particle_vert, textured_particle_frag.h"; /* FIXME: Bring back the ssao shader tests - testShaderBuild(gaussian_blur_vertical_vert, gaussian_blur_frag); - testShaderBuild(gaussian_blur_horizontal_vert, gaussian_blur_frag); - testShaderBuild(ambient_occlusion_vert, ambient_occlusion_frag); - testShaderBuild(ambient_occlusion_vert, occlusion_blend_frag); + testShaderBuild(gaussian_blur_vertical_vert, gaussian_blur_frag.h"; + testShaderBuild(gaussian_blur_horizontal_vert, gaussian_blur_frag.h"; + testShaderBuild(ambient_occlusion_vert, ambient_occlusion_frag.h"; + testShaderBuild(ambient_occlusion_vert, occlusion_blend_frag.h"; */ - testShaderBuild(overlay3D_vert, overlay3D_frag); + testShaderBuild(overlay3D_vert, overlay3D_frag.h"; - testShaderBuild(paintStroke_vert,paintStroke_frag); - testShaderBuild(polyvox_vert, polyvox_frag); + testShaderBuild(paintStroke_vert,paintStroke_frag.h"; + testShaderBuild(polyvox_vert, polyvox_frag.h"; }); _context.swapBuffers(this); diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index 8e6ca31c71..2092bc0ea2 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -44,6 +44,18 @@ int main (int argc, char** argv) { EXIT, } mode = READY; + enum Type { + VERTEX = 0, + FRAGMENT, + GEOMETRY, + } type = VERTEX; + static const char* shaderTypeString[] = { + "VERTEX", "PIXEL", "GEOMETRY" + }; + static const char* shaderCreateString[] = { + "Vertex", "Pixel", "Geometry" + }; + for (int ii = 1; (mode != EXIT) && (ii < argc); ii++) { inputs.push_back(argv[ii]); @@ -66,6 +78,15 @@ int main (int argc, char** argv) { } else if (inputs.back() == "-c++") { makeCPlusPlus = true; mode = READY; + } else if (inputs.back() == "-vert") { + type = VERTEX; + mode = READY; + } else if (inputs.back() == "-frag") { + type = FRAGMENT; + mode = READY; + } else if (inputs.back() == "-geom") { + type = GEOMETRY; + mode = READY; } else { // just grabbed the source filename, stop parameter parsing srcFilename = inputs.back(); @@ -186,7 +207,6 @@ int main (int argc, char** argv) { scribe->displayTree(cerr, level); } - std::ostringstream targetStringStream; if (makeCPlusPlus) { // Because there is a maximum size for literal strings declared in source we need to partition the // full source string stream into pages that seems to be around that value... @@ -208,32 +228,108 @@ int main (int argc, char** argv) { pageSize += lineSize; } - targetStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; - targetStringStream << "extern const char " << targetName << "[] = \n"; + std::stringstream headerStringStream; + std::stringstream sourceStringStream; + std::string headerFileName = destFilename + ".h"; + std::string sourceFileName = destFilename + ".cpp"; + sourceStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; + + // Write header file + headerStringStream << "#ifndef " << targetName << "_h" << std::endl; + headerStringStream << "#define " << targetName << "_h\n" << std::endl; + headerStringStream << "#include \n" << std::endl; + headerStringStream << "class " << targetName << " {" << std::endl; + headerStringStream << "public:" << std::endl; + headerStringStream << "\tstatic gpu::Shader::Type getType() { return gpu::Shader::" << shaderTypeString[type] << "; }" << std::endl; + headerStringStream << "\tstatic const char* getSource() { return _source; }" << std::endl; + headerStringStream << "\tstatic gpu::ShaderPointer getShader();" << std::endl; + headerStringStream << "private:" << std::endl; + headerStringStream << "\tstatic const char* _source;" << std::endl; + headerStringStream << "\tstatic gpu::ShaderPointer _shader;" << std::endl; + headerStringStream << "};\n" << std::endl; + headerStringStream << "#endif // " << targetName << "_h" << std::endl; + + bool mustOutputHeader = destFilename.empty(); + // Compare with existing file + { + std::fstream headerFile; + headerFile.open(headerFileName, std::fstream::in); + if (headerFile.is_open()) { + // Skip first line + std::string line; + std::stringstream previousHeaderStringStream; + std::getline(headerFile, line); + + previousHeaderStringStream << headerFile.rdbuf(); + mustOutputHeader = mustOutputHeader || previousHeaderStringStream.str() != headerStringStream.str(); + } else { + mustOutputHeader = true; + } + } + + if (mustOutputHeader) { + if (!destFilename.empty()) { + // File content has changed so write it + std::fstream headerFile; + headerFile.open(headerFileName, std::fstream::out); + if (headerFile.is_open()) { + // First line contains the date of modification + headerFile << sourceStringStream.str(); + headerFile << headerStringStream.str(); + } else { + cerr << "Scribe output file <" << headerFileName << "> failed to open." << endl; + return 0; + } + } else { + cerr << sourceStringStream.str(); + cerr << headerStringStream.str(); + } + } + + // Write source file + sourceStringStream << "#include \"" << targetName << ".h\"\n" << std::endl; + sourceStringStream << "gpu::ShaderPointer " << targetName << "::_shader;" << std::endl; + sourceStringStream << "const char* " << targetName << "::_source = "; // Write the pages content for (auto page : pages) { - targetStringStream << "R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\"\n"; + sourceStringStream << "R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\"\n"; } - targetStringStream << ";\n" << std::endl << std::endl; - targetStringStream << "// Hack to fix Android link error by forcing a reference to global variable\n"; - targetStringStream << "class " << targetName << "_used {\npublic:\nstatic const char* get() { return " << targetName << "; }\n};\n" << std::endl; - } else { - targetStringStream << destStringStream.str(); - } + sourceStringStream << ";\n" << std::endl << std::endl; - // Destination stream - if (!destFilename.empty()) { - std::fstream destFileStream; - destFileStream.open(destFilename, std::fstream::out); - if (!destFileStream.is_open()) { - cerr << "Scribe output file " << destFilename << "> failed to open." << endl; - return 0; + sourceStringStream << "gpu::ShaderPointer " << targetName << "::getShader() {" << std::endl; + sourceStringStream << "\tif (_shader==nullptr) {" << std::endl; + sourceStringStream << "\t\t_shader = gpu::Shader::create" << shaderCreateString[type] << "(std::string(_source));" << std::endl; + sourceStringStream << "\t}" << std::endl; + sourceStringStream << "\treturn _shader;" << std::endl; + sourceStringStream << "}\n" << std::endl; + + // Destination stream + if (!destFilename.empty()) { + std::fstream sourceFile; + sourceFile.open(sourceFileName, std::fstream::out); + if (!sourceFile.is_open()) { + cerr << "Scribe output file <" << sourceFileName << "> failed to open." << endl; + return 0; + } + sourceFile << sourceStringStream.str(); + } else { + cerr << sourceStringStream.str(); } - - destFileStream << targetStringStream.str(); } else { - cerr << targetStringStream.str(); + // Destination stream + if (!destFilename.empty()) { + std::fstream destFileStream; + destFileStream.open(destFilename, std::fstream::out); + if (!destFileStream.is_open()) { + cerr << "Scribe output file <" << destFilename << "> failed to open." << endl; + return 0; + } + + destFileStream << destStringStream.str(); + } else { + cerr << destStringStream.str(); + } } return 0; From 49549ced171962fc090ee8bfe4d3d8e27be260f0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Jan 2018 10:52:58 +0100 Subject: [PATCH 038/381] Fixed compilation with new shader system --- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 8 +- .../RenderableParticleEffectEntityItem.cpp | 4 +- .../src/RenderablePolyLineEntityItem.cpp | 4 +- .../src/RenderablePolyVoxEntityItem.cpp | 4 +- .../src/RenderableShapeEntityItem.cpp | 8 +- .../procedural/src/procedural/Procedural.cpp | 2 +- .../src/procedural/ProceduralSkybox.cpp | 8 +- .../src/AmbientOcclusionEffect.cpp | 8 +- libraries/render-utils/src/AnimDebugDraw.cpp | 4 +- .../render-utils/src/AntialiasingEffect.cpp | 8 +- libraries/render-utils/src/BloomEffect.cpp | 4 +- .../render-utils/src/DebugDeferredBuffer.cpp | 5 +- .../src/DeferredLightingEffect.cpp | 25 ++- libraries/render-utils/src/DrawHaze.cpp | 2 +- libraries/render-utils/src/GeometryCache.cpp | 35 ++-- .../render-utils/src/HighlightEffect.cpp | 19 +- libraries/render-utils/src/LightClusters.cpp | 8 +- .../render-utils/src/RenderForwardTask.cpp | 2 +- .../render-utils/src/RenderPipelines.cpp | 154 ++++++++-------- .../render-utils/src/StencilMaskPass.cpp | 2 +- .../render-utils/src/SubsurfaceScattering.cpp | 8 +- .../render-utils/src/SurfaceGeometryPass.cpp | 6 +- .../render-utils/src/ToneMappingEffect.cpp | 2 +- libraries/render-utils/src/ZoneRenderer.cpp | 6 +- libraries/render-utils/src/text/Font.cpp | 6 +- libraries/render/src/render/BlurTask.cpp | 8 +- .../render/src/render/DrawSceneOctree.cpp | 10 +- libraries/render/src/render/DrawStatus.cpp | 8 +- libraries/render/src/render/DrawTask.cpp | 4 +- tests/shaders/src/main.cpp | 172 +++++++++--------- tools/scribe/src/main.cpp | 10 +- 31 files changed, 273 insertions(+), 281 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index f39203c89d..0a5704f6da 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -33,8 +33,8 @@ #include "../Logging.h" #include "../CompositorHelper.h" -#include "hmd_ui_vert.h" -#include "hmd_ui_frag.h" +#include "render-utils/hmd_ui_vert.h" +#include "render-utils/hmd_ui_frag.h" static const QString MONO_PREVIEW = "Mono Preview"; static const QString DISABLE_PREVIEW = "Disable Preview"; @@ -403,8 +403,8 @@ void HmdDisplayPlugin::HUDRenderer::build() { void HmdDisplayPlugin::HUDRenderer::updatePipeline() { if (!pipeline) { - auto vs = gpu::Shader::createVertex(std::string(hmd_ui_vert)); - auto ps = gpu::Shader::createPixel(std::string(hmd_ui_frag)); + auto vs = hmd_ui_vert::getShader(); + auto ps = hmd_ui_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet()); uniformsLocation = program->getUniformBuffers().findLocation("hudBuffer"); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a3e6cd4341..9981245e6f 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -36,8 +36,8 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMask(*state); - auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert)); - auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); + auto vertShader = textured_particle_vert::getShader(); + auto fragShader = textured_particle_frag::getShader(); auto program = gpu::Shader::createProgram(vertShader, fragShader); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index b362721cde..8a53c9fba5 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -48,8 +48,8 @@ struct PolyLineUniforms { static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { if (!polylinePipeline) { - auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert)); - auto PS = gpu::Shader::createPixel(std::string(paintStroke_frag)); + auto VS = paintStroke_vert::getShader(); + auto PS = paintStroke_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(VS, PS); #ifdef POLYLINE_ENTITY_USE_FADE_EFFECT auto fadeVS = gpu::Shader::createVertex(std::string(paintStroke_fade_vert)); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index cf12da86e9..379103d4b3 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1461,8 +1461,8 @@ static gpu::Stream::FormatPointer _vertexFormat; ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) { if (!_pipelines[0]) { - gpu::ShaderPointer vertexShaders[2] = { gpu::Shader::createVertex(std::string(polyvox_vert)), gpu::Shader::createVertex(std::string(polyvox_fade_vert)) }; - gpu::ShaderPointer pixelShaders[2] = { gpu::Shader::createPixel(std::string(polyvox_frag)), gpu::Shader::createPixel(std::string(polyvox_fade_frag)) }; + gpu::ShaderPointer vertexShaders[2] = { polyvox_vert::getShader(), polyvox_fade_vert::getShader() }; + gpu::ShaderPointer pixelShaders[2] = { polyvox_frag::getShader(), polyvox_fade_frag::getShader() }; gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT)); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index eddde317fe..0c4f8cbd39 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,8 +16,8 @@ #include #include -#include "simple_vert.h" -#include "simple_frag.h" +#include "render-utils/simple_vert.h" +#include "render-utils/simple_frag.h" //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT @@ -32,8 +32,8 @@ static const float SPHERE_ENTITY_SCALE = 0.5f; ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { - _procedural._vertexSource = simple_vert; - _procedural._fragmentSource = simple_frag; + _procedural._vertexSource = simple_vert::getSource(); + _procedural._fragmentSource = simple_frag::getSource(); _procedural._opaqueState->setCullMode(gpu::State::CULL_NONE); _procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL); PrepareStencil::testMaskDrawShape(*_procedural._opaqueState); diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index ba29768f68..52c56d1e7d 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -241,7 +241,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm std::string fragmentShaderSource = _fragmentSource; size_t replaceIndex = fragmentShaderSource.find(PROCEDURAL_COMMON_BLOCK); if (replaceIndex != std::string::npos) { - fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag.h"; + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag::getSource()); } replaceIndex = fragmentShaderSource.find(PROCEDURAL_VERSION); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 60fde7bd14..8fb74e0b1b 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -15,12 +15,12 @@ #include #include -#include "skybox_vert.h" -#include "skybox_frag.h" +#include "model/skybox_vert.h" +#include "model/skybox_frag.h" ProceduralSkybox::ProceduralSkybox() : model::Skybox() { - _procedural._vertexSource = skybox_vert; - _procedural._fragmentSource = skybox_frag; + _procedural._vertexSource = skybox_vert::getSource(); + _procedural._fragmentSource = skybox_frag::getSource(); // Adjust the pipeline state for background using the stencil test _procedural.setDoesFade(false); // Must match PrepareStencil::STENCIL_BACKGROUND diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 83753131c8..015f5678c8 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -263,7 +263,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { if (!_occlusionPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makeOcclusion_frag)); + auto ps = ssao_makeOcclusion_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -288,7 +288,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { if (!_hBlurPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makeHorizontalBlur_frag)); + auto ps = ssao_makeHorizontalBlur_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -311,7 +311,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { if (!_vBlurPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makeVerticalBlur_frag)); + auto ps = ssao_makeVerticalBlur_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -458,7 +458,7 @@ void DebugAmbientOcclusion::configure(const Config& config) { const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() { if (!_debugPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_debugOcclusion_frag)); + auto ps = ssao_debugOcclusion_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 382b4e2d93..dc9a4d8a1b 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -102,8 +102,8 @@ AnimDebugDraw::AnimDebugDraw() : state->setBlendFunction(false, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - auto vertShader = gpu::Shader::createVertex(std::string(animdebugdraw_vert)); - auto fragShader = gpu::Shader::createPixel(std::string(animdebugdraw_frag)); + auto vertShader = animdebugdraw_vert::getShader(); + auto fragShader = animdebugdraw_frag::getShader(); auto program = gpu::Shader::createProgram(vertShader, fragShader); _pipeline = gpu::Pipeline::create(program, state); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 70c2e3b5ce..bdd8f19a5c 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -57,8 +57,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* ar } if (!_antialiasingPipeline) { - auto vs = gpu::Shader::createVertex(std::string(fxaa_vert)); - auto ps = gpu::Shader::createPixel(std::string(fxaa_frag)); + auto vs = fxaa_vert::getShader(); + auto ps = fxaa_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -82,8 +82,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* ar const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { if (!_blendPipeline) { - auto vs = gpu::Shader::createVertex(std::string(fxaa_vert)); - auto ps = gpu::Shader::createPixel(std::string(fxaa_blend_frag)); + auto vs = fxaa_vert::getShader(); + auto ps = fxaa_blend_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 9d9367a6d5..89a83a651a 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -61,7 +61,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag)); + auto ps = BloomThreshold_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -113,7 +113,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag)); + auto ps = BloomApply_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index fe03ead4e1..24cffe2fb8 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -369,8 +369,7 @@ bool DebugDeferredBuffer::pipelineNeedsUpdate(Mode mode, std::string customFile) const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::string customFile) { if (pipelineNeedsUpdate(mode, customFile)) { - static const std::string VERTEX_SHADER { debug_deferred_buffer_vert }; - static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag }; + static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag::getSource() }; static const std::string SOURCE_PLACEHOLDER { "//SOURCE_PLACEHOLDER" }; static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, @@ -380,7 +379,7 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), getShaderSourceCode(mode, customFile)); - static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto vs = debug_deferred_buffer_vert::getShader(); const auto ps = gpu::Shader::createPixel(bakedFragmentShader); const auto program = gpu::Shader::createProgram(vs, ps); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 81a33f17e3..8c3b76f557 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -79,7 +79,7 @@ enum DeferredShader_BufferSlot { LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, }; -static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); +static void loadLightProgram(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); void DeferredLightingEffect::init() { _directionalAmbientSphereLightLocations = std::make_shared(); @@ -91,14 +91,14 @@ void DeferredLightingEffect::init() { _localLightLocations = std::make_shared(); _localLightOutlineLocations = std::make_shared(); - loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_ambient_light_frag::getShader(), false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_skybox_light_frag::getShader(), false, _directionalSkyboxLight, _directionalSkyboxLightLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_ambient_light_shadow_frag::getShader(), false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_skybox_light_shadow_frag::getShader(), false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations); - loadLightProgram(deferred_light_vert, local_lights_shading_frag, true, _localLight, _localLightLocations); - loadLightProgram(deferred_light_vert, local_lights_drawOutline_frag, true, _localLightOutline, _localLightOutlineLocations); + loadLightProgram(deferred_light_vert::getShader(), local_lights_shading_frag::getShader(), true, _localLight, _localLightLocations); + loadLightProgram(deferred_light_vert::getShader(), local_lights_drawOutline_frag::getShader(), true, _localLightOutline, _localLightOutlineLocations); } void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) { @@ -144,11 +144,8 @@ void DeferredLightingEffect::unsetKeyLightBatch(gpu::Batch& batch, int lightBuff } } -static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* fragSource, LightLocationsPtr& locations) { - auto VS = gpu::Shader::createVertex(std::string(vertSource)); - auto PS = gpu::Shader::createPixel(std::string(fragSource)); - - gpu::ShaderPointer program = gpu::Shader::createProgram(VS, PS); +static gpu::ShaderPointer makeLightProgram(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, LightLocationsPtr& locations) { + gpu::ShaderPointer program = gpu::Shader::createProgram(vertShader, fragShader); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("colorMap"), DEFERRED_BUFFER_COLOR_UNIT)); @@ -196,9 +193,9 @@ static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* f return program; } -static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) { +static void loadLightProgram(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) { - gpu::ShaderPointer program = makeLightProgram(vertSource, fragSource, locations); + gpu::ShaderPointer program = makeLightProgram(vertShader, fragShader, locations); auto state = std::make_shared(); state->setColorWriteMask(true, true, true, false); diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index da07f5bd9b..986212ca6e 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -133,7 +133,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu RenderArgs* args = renderContext->args; if (!_hazePipeline) { - gpu::ShaderPointer ps = gpu::Shader::createPixel(std::string(Haze_frag)); + gpu::ShaderPointer ps = Haze_frag::getShader(); gpu::ShaderPointer vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 2616d08600..e962c2901f 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1946,8 +1946,8 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const static std::once_flag once; std::call_once(once, [&] { auto state = std::make_shared(); - auto VS = gpu::Shader::createVertex(std::string(glowLine_vert)); - auto PS = gpu::Shader::createPixel(std::string(glowLine_frag)); + auto VS = glowLine_vert::getShader(); + auto PS = glowLine_frag::getShader(); auto program = gpu::Shader::createProgram(VS, PS); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -2002,8 +2002,8 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { if (!_standardDrawPipeline) { - auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); - auto ps = gpu::Shader::createPixel(std::string(standardDrawTexture_frag)); + auto vs = standardTransformPNTC_vert::getShader(); + auto ps = standardDrawTexture_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram((*program)); @@ -2033,8 +2033,8 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) { if (!_gridPipeline) { - auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); - auto ps = gpu::Shader::createPixel(std::string(grid_frag)); + auto vs = standardTransformPNTC_vert::getShader(); + auto ps = grid_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram((*program)); _gridSlot = program->getUniformBuffers().findLocation("gridBuffer"); @@ -2117,12 +2117,9 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } -static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable, +static void buildWebShader(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, bool blendEnable, gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) { - auto VS = gpu::Shader::createVertex(vertShaderText); - auto PS = gpu::Shader::createPixel(fragShaderText); - - shaderPointerOut = gpu::Shader::createProgram(VS, PS); + shaderPointerOut = gpu::Shader::createProgram(vertShader, fragShader); gpu::Shader::BindingSet slotBindings; gpu::Shader::makeProgram(*shaderPointerOut, slotBindings); @@ -2145,8 +2142,8 @@ void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) { gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { static std::once_flag once; std::call_once(once, [&]() { - buildWebShader(simple_vert, simple_opaque_web_browser_frag, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA); - buildWebShader(simple_vert, simple_transparent_web_browser_frag, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA); + buildWebShader(simple_vert::getShader(), simple_opaque_web_browser_frag::getShader(), false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA); + buildWebShader(simple_vert::getShader(), simple_transparent_web_browser_frag::getShader(), true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA); }); return transparent ? _simpleTransparentWebBrowserPipelineNoAA : _simpleOpaqueWebBrowserPipelineNoAA; @@ -2175,9 +2172,9 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp if (!fading) { static std::once_flag once; std::call_once(once, [&]() { - auto VS = gpu::Shader::createVertex(std::string(simple_vert)); - auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); - auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); + auto VS = simple_vert::getShader(); + auto PS = simple_textured_frag::getShader(); + auto PSUnlit = simple_textured_unlit_frag::getShader(); _simpleShader = gpu::Shader::createProgram(VS, PS); _unlitShader = gpu::Shader::createProgram(VS, PSUnlit); @@ -2190,9 +2187,9 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp } else { static std::once_flag once; std::call_once(once, [&]() { - auto VS = gpu::Shader::createVertex(std::string(simple_fade_vert)); - auto PS = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); - auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); + auto VS = simple_fade_vert::getShader(); + auto PS = simple_textured_fade_frag::getShader(); + auto PSUnlit = simple_textured_unlit_fade_frag::getShader(); _simpleFadeShader = gpu::Shader::createProgram(VS, PS); _unlitFadeShader = gpu::Shader::createProgram(VS, PSUnlit); diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index fee1f4a568..9501a74d52 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -130,8 +130,8 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c fillState->setColorWriteMask(false, false, false, false); fillState->setCullMode(gpu::State::CULL_FRONT); - auto vs = gpu::Shader::createVertex(std::string(Highlight_aabox_vert)); - auto ps = gpu::Shader::createPixel(std::string(nop_frag)); + auto vs = Highlight_aabox_vert::getShader(); + auto ps = nop_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -313,7 +313,7 @@ const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightSt state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(Highlight_frag)); + auto ps = Highlight_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -325,7 +325,7 @@ const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightSt _pipeline = gpu::Pipeline::create(program, state); - ps = gpu::Shader::createPixel(std::string(Highlight_filled_frag)); + ps = Highlight_filled_frag::getShader(); program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); @@ -385,8 +385,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons } void DebugHighlight::initializePipelines() { - static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert }; - static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag }; + static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag::getSource() }; static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, @@ -396,7 +395,7 @@ void DebugHighlight::initializePipelines() { state->setDepthTest(gpu::State::DepthTest(false, false)); state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); - const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto vs = debug_deferred_buffer_vert::getShader(); // Depth shader { @@ -553,14 +552,14 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const #include "model_shadow_frag.h" void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { - auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelVertex = model_shadow_vert::getShader(); + auto modelPixel = model_shadow_frag::getShader(); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned(), modelProgram, state); - auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); + auto skinVertex = skin_model_shadow_vert::getShader(); gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned(), diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index d6ac7fd2e2..ea02edb601 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -605,8 +605,8 @@ void DebugLightClusters::configure(const Config& config) { const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() { if (!_drawClusterGrid) { - auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert)); - auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawGrid_frag)); + auto vs = lightClusters_drawGrid_vert::getShader(); + auto ps = lightClusters_drawGrid_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -635,7 +635,7 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterFromDepthPipeline() if (!_drawClusterFromDepth) { // auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert)); auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterFromDepth_frag)); + auto ps = lightClusters_drawClusterFromDepth_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -665,7 +665,7 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterContentPipeline() { if (!_drawClusterContent) { // auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawClusterContent_vert)); auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterContent_frag)); + auto ps = lightClusters_drawClusterContent_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 16e739f432..1eeac7f449 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -125,7 +125,7 @@ void Draw::run(const RenderContextPointer& renderContext, const gpu::PipelinePointer Stencil::getPipeline() { if (!_stencilPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(nop_frag); + auto ps = nop_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram(*program); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 7f644add72..76e5cbc72a 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -117,16 +117,16 @@ void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* a void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args); void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest) { - auto vertex = gpu::Shader::createVertex(std::string(overlay3D_vert)); - auto vertexModel = gpu::Shader::createVertex(std::string(model_vert)); - auto pixel = gpu::Shader::createPixel(std::string(overlay3D_frag)); - auto pixelTranslucent = gpu::Shader::createPixel(std::string(overlay3D_translucent_frag)); - auto pixelUnlit = gpu::Shader::createPixel(std::string(overlay3D_unlit_frag)); - auto pixelTranslucentUnlit = gpu::Shader::createPixel(std::string(overlay3D_translucent_unlit_frag)); - auto pixelModel = gpu::Shader::createPixel(std::string(overlay3D_model_frag)); - auto pixelModelTranslucent = gpu::Shader::createPixel(std::string(overlay3D_model_translucent_frag)); - auto pixelModelUnlit = gpu::Shader::createPixel(std::string(overlay3D_model_unlit_frag)); - auto pixelModelTranslucentUnlit = gpu::Shader::createPixel(std::string(overlay3D_model_translucent_unlit_frag)); + auto vertex = overlay3D_vert::getShader(); + auto vertexModel = model_vert::getShader(); + auto pixel = overlay3D_frag::getShader(); + auto pixelTranslucent = overlay3D_translucent_frag::getShader(); + auto pixelUnlit = overlay3D_unlit_frag::getShader(); + auto pixelTranslucentUnlit = overlay3D_translucent_unlit_frag::getShader(); + auto pixelModel = overlay3D_model_frag::getShader(); + auto pixelModelTranslucent = overlay3D_model_translucent_frag::getShader(); + auto pixelModelUnlit = overlay3D_model_unlit_frag::getShader(); + auto pixelModelTranslucentUnlit = overlay3D_model_translucent_unlit_frag::getShader(); auto opaqueProgram = gpu::Shader::createProgram(vertex, pixel); auto translucentProgram = gpu::Shader::createProgram(vertex, pixelTranslucent); @@ -183,60 +183,60 @@ void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest) { void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { // Vertex shaders - auto simpleVertex = gpu::Shader::createVertex(std::string(simple_vert)); - auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); - auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); - auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); - auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); - auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); - auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); - auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - auto modelLightmapFadeVertex = gpu::Shader::createVertex(std::string(model_lightmap_fade_vert)); - auto modelLightmapNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_fade_vert)); - auto skinModelFadeVertex = gpu::Shader::createVertex(std::string(skin_model_fade_vert)); - auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert)); + auto simpleVertex = simple_vert::getShader(); + auto modelVertex = model_vert::getShader(); + auto modelNormalMapVertex = model_normal_map_vert::getShader(); + auto modelLightmapVertex = model_lightmap_vert::getShader(); + auto modelLightmapNormalMapVertex = model_lightmap_normal_map_vert::getShader(); + auto modelShadowVertex = model_shadow_vert::getShader(); + auto skinModelVertex = skin_model_vert::getShader(); + auto skinModelNormalMapVertex = skin_model_normal_map_vert::getShader(); + auto skinModelShadowVertex = skin_model_shadow_vert::getShader(); + auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader(); + auto modelLightmapNormalMapFadeVertex = model_lightmap_normal_map_fade_vert::getShader(); + auto skinModelFadeVertex = skin_model_fade_vert::getShader(); + auto skinModelNormalMapFadeVertex = skin_model_normal_map_fade_vert::getShader(); - auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_fade_vert)); - auto modelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_normal_map_fade_vert)); - auto simpleFadeVertex = gpu::Shader::createVertex(std::string(simple_fade_vert)); - auto modelShadowFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto skinModelShadowFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); + auto modelFadeVertex = model_fade_vert::getShader(); + auto modelNormalMapFadeVertex = model_normal_map_fade_vert::getShader(); + auto simpleFadeVertex = simple_fade_vert::getShader(); + auto modelShadowFadeVertex = model_shadow_fade_vert::getShader(); + auto skinModelShadowFadeVertex = skin_model_shadow_fade_vert::getShader(); // Pixel shaders - auto simplePixel = gpu::Shader::createPixel(std::string(simple_textured_frag)); - auto simpleUnlitPixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); - auto simpleTranslucentPixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_frag)); - auto simpleTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_unlit_frag)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); - auto modelUnlitPixel = gpu::Shader::createPixel(std::string(model_unlit_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); - auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); - auto modelTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_frag)); - auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); - auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); - auto modelLightmapNormalMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)); - auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); - auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); - auto modelLightmapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_fade_frag)); - auto modelLightmapNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_fade_frag)); - auto modelLightmapSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_fade_frag)); - auto modelLightmapNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_fade_frag)); + auto simplePixel = simple_textured_frag::getShader(); + auto simpleUnlitPixel = simple_textured_unlit_frag::getShader(); + auto simpleTranslucentPixel = simple_transparent_textured_frag::getShader(); + auto simpleTranslucentUnlitPixel = simple_transparent_textured_unlit_frag::getShader(); + auto modelPixel = model_frag::getShader(); + auto modelUnlitPixel = model_unlit_frag::getShader(); + auto modelNormalMapPixel = model_normal_map_frag::getShader(); + auto modelSpecularMapPixel = model_specular_map_frag::getShader(); + auto modelNormalSpecularMapPixel = model_normal_specular_map_frag::getShader(); + auto modelTranslucentPixel = model_translucent_frag::getShader(); + auto modelTranslucentUnlitPixel = model_translucent_unlit_frag::getShader(); + auto modelShadowPixel = model_shadow_frag::getShader(); + auto modelLightmapPixel = model_lightmap_frag::getShader(); + auto modelLightmapNormalMapPixel = model_lightmap_normal_map_frag::getShader(); + auto modelLightmapSpecularMapPixel = model_lightmap_specular_map_frag::getShader(); + auto modelLightmapNormalSpecularMapPixel = model_lightmap_normal_specular_map_frag::getShader(); + auto modelLightmapFadePixel = model_lightmap_fade_frag::getShader(); + auto modelLightmapNormalMapFadePixel = model_lightmap_normal_map_fade_frag::getShader(); + auto modelLightmapSpecularMapFadePixel = model_lightmap_specular_map_fade_frag::getShader(); + auto modelLightmapNormalSpecularMapFadePixel = model_lightmap_normal_specular_map_fade_frag::getShader(); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_fade_frag)); - auto modelUnlitFadePixel = gpu::Shader::createPixel(std::string(model_unlit_fade_frag)); - auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag)); - auto modelSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_specular_map_fade_frag)); - auto modelNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_fade_frag)); - auto modelShadowFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); - auto modelTranslucentFadePixel = gpu::Shader::createPixel(std::string(model_translucent_fade_frag)); - auto modelTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_fade_frag)); - auto simpleFadePixel = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); - auto simpleUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); - auto simpleTranslucentFadePixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_fade_frag)); - auto simpleTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_unlit_fade_frag)); + auto modelFadePixel = model_fade_frag::getShader(); + auto modelUnlitFadePixel = model_unlit_fade_frag::getShader(); + auto modelNormalMapFadePixel = model_normal_map_fade_frag::getShader(); + auto modelSpecularMapFadePixel = model_specular_map_fade_frag::getShader(); + auto modelNormalSpecularMapFadePixel = model_normal_specular_map_fade_frag::getShader(); + auto modelShadowFadePixel = model_shadow_fade_frag::getShader(); + auto modelTranslucentFadePixel = model_translucent_fade_frag::getShader(); + auto modelTranslucentUnlitFadePixel = model_translucent_unlit_fade_frag::getShader(); + auto simpleFadePixel = simple_textured_fade_frag::getShader(); + auto simpleUnlitFadePixel = simple_textured_unlit_fade_frag::getShader(); + auto simpleTranslucentFadePixel = simple_transparent_textured_fade_frag::getShader(); + auto simpleTranslucentUnlitFadePixel = simple_transparent_textured_unlit_fade_frag::getShader(); using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4, _5); @@ -438,17 +438,17 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip void initForwardPipelines(render::ShapePlumber& plumber) { // Vertex shaders - auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); - auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); - auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); - auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); + auto modelVertex = model_vert::getShader(); + auto modelNormalMapVertex = model_normal_map_vert::getShader(); + auto skinModelVertex = skin_model_vert::getShader(); + auto skinModelNormalMapVertex = skin_model_normal_map_vert::getShader(); // Pixel shaders - auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag)); - auto modelUnlitPixel = gpu::Shader::createPixel(std::string(forward_model_unlit_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag)); + auto modelPixel = forward_model_frag::getShader(); + auto modelUnlitPixel = forward_model_unlit_frag::getShader(); + auto modelNormalMapPixel = forward_model_normal_map_frag::getShader(); + auto modelSpecularMapPixel = forward_model_specular_map_frag::getShader(); + auto modelNormalSpecularMapPixel = forward_model_normal_specular_map_frag::getShader(); using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, nullptr, nullptr); @@ -574,29 +574,29 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderAr } void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) { - auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelVertex = model_shadow_vert::getShader(); + auto modelPixel = model_shadow_frag::getShader(); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned().withoutFade(), modelProgram, state); - auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag)); + auto skinVertex = skin_model_shadow_vert::getShader(); + auto skinPixel = skin_model_shadow_frag::getShader(); gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned().withoutFade(), skinProgram, state); - auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); + auto modelFadeVertex = model_shadow_fade_vert::getShader(); + auto modelFadePixel = model_shadow_fade_frag::getShader(); gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned().withFade(), modelFadeProgram, state); - auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); - auto skinFadePixel = gpu::Shader::createPixel(std::string(skin_model_shadow_fade_frag)); + auto skinFadeVertex = skin_model_shadow_fade_vert::getShader(); + auto skinFadePixel = skin_model_shadow_fade_frag::getShader(); gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, skinFadePixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned().withFade(), diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 80c97cf29f..48beda78bc 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -60,7 +60,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() { gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() { if (!_paintStencilPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(stencil_drawMask_frag); + auto ps = stencil_drawMask_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram((*program)); diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 1786898e57..d6ec73da85 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -308,7 +308,7 @@ void diffuseProfileGPU(gpu::TexturePointer& profileMap, RenderArgs* args) { gpu::PipelinePointer makePipeline; { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_makeProfile_frag)); + auto ps = subsurfaceScattering_makeProfile_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -344,7 +344,7 @@ void diffuseScatterGPU(const gpu::TexturePointer& profileMap, gpu::TexturePointe gpu::PipelinePointer makePipeline; { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_makeLUT_frag)); + auto ps = subsurfaceScattering_makeLUT_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -382,7 +382,7 @@ void computeSpecularBeckmannGPU(gpu::TexturePointer& beckmannMap, RenderArgs* ar gpu::PipelinePointer makePipeline; { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_makeSpecularBeckmann_frag)); + auto ps = subsurfaceScattering_makeSpecularBeckmann_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -457,7 +457,7 @@ void DebugSubsurfaceScattering::configure(const Config& config) { gpu::PipelinePointer DebugSubsurfaceScattering::getScatteringPipeline() { if (!_scatteringPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_drawScattering_frag)); + auto ps = subsurfaceScattering_drawScattering_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index af6ff09082..afed9ee8fd 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -212,7 +212,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { if (!_linearDepthPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeLinearDepth_frag); + auto ps = surfaceGeometry_makeLinearDepth_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -239,7 +239,7 @@ const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { const gpu::PipelinePointer& LinearDepthPass::getDownsamplePipeline() { if (!_downsamplePipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_downsampleDepthNormal_frag); + auto ps = surfaceGeometry_downsampleDepthNormal_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -540,7 +540,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext, const gpu::PipelinePointer& SurfaceGeometryPass::getCurvaturePipeline() { if (!_curvaturePipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeCurvature_frag); + auto ps = surfaceGeometry_makeCurvature_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 6cb9541dae..e1abefd681 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -28,7 +28,7 @@ ToneMappingEffect::ToneMappingEffect() { } void ToneMappingEffect::init() { - auto blitPS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(toneMapping_frag)); + auto blitPS = toneMapping_frag::getShader(); auto blitVS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(blitVS, blitPS)); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index c0d01c2eaf..19a0419a5f 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -78,7 +78,7 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { if (!_keyLightPipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(zone_drawKeyLight_frag)); + auto ps = zone_drawKeyLight_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -99,7 +99,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() { if (!_ambientPipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(zone_drawAmbient_frag)); + auto ps = zone_drawAmbient_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -120,7 +120,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() { const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() { if (!_backgroundPipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(zone_drawSkybox_frag)); + auto ps = zone_drawSkybox_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 8449c58c7c..bcd14a4fbc 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -223,9 +223,9 @@ void Font::setupGPU() { // Setup render pipeline { - auto vertexShader = gpu::Shader::createVertex(std::string(sdf_text3D_vert)); - auto pixelShader = gpu::Shader::createPixel(std::string(sdf_text3D_frag)); - auto pixelShaderTransparent = gpu::Shader::createPixel(std::string(sdf_text3D_transparent_frag)); + auto vertexShader = sdf_text3D_vert::getShader(); + auto pixelShader = sdf_text3D_frag::getShader(); + auto pixelShaderTransparent = sdf_text3D_transparent_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(vertexShader, pixelShaderTransparent); diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 2be6f8fad2..0625179a6d 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -210,7 +210,7 @@ BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsamp gpu::PipelinePointer BlurGaussian::getBlurVPipeline() { if (!_blurVPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianV_frag)); + auto ps = blurGaussianV_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -232,7 +232,7 @@ gpu::PipelinePointer BlurGaussian::getBlurVPipeline() { gpu::PipelinePointer BlurGaussian::getBlurHPipeline() { if (!_blurHPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianH_frag)); + auto ps = blurGaussianH_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -324,7 +324,7 @@ BlurGaussianDepthAware::BlurGaussianDepthAware(bool generateOutputFramebuffer, c gpu::PipelinePointer BlurGaussianDepthAware::getBlurVPipeline() { if (!_blurVPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianDepthAwareV_frag)); + auto ps = blurGaussianDepthAwareV_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -347,7 +347,7 @@ gpu::PipelinePointer BlurGaussianDepthAware::getBlurVPipeline() { gpu::PipelinePointer BlurGaussianDepthAware::getBlurHPipeline() { if (!_blurHPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianDepthAwareH_frag)); + auto ps = blurGaussianDepthAwareH_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 36663a454a..f1e85dbb71 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -34,8 +34,8 @@ using namespace render; const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { if (!_drawCellBoundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawCellBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawCellBounds_frag)); + auto vs = drawCellBounds_vert::getShader(); + auto ps = drawCellBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -59,7 +59,7 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { const gpu::PipelinePointer DrawSceneOctree::getDrawLODReticlePipeline() { if (!_drawLODReticlePipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(drawLODReticle_frag)); + auto ps = drawLODReticle_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -162,8 +162,8 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS const gpu::PipelinePointer DrawItemSelection::getDrawItemBoundPipeline() { if (!_drawItemBoundPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + auto vs = drawItemBounds_vert::getShader(); + auto ps = drawItemBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 148e104453..a11e9b1a88 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -35,8 +35,8 @@ void DrawStatusConfig::dirtyHelper() { const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { if (!_drawItemBoundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + auto vs = drawItemBounds_vert::getShader(); + auto ps = drawItemBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -63,8 +63,8 @@ const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() { if (!_drawItemStatusPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemStatus_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemStatus_frag)); + auto vs = drawItemStatus_vert::getShader(); + auto ps = drawItemStatus_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index a60bf91062..88d38d1c66 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -155,8 +155,8 @@ void DrawLight::run(const RenderContextPointer& renderContext, const ItemBounds& const gpu::PipelinePointer DrawBounds::getPipeline() { if (!_boundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + auto vs = drawItemBounds_vert::getShader(); + auto ps = drawItemBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index de37c505a6..83ada05fbd 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -23,65 +23,65 @@ #include -#include "simple_vert.h" -#include "simple_frag.h" -#include "simple_textured_frag.h" -#include "simple_textured_unlit_frag.h" +#include "render-utils/simple_vert.h" +#include "render-utils/simple_frag.h" +#include "render-utils/simple_textured_frag.h" +#include "render-utils/simple_textured_unlit_frag.h" -#include "deferred_light_vert.h" -#include "deferred_light_point_vert.h" -#include "deferred_light_spot_vert.h" +#include "render-utils/deferred_light_vert.h" +#include "render-utils/deferred_light_point_vert.h" +#include "render-utils/deferred_light_spot_vert.h" -#include "directional_ambient_light_frag.h" -#include "directional_skybox_light_frag.h" +#include "render-utils/directional_ambient_light_frag.h" +#include "render-utils/directional_skybox_light_frag.h" -#include "standardTransformPNTC_vert.h" -#include "standardDrawTexture_frag.h" +#include "render-utils/standardTransformPNTC_vert.h" +#include "render-utils/standardDrawTexture_frag.h" -#include "model_vert.h" -#include "model_shadow_vert.h" -#include "model_normal_map_vert.h" -#include "model_lightmap_vert.h" -#include "model_lightmap_normal_map_vert.h" -#include "skin_model_vert.h" -#include "skin_model_shadow_vert.h" -#include "skin_model_normal_map_vert.h" +#include "render-utils/model_vert.h" +#include "render-utils/model_shadow_vert.h" +#include "render-utils/model_normal_map_vert.h" +#include "render-utils/model_lightmap_vert.h" +#include "render-utils/model_lightmap_normal_map_vert.h" +#include "render-utils/skin_model_vert.h" +#include "render-utils/skin_model_shadow_vert.h" +#include "render-utils/skin_model_normal_map_vert.h" -#include "model_frag.h" -#include "model_shadow_frag.h" -#include "model_normal_map_frag.h" -#include "model_normal_specular_map_frag.h" -#include "model_specular_map_frag.h" -#include "model_lightmap_frag.h" -#include "model_lightmap_normal_map_frag.h" -#include "model_lightmap_normal_specular_map_frag.h" -#include "model_lightmap_specular_map_frag.h" -#include "model_translucent_frag.h" +#include "render-utils/model_frag.h" +#include "render-utils/model_shadow_frag.h" +#include "render-utils/model_normal_map_frag.h" +#include "render-utils/model_normal_specular_map_frag.h" +#include "render-utils/model_specular_map_frag.h" +#include "render-utils/model_lightmap_frag.h" +#include "render-utils/model_lightmap_normal_map_frag.h" +#include "render-utils/model_lightmap_normal_specular_map_frag.h" +#include "render-utils/model_lightmap_specular_map_frag.h" +#include "render-utils/model_translucent_frag.h" -#include "textured_particle_frag.h" -#include "textured_particle_vert.h" +#include "entities-renderer/textured_particle_frag.h" +#include "entities-renderer/textured_particle_vert.h" -#include "overlay3D_vert.h" -#include "overlay3D_frag.h" +#include "render-utils/overlay3D_vert.h" +#include "render-utils/overlay3D_frag.h" -#include "skybox_vert.h" -#include "skybox_frag.h" +#include "model/skybox_vert.h" +#include "model/skybox_frag.h" -#include "DrawTransformUnitQuad_vert.h" -#include "DrawTexcoordRectTransformUnitQuad_vert.h" -#include "DrawViewportQuadTransformTexcoord_vert.h" -#include "DrawTexture_frag.h" -#include "DrawTextureOpaque_frag.h" -#include "DrawColoredTexture_frag.h" +#include "gpu/DrawTransformUnitQuad_vert.h" +#include "gpu/DrawTexcoordRectTransformUnitQuad_vert.h" +#include "gpu/DrawViewportQuadTransformTexcoord_vert.h" +#include "gpu/DrawTexture_frag.h" +#include "gpu/DrawTextureOpaque_frag.h" +#include "gpu/DrawColoredTexture_frag.h" -#include "sdf_text3D_vert.h" -#include "sdf_text3D_frag.h" +#include "render-utils/sdf_text3D_vert.h" +#include "render-utils/sdf_text3D_frag.h" -#include "paintStroke_vert.h" -#include "paintStroke_frag.h" +#include "entities-renderer/paintStroke_vert.h" +#include "entities-renderer/paintStroke_frag.h" -#include "polyvox_vert.h" -#include "polyvox_frag.h" +#include "entities-renderer/polyvox_vert.h" +#include "entities-renderer/polyvox_frag.h" // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow { @@ -159,54 +159,54 @@ void QTestWindow::draw() { static std::once_flag once; std::call_once(once, [&]{ - testShaderBuild(sdf_text3D_vert, sdf_text3D_frag.h"; + testShaderBuild(sdf_text3D_vert::getSource(), sdf_text3D_frag::getSource()); - testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag.h"; - testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag.h"; - testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag.h"; - testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag.h"; - testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag.h"; + testShaderBuild(DrawTransformUnitQuad_vert::getSource(), DrawTexture_frag::getSource()); + testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert::getSource(), DrawTexture_frag::getSource()); + testShaderBuild(DrawViewportQuadTransformTexcoord_vert::getSource(), DrawTexture_frag::getSource()); + testShaderBuild(DrawTransformUnitQuad_vert::getSource(), DrawTextureOpaque_frag::getSource()); + testShaderBuild(DrawTransformUnitQuad_vert::getSource(), DrawColoredTexture_frag::getSource()); - testShaderBuild(skybox_vert, skybox_frag.h"; - testShaderBuild(simple_vert, simple_frag.h"; - testShaderBuild(simple_vert, simple_textured_frag.h"; - testShaderBuild(simple_vert, simple_textured_unlit_frag.h"; - testShaderBuild(deferred_light_vert, directional_ambient_light_frag.h"; - testShaderBuild(deferred_light_vert, directional_skybox_light_frag.h"; - testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag.h"; - testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag.h"; + testShaderBuild(skybox_vert::getSource(), skybox_frag::getSource()); + testShaderBuild(simple_vert::getSource(), simple_frag::getSource()); + testShaderBuild(simple_vert::getSource(), simple_textured_frag::getSource()); + testShaderBuild(simple_vert::getSource(), simple_textured_unlit_frag::getSource()); + testShaderBuild(deferred_light_vert::getSource(), directional_ambient_light_frag::getSource()); + testShaderBuild(deferred_light_vert::getSource(), directional_skybox_light_frag::getSource()); + testShaderBuild(standardTransformPNTC_vert::getSource(), standardDrawTexture_frag::getSource()); + testShaderBuild(standardTransformPNTC_vert::getSource(), DrawTextureOpaque_frag::getSource()); - testShaderBuild(model_vert, model_frag.h"; - testShaderBuild(model_normal_map_vert, model_normal_map_frag.h"; - testShaderBuild(model_vert, model_specular_map_frag.h"; - testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag.h"; - testShaderBuild(model_vert, model_translucent_frag.h"; - testShaderBuild(model_normal_map_vert, model_translucent_frag.h"; - testShaderBuild(model_lightmap_vert, model_lightmap_frag.h"; - testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag.h"; - testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag.h"; - testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag.h"; + testShaderBuild(model_vert::getSource(), model_frag::getSource()); + testShaderBuild(model_normal_map_vert::getSource(), model_normal_map_frag::getSource()); + testShaderBuild(model_vert::getSource(), model_specular_map_frag::getSource()); + testShaderBuild(model_normal_map_vert::getSource(), model_normal_specular_map_frag::getSource()); + testShaderBuild(model_vert::getSource(), model_translucent_frag::getSource()); + testShaderBuild(model_normal_map_vert::getSource(), model_translucent_frag::getSource()); + testShaderBuild(model_lightmap_vert::getSource(), model_lightmap_frag::getSource()); + testShaderBuild(model_lightmap_normal_map_vert::getSource(), model_lightmap_normal_map_frag::getSource()); + testShaderBuild(model_lightmap_vert::getSource(), model_lightmap_specular_map_frag::getSource()); + testShaderBuild(model_lightmap_normal_map_vert::getSource(), model_lightmap_normal_specular_map_frag::getSource()); - testShaderBuild(skin_model_vert, model_frag.h"; - testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag.h"; - testShaderBuild(skin_model_vert, model_specular_map_frag.h"; - testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag.h"; - testShaderBuild(skin_model_vert, model_translucent_frag.h"; - testShaderBuild(skin_model_normal_map_vert, model_translucent_frag.h"; + testShaderBuild(skin_model_vert::getSource(), model_frag::getSource()); + testShaderBuild(skin_model_normal_map_vert::getSource(), model_normal_map_frag::getSource()); + testShaderBuild(skin_model_vert::getSource(), model_specular_map_frag::getSource()); + testShaderBuild(skin_model_normal_map_vert::getSource(), model_normal_specular_map_frag::getSource()); + testShaderBuild(skin_model_vert::getSource(), model_translucent_frag::getSource()); + testShaderBuild(skin_model_normal_map_vert::getSource(), model_translucent_frag::getSource()); - testShaderBuild(model_shadow_vert, model_shadow_frag.h"; - testShaderBuild(textured_particle_vert, textured_particle_frag.h"; + testShaderBuild(model_shadow_vert::getSource(), model_shadow_frag::getSource()); + testShaderBuild(textured_particle_vert::getSource(), textured_particle_frag::getSource()); /* FIXME: Bring back the ssao shader tests - testShaderBuild(gaussian_blur_vertical_vert, gaussian_blur_frag.h"; - testShaderBuild(gaussian_blur_horizontal_vert, gaussian_blur_frag.h"; - testShaderBuild(ambient_occlusion_vert, ambient_occlusion_frag.h"; - testShaderBuild(ambient_occlusion_vert, occlusion_blend_frag.h"; + testShaderBuild(gaussian_blur_vert::getSource()ical_vert::getSource(), gaussian_blur_frag::getSource()); + testShaderBuild(gaussian_blur_horizontal_vert::getSource(), gaussian_blur_frag::getSource()); + testShaderBuild(ambient_occlusion_vert::getSource(), ambient_occlusion_frag::getSource()); + testShaderBuild(ambient_occlusion_vert::getSource(), occlusion_blend_frag::getSource()); */ - testShaderBuild(overlay3D_vert, overlay3D_frag.h"; + testShaderBuild(overlay3D_vert::getSource(), overlay3D_frag::getSource()); - testShaderBuild(paintStroke_vert,paintStroke_frag.h"; - testShaderBuild(polyvox_vert, polyvox_frag.h"; + testShaderBuild(paintStroke_vert::getSource(),paintStroke_frag::getSource()); + testShaderBuild(polyvox_vert::getSource(), polyvox_frag::getSource()); }); _context.swapBuffers(this); diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index 2092bc0ea2..a7d12d677d 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -184,7 +184,7 @@ int main (int argc, char** argv) { srcStream.open(srcFilename, std::fstream::in); if (!srcStream.is_open()) { cerr << "Failed to open source file <" << srcFilename << ">" << endl; - return 0; + return 1; } auto scribe = std::make_shared(srcFilename, config); @@ -194,7 +194,7 @@ int main (int argc, char** argv) { int numErrors = scribe->scribe(destStringStream, srcStream, vars); if (numErrors) { cerr << "Scribe " << srcFilename << "> failed: " << numErrors << " errors." << endl; - return 0; + return 1; }; @@ -279,7 +279,7 @@ int main (int argc, char** argv) { headerFile << headerStringStream.str(); } else { cerr << "Scribe output file <" << headerFileName << "> failed to open." << endl; - return 0; + return 1; } } else { cerr << sourceStringStream.str(); @@ -310,7 +310,7 @@ int main (int argc, char** argv) { sourceFile.open(sourceFileName, std::fstream::out); if (!sourceFile.is_open()) { cerr << "Scribe output file <" << sourceFileName << "> failed to open." << endl; - return 0; + return 1; } sourceFile << sourceStringStream.str(); } else { @@ -323,7 +323,7 @@ int main (int argc, char** argv) { destFileStream.open(destFilename, std::fstream::out); if (!destFileStream.is_open()) { cerr << "Scribe output file <" << destFilename << "> failed to open." << endl; - return 0; + return 1; } destFileStream << destStringStream.str(); From 7b420d48e2cf1972d9398ee0032901dc9257cd39 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Jan 2018 10:59:48 +0100 Subject: [PATCH 039/381] Generated shader files are now grouped in a sub-filter group in the project --- cmake/macros/AutoScribeShader.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index c92f7a3ffe..32b0bc3a2e 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -132,7 +132,7 @@ macro(AUTOSCRIBE_SHADER_LIB) if (WIN32) source_group("Shaders" FILES ${SHADER_INCLUDE_FILES}) source_group("Shaders" FILES ${SHADER_SOURCE_FILES}) - source_group("Shaders" FILES ${AUTOSCRIBE_SHADER_SRC}) + source_group("Shaders\\generated" FILES ${AUTOSCRIBE_SHADER_SRC}) endif() list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_INCLUDE_FILES}) From 54690219e3a22846af3a6ff17353a6eda76a3df4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Jan 2018 11:12:37 +0100 Subject: [PATCH 040/381] Changed the way shader type is sent to Scribe --- cmake/macros/AutoScribeShader.cmake | 8 ++++---- tools/scribe/src/main.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index 32b0bc3a2e..313de1437d 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -54,13 +54,13 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) # Target dependant Custom rule on the SHADER_FILE if (APPLE) set(GLPROFILE MAC_GL) - set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe) add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (ANDROID) set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) # for an android build, we can't use the scribe that cmake would normally produce as a target, # since it's unrunnable by the cross-compiling build machine @@ -80,13 +80,13 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (UNIX) set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe) add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) else () set(GLPROFILE PC_GL) - set(SCRIBE_ARGS -c++ -${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) endif() diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index a7d12d677d..83c2fe287a 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -41,6 +41,7 @@ int main (int argc, char** argv) { GRAB_VAR_VALUE, GRAB_INCLUDE_PATH, GRAB_TARGET_NAME, + GRAB_SHADER_TYPE, EXIT, } mode = READY; @@ -78,15 +79,8 @@ int main (int argc, char** argv) { } else if (inputs.back() == "-c++") { makeCPlusPlus = true; mode = READY; - } else if (inputs.back() == "-vert") { - type = VERTEX; - mode = READY; - } else if (inputs.back() == "-frag") { - type = FRAGMENT; - mode = READY; - } else if (inputs.back() == "-geom") { - type = GEOMETRY; - mode = READY; + } else if (inputs.back() == "-T") { + mode = GRAB_SHADER_TYPE; } else { // just grabbed the source filename, stop parameter parsing srcFilename = inputs.back(); @@ -127,6 +121,21 @@ int main (int argc, char** argv) { } break; + case GRAB_SHADER_TYPE: + { + if (inputs.back() == "frag") { + type = FRAGMENT; + } else if (inputs.back() == "geom") { + type = GEOMETRY; + } else if (inputs.back() == "vert") { + type = VERTEX; + } else { + cerr << "Unrecognized shader type. Supported is vert, frag or geom" << endl; + } + mode = READY; + } + break; + case EXIT: { // THis shouldn't happen } @@ -145,6 +154,8 @@ int main (int argc, char** argv) { cerr << " -listVars : Will list the vars name and value in the standard output." << endl; cerr << " -showParseTree : Draw the tree obtained while parsing the source" << endl; cerr << " -c++ : Generate a c++ source file containing the output file stream stored as a char[] variable" << endl; + cerr << " -T vert/frag/geom : define the type of the shader. Defaults to VERTEX if not specified." << endl; + cerr << " This is necessary if the -c++ option is used." << endl; return 0; } From 157a229a4fd57c571a4c05b656bb6ab9efa09342 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Jan 2018 15:09:32 +0100 Subject: [PATCH 041/381] Tried to add GLSL validation in scribe. Issues with determining the correct version... --- tools/scribe/src/main.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index 83c2fe287a..9bdcccbfa7 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -56,6 +56,7 @@ int main (int argc, char** argv) { static const char* shaderCreateString[] = { "Vertex", "Pixel", "Geometry" }; + std::string shaderStage{ "vert" }; for (int ii = 1; (mode != EXIT) && (ii < argc); ii++) { inputs.push_back(argv[ii]); @@ -124,10 +125,13 @@ int main (int argc, char** argv) { case GRAB_SHADER_TYPE: { if (inputs.back() == "frag") { + shaderStage = inputs.back(); type = FRAGMENT; } else if (inputs.back() == "geom") { + shaderStage = inputs.back(); type = GEOMETRY; } else if (inputs.back() == "vert") { + shaderStage = inputs.back(); type = VERTEX; } else { cerr << "Unrecognized shader type. Supported is vert, frag or geom" << endl; @@ -218,6 +222,36 @@ int main (int argc, char** argv) { scribe->displayTree(cerr, level); } + // This would be nice to implement but not sure how to handle GLSL version +#if 0 + // Check if we need to validate the code + auto validatorPath = getenv("SCRIBE_VALIDATOR"); + if (validatorPath) { + // Create temporary file with shader code + char tempFileNameStub[L_tmpnam]; + tmpnam(tempFileNameStub); + std::string tempFileName{ tempFileNameStub }; + tempFileName += "."; + tempFileName += shaderStage; + std::ofstream tempStream(tempFileName); + if (tempStream.is_open()) { + tempStream << destStringStream.str(); + tempStream.close(); + std::string validationCommand{ validatorPath }; + validationCommand += " "; + validationCommand += tempFileName; + cout << validationCommand << endl; + auto returnCode = system(validationCommand.c_str()); + if (returnCode != 0) { + cerr << "Scribe shader " << targetName << " validation error." << endl; + } + //remove(tempFileName.c_str()); + } else { + cerr << "Scribe is unable to write shader " << targetName << " to temporary file for validation." << endl; + } + } +#endif + if (makeCPlusPlus) { // Because there is a maximum size for literal strings declared in source we need to partition the // full source string stream into pages that seems to be around that value... From 604817bb59ee3fac1211bd2e743e1107def4556e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Jan 2018 19:21:58 +0100 Subject: [PATCH 042/381] Forgot to fix output custom command on all other platforms than Windows --- cmake/macros/AutoScribeShader.cmake | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index 313de1437d..f15930a7c2 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -56,8 +56,7 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) set(GLPROFILE MAC_GL) set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe) - add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (ANDROID) set(GLPROFILE LINUX_GL) set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) @@ -76,14 +75,12 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) ") endif () - add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS}) - add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (UNIX) set(GLPROFILE LINUX_GL) set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe) - add_custom_command(OUTPUT ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) else () set(GLPROFILE PC_GL) set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) From 514eea5477e5aa73a01fb2e1a610f52787a63dff Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 17 Jan 2018 17:02:10 -0800 Subject: [PATCH 043/381] CR changes --- libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/OBJReader.cpp | 32 +++++++++++--------------------- libraries/fbx/src/OBJReader.h | 3 +-- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 0e85891398..56cda9d137 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -179,7 +179,7 @@ public: float emissiveIntensity{ 1.0f }; float ambientFactor{ 1.0f }; - float bumpMultiplier{ 1.0f }; // TODO: to be implemented + float bumpMultiplier { 1.0f }; // TODO: to be implemented QString materialID; QString name; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 008b51b0be..8624efdd56 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -389,10 +389,8 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file QString parser = textureLine; while (parser.length() > 0) { if (parser.startsWith("-blend")) { // -blendu/-blendv - parser.remove(0, 11); // remove through "-blendu on " or "-blendu off" - if (parser[0] == ' ') { // extra character for space after off - parser.remove(0, 1); - } + int removeLength = parser[10] == 'f' ? 12 : 11; + parser.remove(0, removeLength); // remove through "-blendu on " or "-blendu off" #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -blendu/-blendv"; #endif @@ -407,18 +405,14 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -boost"; #endif } else if (parser.startsWith("-cc")) { - parser.remove(0, 7); // remove through "-cc on " or "-cc off" - if (parser[0] == ' ') { // extra character for space after off - parser.remove(0, 1); - } + int removeLength = parser[6] == 'f' ? 8 : 7; + parser.remove(0, removeLength); // remove through "-cc on " or "-cc off" #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -cc"; #endif } else if (parser.startsWith("-clamp")) { - parser.remove(0, 10); // remove through "-clamp on " or "-clamp off" - if (parser[0] == ' ') { // extra character for space after off - parser.remove(0, 1); - } + int removeLength = parser[9] == 'f' ? 11 : 10; + parser.remove(0, removeLength); // remove through "-clamp on " or "-clamp off" #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option -clamp"; #endif @@ -941,18 +935,17 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool applyRoughness = false; bool applyNonMetallic = false; bool fresnelOn = false; - bool fresnelOff = false; // Illumination model reference http://paulbourke.net/dataformats/mtl/ switch (objMaterial.illuminationModel) { case 0: // Color on and Ambient off - // We don't support ambient - do nothing? + // We don't support ambient = do nothing? break; case 1: // Color on and Ambient on - // We don't support ambient - do nothing? + // We don't support ambient = do nothing? break; case 2: // Highlight on - // Change specular intensity? + // Change specular intensity = do nothing for now? break; case 3: // Reflection on and Ray trace on applyShininess = true; @@ -969,7 +962,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, applyTransparency = true; applyNonMetallic = true; applyShininess = true; - fresnelOff = true; break; case 7: // Transparency: Refraction on and Reflection: Fresnel on and Ray trace on applyTransparency = true; @@ -990,8 +982,8 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, break; } - if (applyTransparency && fbxMaterial.opacity <= ILLUMINATION_MODEL_MIN_OPACITY) { - fbxMaterial.opacity = ILLUMINATION_MODEL_MIN_OPACITY; + if (applyTransparency) { + fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); } if (applyShininess) { modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS); @@ -1003,8 +995,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, } if (fresnelOn) { modelMaterial->setFresnel(glm::vec3(1.0f)); - } else if (fresnelOff) { - modelMaterial->setFresnel(glm::vec3(0.0f)); } modelMaterial->setOpacity(fbxMaterial.opacity); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 44382e3603..9083a69340 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -50,8 +50,7 @@ private: class OBJMaterialTextureOptions { public: - float bumpMultiplier; - OBJMaterialTextureOptions() : bumpMultiplier(1.0f) {} + float bumpMultiplier { 1.0f }; } ; // Materials and references to material names can come in any order, and different mesh parts can refer to the same material. From 26bf78fb5dbb58baf3f4251ca852518c29ef4660 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 17 Jan 2018 18:30:31 -0800 Subject: [PATCH 044/381] first pass new entity selection edit tools wip --- .../system/libraries/entitySelectionTool.js | 4248 ++++------------- 1 file changed, 964 insertions(+), 3284 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index b8ba146757..220a7b7c70 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1,9 +1,10 @@ // -// entitySelectionToolClass.js +// entitySelectionTool.js // examples // // Created by Brad hefta-Gaub on 10/1/14. // Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017 +// Modified by David Back on 1/9/2018 // Copyright 2014 High Fidelity, Inc. // // This script implements a class useful for building tools for editing entities. @@ -21,12 +22,6 @@ SPACE_WORLD = "world"; Script.include("./controllers.js"); -function objectTranslationPlanePoint(position, dimensions) { - var newPosition = { x: position.x, y: position.y, z: position.z }; - newPosition.y -= dimensions.y / 2.0; - return newPosition; -} - SelectionManager = (function() { var that = {}; @@ -53,10 +48,6 @@ SelectionManager = (function() { print("ERROR: entitySelectionTool.handleEntitySelectionToolUpdates - got malformed message: " + message); } - // if (message === 'callUpdate') { - // that._update(); - // } - if (messageParsed.method === "selectEntity") { if (wantDebug) { print("setting selection to " + messageParsed.entityID); @@ -235,20 +226,49 @@ function getRelativeCenterPosition(dimensions, registrationPoint) { SelectionDisplay = (function() { var that = {}; - var MINIMUM_DIMENSION = 0.001; + var COLOR_GREEN = { red:0, green:255, blue:0 }; + var COLOR_BLUE = { red:0, green:0, blue:255 }; + var COLOR_RED = { red:255, green:0, blue:0 }; - var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; + var GRABBER_TRANSLATE_ARROW_CONE_OFFSET = 0.3625; + var GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET = 0.3; + var GRABBER_STRETCH_SPHERE_OFFSET = 0.2; + var GRABBER_SCALE_CUBE_OFFSET = 0.2; + + var GRABBER_SCALE_CUBE_IDLE_COLOR = { red:120, green:120, blue:120 }; + var GRABBER_SCALE_CUBE_SELECTED_COLOR = { red:0, green:0, blue:0 }; + var GRABBER_SCALE_EDGE_COLOR = { red:120, green:120, blue:120 }; + + var SCALE_MINIMUM_DIMENSION = 0.02; // These are multipliers for sizing the rotation degrees display while rotating an entity - var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.2; + var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.0; var ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.6; var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.18; var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.14; - var ROTATE_ARROW_WEST_NORTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-north.svg"; - var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.svg"; + var TRANSLATE_DIRECTION = { + X : 0, + Y : 1, + Z : 2 + } - var showExtendedStretchHandles = false; + var ROTATE_DIRECTION = { + PITCH : 0, + YAW : 1, + ROLL : 2 + } + + var SCALE_DIRECTION = { + LBN : 0, + RBN : 1, + LBF : 2, + RBF : 3, + LTN : 4, + RTN : 5, + LTF : 6, + RTF : 7 + } var spaceMode = SPACE_LOCAL; var overlayNames = []; @@ -259,185 +279,95 @@ SelectionDisplay = (function() { getControllerWorldLocation(Controller.Standard.RightHand, true) ]; - var handleHoverColor = { - red: 224, - green: 67, - blue: 36 - }; - var handleHoverAlpha = 1.0; - - var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool - var innerRadius; - var outerRadius; - var yawHandleRotation; - var pitchHandleRotation; - var rollHandleRotation; - var yawCenter; - var pitchCenter; - var rollCenter; var rotZero; var rotationNormal; + var worldRotationX; + var worldRotationY; + var worldRotationZ; - var handleColor = { - red: 255, - green: 255, - blue: 255 - }; - var handleAlpha = 0.7; + var activeTool = null; + var grabberTools = {}; - var highlightedHandleColor = { - red: 183, - green: 64, - blue: 44 - }; - var highlightedHandleAlpha = 0.9; - - var previousHandle = false; - var previousHandleColor; - var previousHandleAlpha; - - var grabberSizeCorner = 0.025; // These get resized by updateHandleSizes(). - var grabberSizeEdge = 0.015; - var grabberSizeFace = 0.025; - var grabberAlpha = 1; - var grabberColorCorner = { - red: 120, - green: 120, - blue: 120 - }; - var grabberColorEdge = { - red: 0, - green: 0, - blue: 0 - }; - var grabberColorFace = { - red: 120, - green: 120, - blue: 120 - }; - var grabberColorCloner = { - red: 0, - green: 155, - blue: 0 - }; - var grabberLineWidth = 0.5; - var grabberSolid = true; - var grabberMoveUpPosition = Vec3.ZERO; - - var lightOverlayColor = { - red: 255, - green: 153, - blue: 0 - }; - - var grabberPropertiesCorner = { - position: Vec3.ZERO, - size: grabberSizeCorner, - color: grabberColorCorner, - alpha: 1, - solid: grabberSolid, + var grabberPropertiesTranslateArrowCones = { + shape: "Cone", + dimensions: { x:0.05, y:0.05, z:0.05 }, + solid: true, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 + ignoreRayIntersection: false, + drawInFront: true }; - - var grabberPropertiesEdge = { - position: Vec3.ZERO, - size: grabberSizeEdge, - color: grabberColorEdge, - alpha: 1, - solid: grabberSolid, + var grabberPropertiesTranslateArrowCylinders = { + shape: "Cylinder", + dimensions: { x:0.01, y:0.075, z:0.01 }, + solid: true, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 + ignoreRayIntersection: false, + drawInFront: true }; + var grabberTranslateXCone = Overlays.addOverlay("shape", grabberPropertiesTranslateArrowCones); + var grabberTranslateXCylinder = Overlays.addOverlay("shape", grabberPropertiesTranslateArrowCylinders); + Overlays.editOverlay(grabberTranslateXCone, { color : COLOR_RED }); + Overlays.editOverlay(grabberTranslateXCylinder, { color : COLOR_RED }); + var grabberTranslateYCone = Overlays.addOverlay("shape", grabberPropertiesTranslateArrowCones); + var grabberTranslateYCylinder = Overlays.addOverlay("shape", grabberPropertiesTranslateArrowCylinders); + Overlays.editOverlay(grabberTranslateYCone, { color : COLOR_GREEN }); + Overlays.editOverlay(grabberTranslateYCylinder, { color : COLOR_GREEN }); + var grabberTranslateZCone = Overlays.addOverlay("shape", grabberPropertiesTranslateArrowCones); + var grabberTranslateZCylinder = Overlays.addOverlay("shape", grabberPropertiesTranslateArrowCylinders); + Overlays.editOverlay(grabberTranslateZCone, { color : COLOR_BLUE }); + Overlays.editOverlay(grabberTranslateZCylinder, { color : COLOR_BLUE }); - var grabberPropertiesFace = { - position: Vec3.ZERO, - size: grabberSizeFace, - color: grabberColorFace, + var grabberPropertiesRotateRings = { + size: 0.5, alpha: 1, - solid: grabberSolid, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + majorTickMarksAngle: 5, + majorTickMarksLength: 0.1, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 + ignoreRayIntersection: false, + drawInFront: true }; + var grabberRotatePitchRing = Overlays.addOverlay("circle3d", grabberPropertiesRotateRings); + Overlays.editOverlay(grabberRotatePitchRing, { + color : COLOR_RED, + majorTickMarksColor: COLOR_RED, + }); + var grabberRotateYawRing = Overlays.addOverlay("circle3d", grabberPropertiesRotateRings); + Overlays.editOverlay(grabberRotateYawRing, { + color : COLOR_GREEN, + majorTickMarksColor: COLOR_GREEN, + }); + var grabberRotateRollRing = Overlays.addOverlay("circle3d", grabberPropertiesRotateRings); + Overlays.editOverlay(grabberRotateRollRing, { + color : COLOR_BLUE, + majorTickMarksColor: COLOR_BLUE, + }); - var grabberPropertiesCloner = { - position: Vec3.ZERO, - size: grabberSizeCorner, - color: grabberColorCloner, + var grabberRotateCurrentRing = Overlays.addOverlay("circle3d", { + size: 0.5, alpha: 1, - solid: grabberSolid, + color: { red: 224, green: 67, blue: 36 }, + solid: true, + innerRadius: 0.9, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 - }; - - var spotLightLineProperties = { - color: lightOverlayColor - }; - - var highlightBox = Overlays.addOverlay("cube", { - position: Vec3.ZERO, - size: 1, - color: { - red: 90, - green: 90, - blue: 90 - }, - alpha: 1, - solid: false, - visible: false, - dashed: true, - ignoreRayIntersection: true, // this never ray intersects + ignoreRayIntersection: true, drawInFront: true }); - var selectionBox = Overlays.addOverlay("cube", { - position: Vec3.ZERO, - size: 1, - color: { - red: 255, - green: 0, - blue: 0 - }, - alpha: 1, - solid: false, - visible: false, - dashed: false - }); - - var selectionBoxes = []; - var rotationDegreesDisplay = Overlays.addOverlay("text3d", { - position: Vec3.ZERO, text: "", - color: { - red: 0, - green: 0, - blue: 0 - }, - backgroundColor: { - red: 255, - green: 255, - blue: 255 - }, + color: { red: 0, green: 0, blue: 0 }, + backgroundColor: { red: 255, green: 255, blue: 255 }, alpha: 0.7, backgroundAlpha: 0.7, visible: false, isFacingAvatar: true, drawInFront: true, ignoreRayIntersection: true, - dimensions: { - x: 0, - y: 0 - }, + dimensions: { x: 0, y: 0 }, lineHeight: 0.0, topMargin: 0, rightMargin: 0, @@ -445,461 +375,152 @@ SelectionDisplay = (function() { leftMargin: 0 }); - var grabberMoveUp = Overlays.addOverlay("image3d", { - url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, + var grabberPropertiesStretchSpheres = { + shape: "Sphere", + dimensions: { x:0.02, y:0.02, z:0.02 }, + solid: true, visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: true, + ignoreRayIntersection: false, drawInFront: true - }); - - // var normalLine = Overlays.addOverlay("line3d", { - // visible: true, - // start: { x: 0, y: 0, z: 0 }, - // end: { x: 0, y: 0, z: 0 }, - // color: { red: 255, green: 255, blue: 0 }, - // ignoreRayIntersection: true, - // }); - - var grabberLBN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRBN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberLBF = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRBF = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberLTN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRTN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberLTF = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRTF = Overlays.addOverlay("cube", grabberPropertiesCorner); - - var grabberTOP = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberBOTTOM = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberLEFT = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberRIGHT = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberNEAR = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberFAR = Overlays.addOverlay("cube", grabberPropertiesFace); - - var grabberEdgeTR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeTL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeTF = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeTN = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBF = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBN = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeNR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeNL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge); - - var grabberSpotLightCircle = Overlays.addOverlay("circle3d", { - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberSpotLightLineT = Overlays.addOverlay("line3d", spotLightLineProperties); - var grabberSpotLightLineB = Overlays.addOverlay("line3d", spotLightLineProperties); - var grabberSpotLightLineL = Overlays.addOverlay("line3d", spotLightLineProperties); - var grabberSpotLightLineR = Overlays.addOverlay("line3d", spotLightLineProperties); - - var grabberSpotLightCenter = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightRadius = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); - - var spotLightGrabberHandles = [ - grabberSpotLightCircle, grabberSpotLightCenter, grabberSpotLightRadius, - grabberSpotLightLineT, grabberSpotLightLineB, grabberSpotLightLineL, grabberSpotLightLineR, - grabberSpotLightT, grabberSpotLightB, grabberSpotLightL, grabberSpotLightR - ]; - - var grabberPointLightCircleX = Overlays.addOverlay("circle3d", { - rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberPointLightCircleY = Overlays.addOverlay("circle3d", { - rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberPointLightCircleZ = Overlays.addOverlay("circle3d", { - rotation: Quat.fromPitchYawRollDegrees(0, 0, 0), - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberPointLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightF = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightN = Overlays.addOverlay("cube", grabberPropertiesEdge); - - var pointLightGrabberHandles = [ - grabberPointLightCircleX, grabberPointLightCircleY, grabberPointLightCircleZ, - grabberPointLightT, grabberPointLightB, grabberPointLightL, - grabberPointLightR, grabberPointLightF, grabberPointLightN - ]; - - var grabberCloner = Overlays.addOverlay("cube", grabberPropertiesCloner); - - var stretchHandles = [ - grabberLBN, - grabberRBN, - grabberLBF, - grabberRBF, - grabberLTN, - grabberRTN, - grabberLTF, - grabberRTF, - grabberTOP, - grabberBOTTOM, - grabberLEFT, - grabberRIGHT, - grabberNEAR, - grabberFAR, - grabberEdgeTR, - grabberEdgeTL, - grabberEdgeTF, - grabberEdgeTN, - grabberEdgeBR, - grabberEdgeBL, - grabberEdgeBF, - grabberEdgeBN, - grabberEdgeNR, - grabberEdgeNL, - grabberEdgeFR, - grabberEdgeFL, - - grabberSpotLightLineT, - grabberSpotLightLineB, - grabberSpotLightLineL, - grabberSpotLightLineR, - - grabberSpotLightCenter, - grabberSpotLightRadius, - grabberSpotLightL, - grabberSpotLightR, - grabberSpotLightT, - grabberSpotLightB, - - grabberPointLightT, - grabberPointLightB, - grabberPointLightL, - grabberPointLightR, - grabberPointLightF, - grabberPointLightN, - - grabberCloner - ]; - - - var baseOverlayAngles = { - x: 0, - y: 0, - z: 0 }; - var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); - var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { - position: { - x: 1, - y: 0, - z: 0 - }, - color: { - red: 51, - green: 152, - blue: 203 - }, + var grabberStretchXSphere = Overlays.addOverlay("shape", grabberPropertiesStretchSpheres); + Overlays.editOverlay(grabberStretchXSphere, { color : COLOR_RED }); + var grabberStretchYSphere = Overlays.addOverlay("shape", grabberPropertiesStretchSpheres); + Overlays.editOverlay(grabberStretchYSphere, { color : COLOR_GREEN }); + var grabberStretchZSphere = Overlays.addOverlay("shape", grabberPropertiesStretchSpheres); + Overlays.editOverlay(grabberStretchZSphere, { color : COLOR_BLUE }); + + var grabberPropertiesStretchPanel = { + shape: "Quad", alpha: 0.5, + dimensions: { x:GRABBER_SCALE_CUBE_OFFSET * 2, y:GRABBER_SCALE_CUBE_OFFSET * 2, z:0.01 }, solid: true, visible: false, - width: 300, - height: 200, - rotation: baseOverlayRotation, - ignoreRayIntersection: true // always ignore this - }); + ignoreRayIntersection: true, + drawInFront: true, + } + var grabberStretchXPanel = Overlays.addOverlay("shape", grabberPropertiesStretchPanel); + Overlays.editOverlay(grabberStretchXPanel, { color : COLOR_RED }); + var grabberStretchYPanel = Overlays.addOverlay("shape", grabberPropertiesStretchPanel); + Overlays.editOverlay(grabberStretchYPanel, { color : COLOR_GREEN }); + var grabberStretchZPanel = Overlays.addOverlay("shape", grabberPropertiesStretchPanel); + Overlays.editOverlay(grabberStretchZPanel, { color : COLOR_BLUE }); - var yawOverlayAngles = { - x: 90, - y: 0, - z: 0 + var grabberPropertiesScaleCubes = { + size: 0.025, + color: GRABBER_SCALE_CUBE_IDLE_COLOR, + solid: true, + visible: false, + ignoreRayIntersection: false, + drawInFront: true, + borderSize: 1.4 }; - var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); - var pitchOverlayAngles = { - x: 0, - y: 90, - z: 0 - }; - var pitchOverlayRotation = Quat.fromVec3Degrees(pitchOverlayAngles); - var rollOverlayAngles = { - x: 0, - y: 180, - z: 0 - }; - var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); + var grabberScaleLBNCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // (-x, -y, -z) + var grabberScaleRBNCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // (-x, -y, z) + var grabberScaleLBFCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // ( x, -y, -z) + var grabberScaleRBFCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // ( x, -y, z) + var grabberScaleLTNCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // (-x, y, -z) + var grabberScaleRTNCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // (-x, y, z) + var grabberScaleLTFCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // ( x, y, -z) + var grabberScaleRTFCube = Overlays.addOverlay("cube", grabberPropertiesScaleCubes); // ( x, y, z) - var xRailOverlay = Overlays.addOverlay("line3d", { + var grabberPropertiesScaleEdge = { + color: GRABBER_SCALE_EDGE_COLOR, visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 255, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - var yRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 255, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - var zRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 0, - blue: 255 - }, - ignoreRayIntersection: true // always ignore this - }); - - var rotateZeroOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 255, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - - var rotateCurrentOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 0, - blue: 255 - }, - ignoreRayIntersection: true // always ignore this - }); - - - var rotateOverlayInner = Overlays.addOverlay("circle3d", { - position: Vec3.ZERO, - size: 1, - color: { - red: 51, - green: 152, - blue: 203 - }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0, - majorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - minorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - - var rotateOverlayOuter = Overlays.addOverlay("circle3d", { - position: Vec3.ZERO, - size: 1, - color: { - red: 51, - green: 152, - blue: 203 - }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, - - hasTickMarks: true, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1, - majorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - minorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - - var rotateOverlayCurrent = Overlays.addOverlay("circle3d", { - position: Vec3.ZERO, - size: 1, - color: { - red: 224, - green: 67, - blue: 36 - }, - alpha: 0.8, - solid: true, - visible: false, - rotation: yawOverlayRotation, - ignoreRayIntersection: true, // always ignore this - hasTickMarks: true, - majorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - minorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - } - }); - - var yawHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true - }); - - - var pitchHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true - }); - - - var rollHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true - }); + ignoreRayIntersection: true, + drawInFront: true, + lineWidth: 0.2 + } + var grabberScaleTREdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleTLEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleTFEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleTNEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleBREdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleBLEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleBFEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleBNEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleNREdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleNLEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleFREdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberScaleFLEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); var allOverlays = [ - highlightBox, - selectionBox, - grabberMoveUp, - yawHandle, - pitchHandle, - rollHandle, - rotateOverlayInner, - rotateOverlayOuter, - rotateOverlayCurrent, - rotateZeroOverlay, - rotateCurrentOverlay, + grabberTranslateXCone, + grabberTranslateXCylinder, + grabberTranslateYCone, + grabberTranslateYCylinder, + grabberTranslateZCone, + grabberTranslateZCylinder, + grabberRotatePitchRing, + grabberRotateYawRing, + grabberRotateRollRing, + grabberRotateCurrentRing, rotationDegreesDisplay, - xRailOverlay, - yRailOverlay, - zRailOverlay, - baseOfEntityProjectionOverlay, - grabberSpotLightCircle, - grabberPointLightCircleX, - grabberPointLightCircleY, - grabberPointLightCircleZ + grabberStretchXSphere, + grabberStretchYSphere, + grabberStretchZSphere, + grabberStretchXPanel, + grabberStretchYPanel, + grabberStretchZPanel, + grabberScaleLBNCube, + grabberScaleRBNCube, + grabberScaleLBFCube, + grabberScaleRBFCube, + grabberScaleLTNCube, + grabberScaleRTNCube, + grabberScaleLTFCube, + grabberScaleRTFCube, + grabberScaleTREdge, + grabberScaleTLEdge, + grabberScaleTFEdge, + grabberScaleTNEdge, + grabberScaleBREdge, + grabberScaleBLEdge, + grabberScaleBFEdge, + grabberScaleBNEdge, + grabberScaleNREdge, + grabberScaleNLEdge, + grabberScaleFREdge, + grabberScaleFLEdge + ]; - ].concat(stretchHandles); - - overlayNames[highlightBox] = "highlightBox"; - overlayNames[selectionBox] = "selectionBox"; - overlayNames[baseOfEntityProjectionOverlay] = "baseOfEntityProjectionOverlay"; - overlayNames[grabberMoveUp] = "grabberMoveUp"; - overlayNames[grabberLBN] = "grabberLBN"; - overlayNames[grabberLBF] = "grabberLBF"; - overlayNames[grabberRBN] = "grabberRBN"; - overlayNames[grabberRBF] = "grabberRBF"; - overlayNames[grabberLTN] = "grabberLTN"; - overlayNames[grabberLTF] = "grabberLTF"; - overlayNames[grabberRTN] = "grabberRTN"; - overlayNames[grabberRTF] = "grabberRTF"; - - overlayNames[grabberTOP] = "grabberTOP"; - overlayNames[grabberBOTTOM] = "grabberBOTTOM"; - overlayNames[grabberLEFT] = "grabberLEFT"; - overlayNames[grabberRIGHT] = "grabberRIGHT"; - overlayNames[grabberNEAR] = "grabberNEAR"; - overlayNames[grabberFAR] = "grabberFAR"; - - overlayNames[grabberEdgeTR] = "grabberEdgeTR"; - overlayNames[grabberEdgeTL] = "grabberEdgeTL"; - overlayNames[grabberEdgeTF] = "grabberEdgeTF"; - overlayNames[grabberEdgeTN] = "grabberEdgeTN"; - overlayNames[grabberEdgeBR] = "grabberEdgeBR"; - overlayNames[grabberEdgeBL] = "grabberEdgeBL"; - overlayNames[grabberEdgeBF] = "grabberEdgeBF"; - overlayNames[grabberEdgeBN] = "grabberEdgeBN"; - overlayNames[grabberEdgeNR] = "grabberEdgeNR"; - overlayNames[grabberEdgeNL] = "grabberEdgeNL"; - overlayNames[grabberEdgeFR] = "grabberEdgeFR"; - overlayNames[grabberEdgeFL] = "grabberEdgeFL"; - - overlayNames[yawHandle] = "yawHandle"; - overlayNames[pitchHandle] = "pitchHandle"; - overlayNames[rollHandle] = "rollHandle"; - - overlayNames[rotateOverlayInner] = "rotateOverlayInner"; - overlayNames[rotateOverlayOuter] = "rotateOverlayOuter"; - overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent"; - - overlayNames[rotateZeroOverlay] = "rotateZeroOverlay"; - overlayNames[rotateCurrentOverlay] = "rotateCurrentOverlay"; - overlayNames[grabberCloner] = "grabberCloner"; - var activeTool = null; - var grabberTools = {}; + overlayNames[grabberTranslateXCone] = "grabberTranslateXCone"; + overlayNames[grabberTranslateXCylinder] = "grabberTranslateXCylinder"; + overlayNames[grabberTranslateYCone] = "grabberTranslateYCone"; + overlayNames[grabberTranslateYCylinder] = "grabberTranslateYCylinder"; + overlayNames[grabberTranslateZCone] = "grabberTranslateZCone"; + overlayNames[grabberTranslateZCylinder] = "grabberTranslateZCylinder"; + overlayNames[grabberRotatePitchRing] = "grabberRotatePitchRing"; + overlayNames[grabberRotateYawRing] = "grabberRotateYawRing"; + overlayNames[grabberRotateRollRing] = "grabberRotateRollRing"; + overlayNames[grabberRotateCurrentRing] = "grabberRotateCurrentRing"; + overlayNames[rotationDegreesDisplay] = "rotationDegreesDisplay"; + overlayNames[grabberStretchXSphere] = "grabberStretchXSphere"; + overlayNames[grabberStretchYSphere] = "grabberStretchYSphere"; + overlayNames[grabberStretchZSphere] = "grabberStretchZSphere"; + overlayNames[grabberStretchXPanel] = "grabberStretchXPanel"; + overlayNames[grabberStretchYPanel] = "grabberStretchYPanel"; + overlayNames[grabberStretchZPanel] = "grabberStretchZPanel"; + overlayNames[grabberScaleLBNCube] = "grabberScaleLBNCube"; + overlayNames[grabberScaleRBNCube] = "grabberScaleRBNCube"; + overlayNames[grabberScaleLBFCube] = "grabberScaleLBFCube"; + overlayNames[grabberScaleRBFCube] = "grabberScaleRBFCube"; + overlayNames[grabberScaleLTNCube] = "grabberScaleLTNCube"; + overlayNames[grabberScaleRTNCube] = "grabberScaleRTNCube"; + overlayNames[grabberScaleLTFCube] = "grabberScaleLTFCube"; + overlayNames[grabberScaleRTFCube] = "grabberScaleRTFCube"; + overlayNames[grabberScaleTREdge] = "grabberScaleTREdge"; + overlayNames[grabberScaleTLEdge] = "grabberScaleTLEdge"; + overlayNames[grabberScaleTFEdge] = "grabberScaleTFEdge"; + overlayNames[grabberScaleTNEdge] = "grabberScaleTNEdge"; + overlayNames[grabberScaleBREdge] = "grabberScaleBREdge"; + overlayNames[grabberScaleBLEdge] = "grabberScaleBLEdge"; + overlayNames[grabberScaleBFEdge] = "grabberScaleBFEdge"; + overlayNames[grabberScaleBNEdge] = "grabberScaleBNEdge"; + overlayNames[grabberScaleNREdge] = "grabberScaleNREdge"; + overlayNames[grabberScaleNLEdge] = "grabberScaleNLEdge"; + overlayNames[grabberScaleFREdge] = "grabberScaleFREdge"; + overlayNames[grabberScaleFLEdge] = "grabberScaleFLEdge"; // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we dont' get mousePressEvents. @@ -932,7 +553,6 @@ SelectionDisplay = (function() { that.triggerMapping.from(Controller.Standard.RT).peek().to(makeTriggerHandler(Controller.Standard.RightHand)); that.triggerMapping.from(Controller.Standard.LT).peek().to(makeTriggerHandler(Controller.Standard.LeftHand)); - function controllerComputePickRay() { var controllerPose = getControllerWorldLocation(activeHand, true); if (controllerPose.valid && that.triggered) { @@ -942,2352 +562,389 @@ SelectionDisplay = (function() { return {origin: controllerPosition, direction: controllerDirection}; } } + function generalComputePickRay(x, y) { return controllerComputePickRay() || Camera.computePickRay(x, y); } + function addGrabberTool(overlay, tool) { grabberTools[overlay] = tool; return tool; } - // @param: toolHandle: The overlayID associated with the tool - // that correlates to the tool you wish to query. - // @note: If toolHandle is null or undefined then activeTool - // will be checked against those values as opposed to - // the tool registered under toolHandle. Null & Undefined - // are treated as separate values. - // @return: bool - Indicates if the activeTool is that queried. - function isActiveTool(toolHandle) { - if (!toolHandle) { - // Allow isActiveTool(null) and similar to return true if there's - // no active tool - return (activeTool === toolHandle); - } - - if (!grabberTools.hasOwnProperty(toolHandle)) { - print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + toolHandle + ". Tools should be egistered via addGrabberTool."); - // EARLY EXIT - return false; - } - - return (activeTool === grabberTools[ toolHandle ]); - } - - // @return string - The mode of the currently active tool; - // otherwise, "UNKNOWN" if there's no active tool. - function getMode() { - return (activeTool ? activeTool.mode : "UNKNOWN"); - } - - - that.cleanup = function() { - for (var i = 0; i < allOverlays.length; i++) { - Overlays.deleteOverlay(allOverlays[i]); - } - for (var j = 0; j < selectionBoxes.length; j++) { - Overlays.deleteOverlay(selectionBoxes[j]); - } - }; - - that.highlightSelectable = function(entityID) { - var properties = Entities.getEntityProperties(entityID); - Overlays.editOverlay(highlightBox, { - visible: true, - position: properties.boundingBox.center, - dimensions: properties.dimensions, - rotation: properties.rotation - }); - }; - - that.unhighlightSelectable = function(entityID) { - Overlays.editOverlay(highlightBox, { - visible: false - }); - }; - - that.select = function(entityID, event) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - - lastCameraPosition = Camera.getPosition(); - lastCameraOrientation = Camera.getOrientation(); - - if (event !== false) { - pickRay = generalComputePickRay(event.x, event.y); - - var wantDebug = false; - if (wantDebug) { - print("select() with EVENT...... "); - print(" event.y:" + event.y); - Vec3.print(" current position:", properties.position); - } - - - } - - Overlays.editOverlay(highlightBox, { - visible: false - }); - - that.updateHandles(); - }; - - // Function: Calculate New Bound Extremes - // uses dot product to discover new top and bottom on the new referential (max and min) - that.calculateNewBoundExtremes = function(boundPointList, referenceVector) { - - if (boundPointList.length < 2) { - return [null, null]; - } - - var refMax = boundPointList[0]; - var refMin = boundPointList[1]; - - var dotMax = Vec3.dot(boundPointList[0], referenceVector); - var dotMin = Vec3.dot(boundPointList[1], referenceVector); - - if (dotMin > dotMax) { - dotMax = dotMin; - dotMin = Vec3.dot(boundPointList[0], referenceVector); - refMax = boundPointList[1]; - refMin = boundPointList[0]; - } - - for (var i = 2; i < boundPointList.length ; i++) { - var dotAux = Vec3.dot(boundPointList[i], referenceVector); - if (dotAux > dotMax) { - dotMax = dotAux; - refMax = boundPointList[i]; - } else if (dotAux < dotMin) { - dotMin = dotAux; - refMin = boundPointList[i]; - } - } - return [refMin, refMax]; - } - - // Function: Project Bounding Box Points - // Projects all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto - // one of the basis of the new avatar referencial - // dimensions - dimensions of the AABB (axis aligned bounding box) on the standard basis - // [1, 0, 0], [0, 1, 0], [0, 0, 1] - // v - projection vector - // rotateHandleOffset - offset for the rotation handle gizmo position - that.projectBoundingBoxPoints = function(dimensions, v, rotateHandleOffset) { - var projT_v = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), v); - projT_v = Vec3.multiply(projT_v, v); - - var projB_v = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), v); - projB_v = Vec3.multiply(projB_v, v); - - var projL_v = Vec3.dot(Vec3.multiply((dimensions.x / 2) + rotateHandleOffset, Vec3.UNIT_X), v); - projL_v = Vec3.multiply(projL_v, v); - - var projR_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.x / 2) - 1.0 * rotateHandleOffset, Vec3.UNIT_X), v); - projR_v = Vec3.multiply(projR_v, v); - - var projN_v = Vec3.dot(Vec3.multiply((dimensions.z / 2) + rotateHandleOffset, Vec3.FRONT), v); - projN_v = Vec3.multiply(projN_v, v); - - var projF_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.z / 2) - 1.0 * rotateHandleOffset, Vec3.FRONT), v); - projF_v = Vec3.multiply(projF_v, v); - - var projList = [projT_v, projB_v, projL_v, projR_v, projN_v, projF_v]; - - return that.calculateNewBoundExtremes(projList, v); - }; - - // FUNCTION: UPDATE ROTATION HANDLES - that.updateRotationHandles = function() { - var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; - var halfDimensions = Vec3.multiply(SelectionManager.worldDimensions, 0.5); - var innerActive = false; - var innerAlpha = 0.2; - var outerAlpha = 0.2; - if (innerActive) { - innerAlpha = 0.5; - } else { - outerAlpha = 0.5; - } - // prev 0.05 - var rotateHandleOffset = 0.05; - - var boundsCenter, objectCenter; - - var dimensions, rotation; - if (spaceMode === SPACE_LOCAL) { - rotation = SelectionManager.localRotation; - } else { - rotation = SelectionManager.worldRotation; - } - objectCenter = SelectionManager.worldPosition; - dimensions = SelectionManager.worldDimensions; - var position = objectCenter; - - boundsCenter = objectCenter; - - var yawCorner; - var pitchCorner; - var rollCorner; - - var cameraPosition = Camera.getPosition(); - var look = Vec3.normalize(Vec3.subtract(cameraPosition, objectCenter)); - - // place yaw, pitch and roll rotations on the avatar referential - - var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ - x: 0, - y: 180, - z: 0 - })); - var upVector = Quat.getUp(avatarReferential); - var rightVector = Vec3.multiply(-1, Quat.getRight(avatarReferential)); - var frontVector = Quat.getFront(avatarReferential); - - // project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) - // onto the new avatar referential - - // UP - var projUP = that.projectBoundingBoxPoints(dimensions, upVector, rotateHandleOffset); - // RIGHT - var projRIGHT = that.projectBoundingBoxPoints(dimensions, rightVector, rotateHandleOffset); - // FRONT - var projFRONT = that.projectBoundingBoxPoints(dimensions, frontVector, rotateHandleOffset); - - // YAW - yawCenter = Vec3.sum(boundsCenter, projUP[0]); - yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); - - yawHandleRotation = Quat.lookAt( - yawCorner, - Vec3.sum(yawCorner, upVector), - Vec3.subtract(yawCenter,yawCorner)); - yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); - - // PTCH - pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); - pitchCenter = Vec3.sum(boundsCenter, projRIGHT[0]); - - pitchHandleRotation = Quat.lookAt( - pitchCorner, - Vec3.sum(pitchCorner, rightVector), - Vec3.subtract(pitchCenter,pitchCorner)); - pitchHandleRotation = Quat.multiply(Quat.angleAxis(45, rightVector), pitchHandleRotation); - - // ROLL - rollCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[1]), projFRONT[0])); - rollCenter = Vec3.sum(boundsCenter, projFRONT[0]); - - rollHandleRotation = Quat.lookAt( - rollCorner, - Vec3.sum(rollCorner, frontVector), - Vec3.subtract(rollCenter,rollCorner)); - rollHandleRotation = Quat.multiply(Quat.angleAxis(45, frontVector), rollHandleRotation); - - - var rotateHandlesVisible = true; - var rotationOverlaysVisible = false; - // note: Commented out as these are currently unused here; however, - // leaving them around as they document intent of state as it - // relates to modes that may be useful later. - // var translateHandlesVisible = true; - // var selectionBoxVisible = true; - var isPointLight = false; - if (SelectionManager.selections.length === 1) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - isPointLight = (properties.type === "Light") && !properties.isSpotlight; - } - - if (isActiveTool(yawHandle) || isActiveTool(pitchHandle) || - isActiveTool(rollHandle) || isActiveTool(selectionBox) || isActiveTool(grabberCloner)) { - rotationOverlaysVisible = true; - rotateHandlesVisible = false; - // translateHandlesVisible = false; - // selectionBoxVisible = false; - } else if (isActiveTool(grabberMoveUp) || isPointLight) { - rotateHandlesVisible = false; - } else if (activeTool) { - // every other mode is a stretch mode... - rotateHandlesVisible = false; - // translateHandlesVisible = false; - } - - Overlays.editOverlay(rotateZeroOverlay, { - visible: rotationOverlaysVisible - }); - Overlays.editOverlay(rotateCurrentOverlay, { - visible: rotationOverlaysVisible - }); - - Overlays.editOverlay(yawHandle, { - visible: rotateHandlesVisible, - position: yawCorner, - rotation: yawHandleRotation - }); - Overlays.editOverlay(pitchHandle, { - visible: rotateHandlesVisible, - position: pitchCorner, - rotation: pitchHandleRotation - }); - Overlays.editOverlay(rollHandle, { - visible: rotateHandlesVisible, - position: rollCorner, - rotation: rollHandleRotation - }); - - - }; - - // FUNCTION: UPDATE HANDLE SIZES - that.updateHandleSizes = function() { - if (SelectionManager.hasSelection()) { - var diff = Vec3.subtract(SelectionManager.worldPosition, Camera.getPosition()); - var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 5; - var dimensions = SelectionManager.worldDimensions; - var avgDimension = (dimensions.x + dimensions.y + dimensions.z) / 3; - grabberSize = Math.min(grabberSize, avgDimension / 10); - - for (var i = 0; i < stretchHandles.length; i++) { - Overlays.editOverlay(stretchHandles[i], { - size: grabberSize - }); - } - var handleSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 7; - handleSize = Math.min(handleSize, avgDimension / 3); - - Overlays.editOverlay(yawHandle, { - scale: handleSize - }); - Overlays.editOverlay(pitchHandle, { - scale: handleSize - }); - Overlays.editOverlay(rollHandle, { - scale: handleSize - }); - var upDiff = Vec3.multiply(( - Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3), - Quat.getUp(MyAvatar.orientation) - ); - var pos = Vec3.sum(grabberMoveUpPosition, upDiff); - Overlays.editOverlay(grabberMoveUp, { - position: pos, - scale: handleSize / 1.25 - }); - } - }; - Script.update.connect(that.updateHandleSizes); - - // FUNCTION: SET SPACE MODE - that.setSpaceMode = function(newSpaceMode) { - var wantDebug = false; - if (wantDebug) { - print("======> SetSpaceMode called. ========"); - } - - if (spaceMode !== newSpaceMode) { - if (wantDebug) { - print(" Updating SpaceMode From: " + spaceMode + " To: " + newSpaceMode); - } - spaceMode = newSpaceMode; - that.updateHandles(); - } else if (wantDebug) { - print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + spaceMode + " DesiredMode: " + newSpaceMode); - } - if (wantDebug) { - print("====== SetSpaceMode called. <========"); - } - }; - - // FUNCTION: TOGGLE SPACE MODE - that.toggleSpaceMode = function() { - var wantDebug = false; - if (wantDebug) { - print("========> ToggleSpaceMode called. ========="); - } - if ((spaceMode === SPACE_WORLD) && (SelectionManager.selections.length > 1)) { - if (wantDebug) { - print("Local space editing is not available with multiple selections"); - } - return; - } - if (wantDebug) { - print("PreToggle: " + spaceMode); - } - spaceMode = (spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL; - that.updateHandles(); - if (wantDebug) { - print("PostToggle: " + spaceMode); - print("======== ToggleSpaceMode called. <========="); - } - }; - - // FUNCTION: UNSELECT ALL - // TODO?: Needs implementation - that.unselectAll = function() {}; - - // FUNCTION: UPDATE HANDLES - that.updateHandles = function() { - var wantDebug = false; - if (wantDebug) { - print("======> Update Handles ======="); - print(" Selections Count: " + SelectionManager.selections.length); - print(" SpaceMode: " + spaceMode); - print(" DisplayMode: " + getMode()); - } - if (SelectionManager.selections.length === 0) { - that.setOverlaysVisible(false); - return; - } - - // print(" Triggering updateRotationHandles"); - that.updateRotationHandles(); - - var rotation, dimensions, position, registrationPoint; - - if (spaceMode === SPACE_LOCAL) { - rotation = SelectionManager.localRotation; - dimensions = SelectionManager.localDimensions; - position = SelectionManager.localPosition; - registrationPoint = SelectionManager.localRegistrationPoint; - } else { - rotation = Quat.IDENTITY; - dimensions = SelectionManager.worldDimensions; - position = SelectionManager.worldPosition; - registrationPoint = SelectionManager.worldRegistrationPoint; - } - - var registrationPointDimensions = { - x: dimensions.x * registrationPoint.x, - y: dimensions.y * registrationPoint.y, - z: dimensions.z * registrationPoint.z - }; - - // Center of entity, relative to registration point - var center = getRelativeCenterPosition(dimensions, registrationPoint); - - // Distances in world coordinates relative to the registration point - var left = -registrationPointDimensions.x; - var right = dimensions.x - registrationPointDimensions.x; - var bottom = -registrationPointDimensions.y; - var top = dimensions.y - registrationPointDimensions.y; - var near = -registrationPointDimensions.z; - var far = dimensions.z - registrationPointDimensions.z; - var front = far; - - var worldTop = SelectionManager.worldDimensions.y / 2; - - var LBN = { - x: left, - y: bottom, - z: near - }; - var RBN = { - x: right, - y: bottom, - z: near - }; - var LBF = { - x: left, - y: bottom, - z: far - }; - var RBF = { - x: right, - y: bottom, - z: far - }; - var LTN = { - x: left, - y: top, - z: near - }; - var RTN = { - x: right, - y: top, - z: near - }; - var LTF = { - x: left, - y: top, - z: far - }; - var RTF = { - x: right, - y: top, - z: far - }; - - var TOP = { - x: center.x, - y: top, - z: center.z - }; - var BOTTOM = { - x: center.x, - y: bottom, - z: center.z - }; - var LEFT = { - x: left, - y: center.y, - z: center.z - }; - var RIGHT = { - x: right, - y: center.y, - z: center.z - }; - var NEAR = { - x: center.x, - y: center.y, - z: near - }; - var FAR = { - x: center.x, - y: center.y, - z: far - }; - - var EdgeTR = { - x: right, - y: top, - z: center.z - }; - var EdgeTL = { - x: left, - y: top, - z: center.z - }; - var EdgeTF = { - x: center.x, - y: top, - z: front - }; - var EdgeTN = { - x: center.x, - y: top, - z: near - }; - var EdgeBR = { - x: right, - y: bottom, - z: center.z - }; - var EdgeBL = { - x: left, - y: bottom, - z: center.z - }; - var EdgeBF = { - x: center.x, - y: bottom, - z: front - }; - var EdgeBN = { - x: center.x, - y: bottom, - z: near - }; - var EdgeNR = { - x: right, - y: center.y, - z: near - }; - var EdgeNL = { - x: left, - y: center.y, - z: near - }; - var EdgeFR = { - x: right, - y: center.y, - z: front - }; - var EdgeFL = { - x: left, - y: center.y, - z: front - }; - - LBN = Vec3.multiplyQbyV(rotation, LBN); - RBN = Vec3.multiplyQbyV(rotation, RBN); - LBF = Vec3.multiplyQbyV(rotation, LBF); - RBF = Vec3.multiplyQbyV(rotation, RBF); - LTN = Vec3.multiplyQbyV(rotation, LTN); - RTN = Vec3.multiplyQbyV(rotation, RTN); - LTF = Vec3.multiplyQbyV(rotation, LTF); - RTF = Vec3.multiplyQbyV(rotation, RTF); - - TOP = Vec3.multiplyQbyV(rotation, TOP); - BOTTOM = Vec3.multiplyQbyV(rotation, BOTTOM); - LEFT = Vec3.multiplyQbyV(rotation, LEFT); - RIGHT = Vec3.multiplyQbyV(rotation, RIGHT); - NEAR = Vec3.multiplyQbyV(rotation, NEAR); - FAR = Vec3.multiplyQbyV(rotation, FAR); - - EdgeTR = Vec3.multiplyQbyV(rotation, EdgeTR); - EdgeTL = Vec3.multiplyQbyV(rotation, EdgeTL); - EdgeTF = Vec3.multiplyQbyV(rotation, EdgeTF); - EdgeTN = Vec3.multiplyQbyV(rotation, EdgeTN); - EdgeBR = Vec3.multiplyQbyV(rotation, EdgeBR); - EdgeBL = Vec3.multiplyQbyV(rotation, EdgeBL); - EdgeBF = Vec3.multiplyQbyV(rotation, EdgeBF); - EdgeBN = Vec3.multiplyQbyV(rotation, EdgeBN); - EdgeNR = Vec3.multiplyQbyV(rotation, EdgeNR); - EdgeNL = Vec3.multiplyQbyV(rotation, EdgeNL); - EdgeFR = Vec3.multiplyQbyV(rotation, EdgeFR); - EdgeFL = Vec3.multiplyQbyV(rotation, EdgeFL); - - LBN = Vec3.sum(position, LBN); - RBN = Vec3.sum(position, RBN); - LBF = Vec3.sum(position, LBF); - RBF = Vec3.sum(position, RBF); - LTN = Vec3.sum(position, LTN); - RTN = Vec3.sum(position, RTN); - LTF = Vec3.sum(position, LTF); - RTF = Vec3.sum(position, RTF); - - TOP = Vec3.sum(position, TOP); - BOTTOM = Vec3.sum(position, BOTTOM); - LEFT = Vec3.sum(position, LEFT); - RIGHT = Vec3.sum(position, RIGHT); - NEAR = Vec3.sum(position, NEAR); - FAR = Vec3.sum(position, FAR); - - EdgeTR = Vec3.sum(position, EdgeTR); - EdgeTL = Vec3.sum(position, EdgeTL); - EdgeTF = Vec3.sum(position, EdgeTF); - EdgeTN = Vec3.sum(position, EdgeTN); - EdgeBR = Vec3.sum(position, EdgeBR); - EdgeBL = Vec3.sum(position, EdgeBL); - EdgeBF = Vec3.sum(position, EdgeBF); - EdgeBN = Vec3.sum(position, EdgeBN); - EdgeNR = Vec3.sum(position, EdgeNR); - EdgeNL = Vec3.sum(position, EdgeNL); - EdgeFR = Vec3.sum(position, EdgeFR); - EdgeFL = Vec3.sum(position, EdgeFL); - - var inModeRotate = (isActiveTool(yawHandle) || isActiveTool(pitchHandle) || isActiveTool(rollHandle)); - var inModeTranslate = (isActiveTool(selectionBox) || isActiveTool(grabberCloner) || isActiveTool(grabberMoveUp)); - var stretchHandlesVisible = !(inModeRotate || inModeTranslate) && (spaceMode === SPACE_LOCAL); - var extendedStretchHandlesVisible = (stretchHandlesVisible && showExtendedStretchHandles); - var cloneHandleVisible = !(inModeRotate || inModeTranslate); - if (wantDebug) { - print(" Set Non-Light Grabbers Visible - Norm: " + stretchHandlesVisible + " Ext: " + extendedStretchHandlesVisible); - } - var isSingleSelection = (SelectionManager.selections.length === 1); - - if (isSingleSelection) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - var isLightSelection = (properties.type === "Light"); - if (isLightSelection) { - if (wantDebug) { - print(" Light Selection revoking Non-Light Grabbers Visibility!"); - } - stretchHandlesVisible = false; - extendedStretchHandlesVisible = false; - cloneHandleVisible = false; - if (properties.isSpotlight) { - that.setPointLightHandlesVisible(false); - - var distance = (properties.dimensions.z / 2) * Math.sin(properties.cutoff * (Math.PI / 180)); - var showEdgeSpotGrabbers = !(inModeTranslate || inModeRotate); - Overlays.editOverlay(grabberSpotLightCenter, { - position: position, - visible: false - }); - Overlays.editOverlay(grabberSpotLightRadius, { - position: NEAR, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - - Overlays.editOverlay(grabberSpotLightL, { - position: EdgeNL, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightR, { - position: EdgeNR, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightT, { - position: EdgeTN, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightB, { - position: EdgeBN, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightCircle, { - position: NEAR, - dimensions: { - x: distance, - y: distance, - z: 1 - }, - rotation: rotation, - visible: true - }); - - Overlays.editOverlay(grabberSpotLightLineT, { - start: position, - end: EdgeTN, - visible: true - }); - Overlays.editOverlay(grabberSpotLightLineB, { - start: position, - end: EdgeBN, - visible: true - }); - Overlays.editOverlay(grabberSpotLightLineR, { - start: position, - end: EdgeNR, - visible: true - }); - Overlays.editOverlay(grabberSpotLightLineL, { - start: position, - end: EdgeNL, - visible: true - }); - - } else { // ..it's a PointLight - that.setSpotLightHandlesVisible(false); - - var showEdgePointGrabbers = !inModeTranslate; - Overlays.editOverlay(grabberPointLightT, { - position: TOP, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightB, { - position: BOTTOM, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightL, { - position: LEFT, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightR, { - position: RIGHT, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightF, { - position: FAR, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightN, { - position: NEAR, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightCircleX, { - position: position, - rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), - dimensions: { - x: properties.dimensions.z / 2.0, - y: properties.dimensions.z / 2.0, - z: 1 - }, - visible: true - }); - Overlays.editOverlay(grabberPointLightCircleY, { - position: position, - rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), - dimensions: { - x: properties.dimensions.z / 2.0, - y: properties.dimensions.z / 2.0, - z: 1 - }, - visible: true - }); - Overlays.editOverlay(grabberPointLightCircleZ, { - position: position, - rotation: rotation, - dimensions: { - x: properties.dimensions.z / 2.0, - y: properties.dimensions.z / 2.0, - z: 1 - }, - visible: true - }); - } - } else { // ..it's not a light at all - that.setSpotLightHandlesVisible(false); - that.setPointLightHandlesVisible(false); - } - }// end of isSingleSelection - - - Overlays.editOverlay(grabberLBN, { - visible: stretchHandlesVisible, - rotation: rotation, - position: LBN - }); - Overlays.editOverlay(grabberRBN, { - visible: stretchHandlesVisible, - rotation: rotation, - position: RBN - }); - Overlays.editOverlay(grabberLBF, { - visible: stretchHandlesVisible, - rotation: rotation, - position: LBF - }); - Overlays.editOverlay(grabberRBF, { - visible: stretchHandlesVisible, - rotation: rotation, - position: RBF - }); - - Overlays.editOverlay(grabberLTN, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: LTN - }); - Overlays.editOverlay(grabberRTN, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: RTN - }); - Overlays.editOverlay(grabberLTF, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: LTF - }); - Overlays.editOverlay(grabberRTF, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: RTF - }); - - Overlays.editOverlay(grabberTOP, { - visible: stretchHandlesVisible, - rotation: rotation, - position: TOP - }); - Overlays.editOverlay(grabberBOTTOM, { - visible: stretchHandlesVisible, - rotation: rotation, - position: BOTTOM - }); - Overlays.editOverlay(grabberLEFT, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: LEFT - }); - Overlays.editOverlay(grabberRIGHT, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: RIGHT - }); - Overlays.editOverlay(grabberNEAR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: NEAR - }); - Overlays.editOverlay(grabberFAR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: FAR - }); - - Overlays.editOverlay(grabberCloner, { - visible: cloneHandleVisible, - rotation: rotation, - position: EdgeTR - }); - - var selectionBoxPosition = Vec3.multiplyQbyV(rotation, center); - selectionBoxPosition = Vec3.sum(position, selectionBoxPosition); - Overlays.editOverlay(selectionBox, { - position: selectionBoxPosition, - dimensions: dimensions, - rotation: rotation, - visible: !inModeRotate - }); - - // Create more selection box overlays if we don't have enough - var overlaysNeeded = SelectionManager.selections.length - selectionBoxes.length; - for (var i = 0; i < overlaysNeeded; i++) { - selectionBoxes.push( - Overlays.addOverlay("cube", { - position: { - x: 0, - y: 0, - z: 0 - }, - size: 1, - color: { - red: 255, - green: 153, - blue: 0 - }, - alpha: 1, - solid: false, - visible: false, - dashed: false, - ignoreRayIntersection: true - })); - } - - i = 0; - // Only show individual selections boxes if there is more than 1 selection - if (SelectionManager.selections.length > 1) { - for (; i < SelectionManager.selections.length; i++) { - var props = Entities.getEntityProperties(SelectionManager.selections[i]); - - // Adjust overlay position to take registrationPoint into account - // centeredRP = registrationPoint with range [-0.5, 0.5] - var centeredRP = Vec3.subtract(props.registrationPoint, { - x: 0.5, - y: 0.5, - z: 0.5 - }); - var offset = vec3Mult(props.dimensions, centeredRP); - offset = Vec3.multiply(-1, offset); - offset = Vec3.multiplyQbyV(props.rotation, offset); - var curBoxPosition = Vec3.sum(props.position, offset); - - var color = {red: 255, green: 128, blue: 0}; - if (i >= SelectionManager.selections.length - 1) { - color = {red: 255, green: 255, blue: 64}; - } - - Overlays.editOverlay(selectionBoxes[i], { - position: curBoxPosition, - color: color, - rotation: props.rotation, - dimensions: props.dimensions, - visible: true - }); - } - } - // Hide any remaining selection boxes - for (; i < selectionBoxes.length; i++) { - Overlays.editOverlay(selectionBoxes[i], { - visible: false - }); - } - - Overlays.editOverlay(grabberEdgeTR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTR - }); - Overlays.editOverlay(grabberEdgeTL, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTL - }); - Overlays.editOverlay(grabberEdgeTF, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTF - }); - Overlays.editOverlay(grabberEdgeTN, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTN - }); - Overlays.editOverlay(grabberEdgeBR, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBR - }); - Overlays.editOverlay(grabberEdgeBL, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBL - }); - Overlays.editOverlay(grabberEdgeBF, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBF - }); - Overlays.editOverlay(grabberEdgeBN, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBN - }); - Overlays.editOverlay(grabberEdgeNR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeNR - }); - Overlays.editOverlay(grabberEdgeNL, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeNL - }); - Overlays.editOverlay(grabberEdgeFR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeFR - }); - Overlays.editOverlay(grabberEdgeFL, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeFL - }); - - var grabberMoveUpOffset = 0.1; - var upVec = Quat.getUp(MyAvatar.orientation); - grabberMoveUpPosition = { - x: position.x + (grabberMoveUpOffset + worldTop) * upVec.x , - y: position.y+ (grabberMoveUpOffset + worldTop) * upVec.y, - z: position.z + (grabberMoveUpOffset + worldTop) * upVec.z - }; - Overlays.editOverlay(grabberMoveUp, { - visible: (!activeTool) || isActiveTool(grabberMoveUp) - }); - - Overlays.editOverlay(baseOfEntityProjectionOverlay, { - visible: !inModeRotate, - solid: true, - position: { - x: SelectionManager.worldPosition.x, - y: grid.getOrigin().y, - z: SelectionManager.worldPosition.z - }, - dimensions: { - x: SelectionManager.worldDimensions.x, - y: SelectionManager.worldDimensions.z - }, - rotation: Quat.fromPitchYawRollDegrees(90, 0, 0) - }); - - if (wantDebug) { - print("====== Update Handles <======="); - } - - }; - - function helperSetOverlaysVisibility(handleArray, isVisible) { - var numHandles = handleArray.length; - var visibilityUpdate = { visible: isVisible }; - for (var handleIndex = 0; handleIndex < numHandles; ++handleIndex) { - Overlays.editOverlay(handleArray[ handleIndex ], visibilityUpdate); - } - } - - // FUNCTION: SET OVERLAYS VISIBLE - that.setOverlaysVisible = function(isVisible) { - helperSetOverlaysVisibility(allOverlays, isVisible); - helperSetOverlaysVisibility(selectionBoxes, isVisible); - }; - - // FUNCTION: SET ROTATION HANDLES VISIBLE - that.setRotationHandlesVisible = function(isVisible) { - var visibilityUpdate = { visible: isVisible }; - Overlays.editOverlay(yawHandle, visibilityUpdate); - Overlays.editOverlay(pitchHandle, visibilityUpdate); - Overlays.editOverlay(rollHandle, visibilityUpdate); - }; - - // FUNCTION: SET STRETCH HANDLES VISIBLE - that.setStretchHandlesVisible = function(isVisible) { - helperSetOverlaysVisibility(stretchHandles, isVisible); - }; - - // FUNCTION: SET GRABBER MOVE UP VISIBLE - that.setGrabberMoveUpVisible = function(isVisible) { - Overlays.editOverlay(grabberMoveUp, { visible: isVisible }); - }; - - // FUNCTION: SET GRABBER TOOLS UP VISIBLE - that.setGrabberToolsVisible = function(isVisible) { - var visibilityUpdate = { visible: isVisible }; - for (var toolKey in grabberTools) { - if (!grabberTools.hasOwnProperty(toolKey)) { - // EARLY ITERATION EXIT--(On to the next one) - continue; - } - - Overlays.editOverlay(toolKey, visibilityUpdate); - } - }; - - // FUNCTION: SET POINT LIGHT HANDLES VISIBLE - that.setPointLightHandlesVisible = function(isVisible) { - helperSetOverlaysVisibility(pointLightGrabberHandles, isVisible); - }; - - // FUNCTION: SET SPOT LIGHT HANDLES VISIBLE - that.setSpotLightHandlesVisible = function(isVisible) { - helperSetOverlaysVisibility(spotLightGrabberHandles, isVisible); - }; - - // FUNCTION: UNSELECT - // TODO?: Needs implementation - that.unselect = function(entityID) {}; - - var initialXZPick = null; - var isConstrained = false; - var constrainMajorOnly = false; - var startPosition = null; - var duplicatedEntityIDs = null; - - // TOOL DEFINITION: TRANSLATE XZ TOOL - var translateXZTool = addGrabberTool(selectionBox,{ - mode: 'TRANSLATE_XZ', - pickPlanePosition: { x: 0, y: 0, z: 0 }, - greatestDimension: 0.0, - startingDistance: 0.0, - startingElevation: 0.0, - onBegin: function(event, pickRay, pickResult, doClone) { - var wantDebug = false; - if (wantDebug) { - print("================== TRANSLATE_XZ(Beg) -> ======================="); - Vec3.print(" pickRay", pickRay); - Vec3.print(" pickRay.origin", pickRay.origin); - Vec3.print(" pickResult.intersection", pickResult.intersection); - } - - SelectionManager.saveProperties(); - that.setRotationHandlesVisible(false); - that.setStretchHandlesVisible(false); - that.setGrabberMoveUpVisible(false); - - startPosition = SelectionManager.worldPosition; - - translateXZTool.pickPlanePosition = pickResult.intersection; - translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, SelectionManager.worldDimensions.y), SelectionManager.worldDimensions.z); - translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); - translateXZTool.startingElevation = translateXZTool.elevation(pickRay.origin, translateXZTool.pickPlanePosition); - if (wantDebug) { - print(" longest dimension: " + translateXZTool.greatestDimension); - print(" starting distance: " + translateXZTool.startingDistance); - print(" starting elevation: " + translateXZTool.startingElevation); - } - - initialXZPick = rayPlaneIntersection(pickRay, translateXZTool.pickPlanePosition, { - x: 0, - y: 1, - z: 0 - }); - - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt || doClone) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } - } - } else { - duplicatedEntityIDs = null; - } - - isConstrained = false; - if (wantDebug) { - print("================== TRANSLATE_XZ(End) <- ======================="); - } - }, - onEnd: function(event, reason) { - pushCommandForSelections(duplicatedEntityIDs); - - Overlays.editOverlay(xRailOverlay, { - visible: false - }); - Overlays.editOverlay(zRailOverlay, { - visible: false - }); - }, - elevation: function(origin, intersection) { - return (origin.y - intersection.y) / Vec3.distance(origin, intersection); - }, - onMove: function(event) { - var wantDebug = false; - pickRay = generalComputePickRay(event.x, event.y); - - var pick = rayPlaneIntersection2(pickRay, translateXZTool.pickPlanePosition, { - x: 0, - y: 1, - z: 0 - }); - - // If the pick ray doesn't hit the pick plane in this direction, do nothing. - // this will happen when someone drags across the horizon from the side they started on. - if (!pick) { - if (wantDebug) { - print(" "+ translateXZTool.mode + "Pick ray does not intersect XZ plane."); - } - - // EARLY EXIT--(Invalid ray detected.) - return; - } - - var vector = Vec3.subtract(pick, initialXZPick); - - // If the mouse is too close to the horizon of the pick plane, stop moving - var MIN_ELEVATION = 0.02; // largest dimension of object divided by distance to it - var elevation = translateXZTool.elevation(pickRay.origin, pick); - if (wantDebug) { - print("Start Elevation: " + translateXZTool.startingElevation + ", elevation: " + elevation); - } - if ((translateXZTool.startingElevation > 0.0 && elevation < MIN_ELEVATION) || - (translateXZTool.startingElevation < 0.0 && elevation > -MIN_ELEVATION)) { - if (wantDebug) { - print(" "+ translateXZTool.mode + " - too close to horizon!"); - } - - // EARLY EXIT--(Don't proceed past the reached limit.) - return; - } - - // If the angular size of the object is too small, stop moving - var MIN_ANGULAR_SIZE = 0.01; // Radians - if (translateXZTool.greatestDimension > 0) { - var angularSize = Math.atan(translateXZTool.greatestDimension / Vec3.distance(pickRay.origin, pick)); - if (wantDebug) { - print("Angular size = " + angularSize); - } - if (angularSize < MIN_ANGULAR_SIZE) { - return; - } - } - - // If shifted, constrain to one axis - if (event.isShifted) { - if (Math.abs(vector.x) > Math.abs(vector.z)) { - vector.z = 0; - } else { - vector.x = 0; - } - if (!isConstrained) { - Overlays.editOverlay(xRailOverlay, { - visible: true - }); - var xStart = Vec3.sum(startPosition, { - x: -10000, - y: 0, - z: 0 - }); - var xEnd = Vec3.sum(startPosition, { - x: 10000, - y: 0, - z: 0 - }); - var zStart = Vec3.sum(startPosition, { - x: 0, - y: 0, - z: -10000 - }); - var zEnd = Vec3.sum(startPosition, { - x: 0, - y: 0, - z: 10000 - }); - Overlays.editOverlay(xRailOverlay, { - start: xStart, - end: xEnd, - visible: true - }); - Overlays.editOverlay(zRailOverlay, { - start: zStart, - end: zEnd, - visible: true - }); - isConstrained = true; - } - } else { - if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { - visible: false - }); - Overlays.editOverlay(zRailOverlay, { - visible: false - }); - isConstrained = false; - } - } - - constrainMajorOnly = event.isControl; - var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, SelectionManager.worldDimensions)); - vector = Vec3.subtract( - grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), - cornerPosition); - - - for (var i = 0; i < SelectionManager.selections.length; i++) { - var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; - if (!properties) { - continue; - } - var newPosition = Vec3.sum(properties.position, { - x: vector.x, - y: 0, - z: vector.z - }); - Entities.editEntity(SelectionManager.selections[i], { - position: newPosition - }); - - if (wantDebug) { - print("translateXZ... "); - Vec3.print(" vector:", vector); - Vec3.print(" newPosition:", properties.position); - Vec3.print(" newPosition:", newPosition); - } - } - - SelectionManager._update(); - } - }); - - // GRABBER TOOL: GRABBER MOVE UP - var lastXYPick = null; - var upDownPickNormal = null; - addGrabberTool(grabberMoveUp, { - mode: "TRANSLATE_UP_DOWN", - onBegin: function(event, pickRay, pickResult) { - upDownPickNormal = Quat.getForward(lastCameraOrientation); - lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); - - SelectionManager.saveProperties(); - that.setGrabberMoveUpVisible(true); - that.setStretchHandlesVisible(false); - that.setRotationHandlesVisible(false); - - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } - } - } else { - duplicatedEntityIDs = null; - } - }, - onEnd: function(event, reason) { - pushCommandForSelections(duplicatedEntityIDs); - }, - onMove: function(event) { - pickRay = generalComputePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); - - var vector = Vec3.subtract(newIntersection, lastXYPick); - - // project vector onto avatar up vector - // we want the avatar referential not the camera. - var avatarUpVector = Quat.getUp(MyAvatar.orientation); - var dotVectorUp = Vec3.dot(vector, avatarUpVector); - vector = Vec3.multiply(dotVectorUp, avatarUpVector); - - - vector = grid.snapToGrid(vector); - - - - var wantDebug = false; - if (wantDebug) { - print("translateUpDown... "); - print(" event.y:" + event.y); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - // Vec3.print(" newPosition:", newPosition); - } - for (var i = 0; i < SelectionManager.selections.length; i++) { - var id = SelectionManager.selections[i]; - var properties = SelectionManager.savedProperties[id]; - - var original = properties.position; - var newPosition = Vec3.sum(properties.position, vector); - - Entities.editEntity(id, { - position: newPosition - }); - } - - SelectionManager._update(); - } - }); - - // GRABBER TOOL: GRABBER CLONER - addGrabberTool(grabberCloner, { - mode: "CLONE", - onBegin: function(event, pickRay, pickResult) { - var doClone = true; - translateXZTool.onBegin(event,pickRay,pickResult,doClone); - }, - elevation: function (event) { - translateXZTool.elevation(event); - }, - - onEnd: function (event) { - translateXZTool.onEnd(event); - }, - - onMove: function (event) { - translateXZTool.onMove(event); - } - }); - - - // FUNCTION: VEC 3 MULT - var vec3Mult = function(v1, v2) { - return { - x: v1.x * v2.x, - y: v1.y * v2.y, - z: v1.z * v2.z - }; - }; - - // FUNCTION: MAKE STRETCH TOOL - // stretchMode - name of mode - // direction - direction to stretch in - // pivot - point to use as a pivot - // offset - the position of the overlay tool relative to the selections center position - // @return: tool obj - var makeStretchTool = function(stretchMode, direction, pivot, offset, customOnMove) { - // directionFor3DStretch - direction and pivot for 3D stretch - // distanceFor3DStretch - distance from the intersection point and the handController - // used to increase the scale taking into account the distance to the object - // DISTANCE_INFLUENCE_THRESHOLD - constant that holds the minimum distance where the - // distance to the object will influence the stretch/resize/scale - var directionFor3DStretch = getDirectionsFor3DStretch(stretchMode); - var distanceFor3DStretch = 0; - var DISTANCE_INFLUENCE_THRESHOLD = 1.2; - - - var signs = { - x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), - y: direction.y < 0 ? -1 : (direction.y > 0 ? 1 : 0), - z: direction.z < 0 ? -1 : (direction.z > 0 ? 1 : 0) - }; - - var mask = { - x: Math.abs(direction.x) > 0 ? 1 : 0, - y: Math.abs(direction.y) > 0 ? 1 : 0, - z: Math.abs(direction.z) > 0 ? 1 : 0 - }; - - - var numDimensions = mask.x + mask.y + mask.z; - - var planeNormal = null; + function addGrabberTranslateTool(overlay, mode, direction) { + var pickNormal = null; var lastPick = null; - var lastPick3D = null; - var initialPosition = null; - var initialDimensions = null; - var initialIntersection = null; - var initialProperties = null; - var registrationPoint = null; - var deltaPivot = null; - var deltaPivot3D = null; - var pickRayPosition = null; - var pickRayPosition3D = null; - var rotation = null; - - var onBegin = function(event, pickRay, pickResult) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - initialProperties = properties; - rotation = (spaceMode === SPACE_LOCAL) ? properties.rotation : Quat.IDENTITY; - - if (spaceMode === SPACE_LOCAL) { - rotation = SelectionManager.localRotation; - initialPosition = SelectionManager.localPosition; - initialDimensions = SelectionManager.localDimensions; - registrationPoint = SelectionManager.localRegistrationPoint; - } else { - rotation = SelectionManager.worldRotation; - initialPosition = SelectionManager.worldPosition; - initialDimensions = SelectionManager.worldDimensions; - registrationPoint = SelectionManager.worldRegistrationPoint; - } - - // Modify range of registrationPoint to be [-0.5, 0.5] - var centeredRP = Vec3.subtract(registrationPoint, { - x: 0.5, - y: 0.5, - z: 0.5 - }); - - // Scale pivot to be in the same range as registrationPoint - var scaledPivot = Vec3.multiply(0.5, pivot); - deltaPivot = Vec3.subtract(centeredRP, scaledPivot); - - var scaledOffset = Vec3.multiply(0.5, offset); - - // Offset from the registration point - offsetRP = Vec3.subtract(scaledOffset, centeredRP); - - // Scaled offset in world coordinates - var scaledOffsetWorld = vec3Mult(initialDimensions, offsetRP); - - pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); - - if (directionFor3DStretch) { - // pivot, offset and pickPlanePosition for 3D manipulation - var scaledPivot3D = Vec3.multiply(0.5, Vec3.multiply(1.0, directionFor3DStretch)); - deltaPivot3D = Vec3.subtract(centeredRP, scaledPivot3D); - - var scaledOffsetWorld3D = vec3Mult(initialDimensions, - Vec3.subtract(Vec3.multiply(0.5, Vec3.multiply(-1.0, directionFor3DStretch)), centeredRP)); - - pickRayPosition3D = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); - } - var start = null; - var end = null; - if ((numDimensions === 1) && mask.x) { - start = Vec3.multiplyQbyV(rotation, { - x: -10000, - y: 0, - z: 0 - }); - start = Vec3.sum(start, properties.position); - end = Vec3.multiplyQbyV(rotation, { - x: 10000, - y: 0, - z: 0 - }); - end = Vec3.sum(end, properties.position); - Overlays.editOverlay(xRailOverlay, { - start: start, - end: end, - visible: true - }); - } - if ((numDimensions === 1) && mask.y) { - start = Vec3.multiplyQbyV(rotation, { - x: 0, - y: -10000, - z: 0 - }); - start = Vec3.sum(start, properties.position); - end = Vec3.multiplyQbyV(rotation, { - x: 0, - y: 10000, - z: 0 - }); - end = Vec3.sum(end, properties.position); - Overlays.editOverlay(yRailOverlay, { - start: start, - end: end, - visible: true - }); - } - if ((numDimensions === 1) && mask.z) { - start = Vec3.multiplyQbyV(rotation, { - x: 0, - y: 0, - z: -10000 - }); - start = Vec3.sum(start, properties.position); - end = Vec3.multiplyQbyV(rotation, { - x: 0, - y: 0, - z: 10000 - }); - end = Vec3.sum(end, properties.position); - Overlays.editOverlay(zRailOverlay, { - start: start, - end: end, - visible: true - }); - } - if (numDimensions === 1) { - if (mask.x === 1) { - planeNormal = { - x: 0, - y: 1, - z: 0 - }; - } else if (mask.y === 1) { - planeNormal = { - x: 1, - y: 0, - z: 0 - }; - } else { - planeNormal = { - x: 0, - y: 1, - z: 0 - }; + var projectionVector = null; + addGrabberTool(overlay, { + mode: mode, + onBegin: function(event, pickRay, pickResult) { + if (direction === TRANSLATE_DIRECTION.X) { + pickNormal = { x:0, y:0, z:1 }; + } else if (direction === TRANSLATE_DIRECTION.Y) { + pickNormal = { x:1, y:0, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Z) { + pickNormal = { x:0, y:1, z:0 }; } - } else if (numDimensions === 2) { - if (mask.x === 0) { - planeNormal = { - x: 1, - y: 0, - z: 0 - }; - } else if (mask.y === 0) { - planeNormal = { - x: 0, - y: 1, - z: 0 - }; - } else { - planeNormal = { - x: 0, - y: 0, - z: 1 - }; - } - } - - planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); - lastPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); - - var planeNormal3D = { - x: 0, - y: 0, - z: 0 - }; - if (directionFor3DStretch) { - lastPick3D = rayPlaneIntersection(pickRay, - pickRayPosition3D, - planeNormal3D); - distanceFor3DStretch = Vec3.length(Vec3.subtract(pickRayPosition3D, pickRay.origin)); - } - - SelectionManager.saveProperties(); - }; - var onEnd = function(event, reason) { - Overlays.editOverlay(xRailOverlay, { - visible: false - }); - Overlays.editOverlay(yRailOverlay, { - visible: false - }); - Overlays.editOverlay(zRailOverlay, { - visible: false - }); + var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + pickNormal = Vec3.multiplyQbyV(rotation, pickNormal); - pushCommandForSelections(); - }; + lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + + SelectionManager.saveProperties(); - var onMove = function(event) { - var proportional = (spaceMode === SPACE_WORLD) || event.isShifted || isActiveTool(grabberSpotLightRadius); - - var position, dimensions, rotation; - if (spaceMode === SPACE_LOCAL) { - position = SelectionManager.localPosition; - dimensions = SelectionManager.localDimensions; - rotation = SelectionManager.localRotation; - } else { - position = SelectionManager.worldPosition; - dimensions = SelectionManager.worldDimensions; - rotation = SelectionManager.worldRotation; - } - - var localDeltaPivot = deltaPivot; - var localSigns = signs; - - var pickRay = generalComputePickRay(event.x, event.y); - - // Are we using handControllers or Mouse - only relevant for 3D tools - var controllerPose = getControllerWorldLocation(activeHand, true); - var vector = null; - if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && - controllerPose.valid && that.triggered && directionFor3DStretch) { - localDeltaPivot = deltaPivot3D; - - newPick = pickRay.origin; - - vector = Vec3.subtract(newPick, lastPick3D); - - vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); - - if (distanceFor3DStretch > DISTANCE_INFLUENCE_THRESHOLD) { - // Range of Motion - vector = Vec3.multiply(distanceFor3DStretch , vector); - } - - localSigns = directionFor3DStretch; - - } else { - newPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); - vector = Vec3.subtract(newPick, lastPick); - - vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); - - vector = vec3Mult(mask, vector); - - } - - if (customOnMove) { - var change = Vec3.multiply(-1, vec3Mult(localSigns, vector)); - customOnMove(vector, change); - } else { - vector = grid.snapToSpacing(vector); - - var changeInDimensions = Vec3.multiply(-1, vec3Mult(localSigns, vector)); - var newDimensions; - if (proportional) { - var absX = Math.abs(changeInDimensions.x); - var absY = Math.abs(changeInDimensions.y); - var absZ = Math.abs(changeInDimensions.z); - var pctChange = 0; - if (absX > absY && absX > absZ) { - pctChange = changeInDimensions.x / initialProperties.dimensions.x; - pctChange = changeInDimensions.x / initialDimensions.x; - } else if (absY > absZ) { - pctChange = changeInDimensions.y / initialProperties.dimensions.y; - pctChange = changeInDimensions.y / initialDimensions.y; - } else { - pctChange = changeInDimensions.z / initialProperties.dimensions.z; - pctChange = changeInDimensions.z / initialDimensions.z; + that.setGrabberTranslateXVisible(direction === TRANSLATE_DIRECTION.X); + that.setGrabberTranslateYVisible(direction === TRANSLATE_DIRECTION.Y); + that.setGrabberTranslateZVisible(direction === TRANSLATE_DIRECTION.Z); + that.setGrabberRotateVisible(false); + that.setGrabberStretchVisible(false); + that.setGrabberScaleVisible(false); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt) { + duplicatedEntityIDs = []; + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + if (!properties.locked) { + var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties + }); + } } - pctChange += 1.0; - newDimensions = Vec3.multiply(pctChange, initialDimensions); } else { - newDimensions = Vec3.sum(initialDimensions, changeInDimensions); + duplicatedEntityIDs = null; + } + }, + onEnd: function(event, reason) { + pushCommandForSelections(duplicatedEntityIDs); + }, + onMove: function(event) { + pickRay = generalComputePickRay(event.x, event.y); + + // translate mode left/right based on view toward entity + var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + var vector = Vec3.subtract(newIntersection, lastPick); + + if (direction === TRANSLATE_DIRECTION.X) { + projectionVector = { x:1, y:0, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Y) { + projectionVector = { x:0, y:1, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Z) { + projectionVector = { x:0, y:0, z:1 }; } - newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); - newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); - newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); - - var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(localDeltaPivot, changeInDimensions)); - var newPosition = Vec3.sum(initialPosition, changeInPosition); + var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); + var dotVector = Vec3.dot(vector, projectionVector); + vector = Vec3.multiply(dotVector, projectionVector); + vector = grid.snapToGrid(vector); + + var wantDebug = false; + if (wantDebug) { + print("translateUpDown... "); + print(" event.y:" + event.y); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + // Vec3.print(" newPosition:", newPosition); + } + for (var i = 0; i < SelectionManager.selections.length; i++) { - Entities.editEntity(SelectionManager.selections[i], { - position: newPosition, + var id = SelectionManager.selections[i]; + var properties = SelectionManager.savedProperties[id]; + var newPosition = Vec3.sum(properties.position, vector); + Entities.editEntity(id, { position: newPosition }); + } + + SelectionManager._update(); + } + }); + } + + function addGrabberStretchTool(overlay, mode, direction) { + var pickNormal = null; + var lastPick = null; + var projectionVector = null; + var stretchPanel = null; + addGrabberTool(overlay, { + mode: mode, + onBegin: function(event, pickRay, pickResult) { + if (direction === TRANSLATE_DIRECTION.X) { + stretchPanel = grabberStretchXPanel; + pickNormal = { x:0, y:0, z:1 }; + } else if (direction === TRANSLATE_DIRECTION.Y) { + stretchPanel = grabberStretchYPanel; + pickNormal = { x:1, y:0, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Z) { + stretchPanel = grabberStretchZPanel; + pickNormal = { x:0, y:1, z:0 }; + } + + Overlays.editOverlay(stretchPanel, { visible:true }); + + var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + pickNormal = Vec3.multiplyQbyV(rotation, pickNormal); + + lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + + SelectionManager.saveProperties(); + + that.setGrabberTranslateVisible(false); + that.setGrabberRotateVisible(false); + that.setGrabberStretchXVisible(direction === TRANSLATE_DIRECTION.X); + that.setGrabberStretchYVisible(direction === TRANSLATE_DIRECTION.Y); + that.setGrabberStretchZVisible(direction === TRANSLATE_DIRECTION.Z); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt) { + duplicatedEntityIDs = []; + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + if (!properties.locked) { + var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties + }); + } + } + } else { + duplicatedEntityIDs = null; + } + }, + onEnd: function(event, reason) { + Overlays.editOverlay(stretchPanel, { visible:false }); + pushCommandForSelections(duplicatedEntityIDs); + }, + onMove: function(event) { + pickRay = generalComputePickRay(event.x, event.y); + + // translate mode left/right based on view toward entity + var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + var vector = Vec3.subtract(newIntersection, lastPick); + + if (direction === TRANSLATE_DIRECTION.X) { + projectionVector = { x:1, y:0, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Y) { + projectionVector = { x:0, y:1, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Z) { + projectionVector = { x:0, y:0, z:1 }; + } + + var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); + + var dotVector = Vec3.dot(vector, projectionVector); + vector = Vec3.multiply(dotVector, projectionVector); + vector = grid.snapToGrid(vector); + + var wantDebug = false; + if (wantDebug) { + print("translateUpDown... "); + print(" event.y:" + event.y); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + // Vec3.print(" newPosition:", newPosition); + } + + for (var i = 0; i < SelectionManager.selections.length; i++) { + var id = SelectionManager.selections[i]; + var properties = SelectionManager.savedProperties[id]; + var newPosition = Vec3.sum(properties.position, vector); + var difference = Vec3.subtract(newPosition, SelectionManager.worldPosition); + var halfDifference = Vec3.multiply(difference, 0.5); + var quarterDifference = Vec3.multiply(halfDifference, 0.5); + var newDimensions = properties.dimensions; + var actualNewPosition = properties.position; + + if (direction == TRANSLATE_DIRECTION.X) { + newDimensions.x += halfDifference.x; + actualNewPosition.x += quarterDifference.x; + } else if (direction == TRANSLATE_DIRECTION.Y) { + newDimensions.y += halfDifference.y; + actualNewPosition.y += quarterDifference.y; + } else if (direction == TRANSLATE_DIRECTION.Z) { + newDimensions.z += halfDifference.z; + actualNewPosition.z += quarterDifference.z; + } + + Entities.editEntity(id, { + position: actualNewPosition, dimensions: newDimensions }); } - + + SelectionManager._update(); + } + }); + } + + function addGrabberScaleTool(overlay, mode, direction) { + var pickNormal = null; + var lastPick = null; + var selectedGrabber = null; + addGrabberTool(overlay, { + mode: mode, + onBegin: function(event, pickRay, pickResult) { + pickNormal = { x:-1, y:1, z:-1 }; + + if (direction === SCALE_DIRECTION.LBN) { + selectedGrabber = grabberScaleLBNCube; + } else if (direction === SCALE_DIRECTION.RBN) { + selectedGrabber = grabberScaleRBNCube; + } else if (direction === SCALE_DIRECTION.LBF) { + selectedGrabber = grabberScaleLBFCube; + } else if (direction === SCALE_DIRECTION.RBF) { + selectedGrabber = grabberScaleRBFCube; + } else if (direction === SCALE_DIRECTION.LTN) { + selectedGrabber = grabberScaleLTNCube; + } else if (direction === SCALE_DIRECTION.RTN) { + selectedGrabber = grabberScaleRTNCube; + } else if (direction === SCALE_DIRECTION.LTF) { + selectedGrabber = grabberScaleLTFCube; + } else if (direction === SCALE_DIRECTION.RTF) { + selectedGrabber = grabberScaleRTFCube; + } + Overlays.editOverlay(selectedGrabber, { color: GRABBER_SCALE_CUBE_SELECTED_COLOR }); + + lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + + SelectionManager.saveProperties(); + + that.setGrabberTranslateVisible(false); + that.setGrabberRotateVisible(false); + that.setGrabberStretchVisible(false); + that.setGrabberScaleVisible(true); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt) { + duplicatedEntityIDs = []; + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + if (!properties.locked) { + var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties + }); + } + } + } else { + duplicatedEntityIDs = null; + } + }, + onEnd: function(event, reason) { + Overlays.editOverlay(selectedGrabber, { color: GRABBER_SCALE_CUBE_IDLE_COLOR }); + pushCommandForSelections(duplicatedEntityIDs); + }, + onMove: function(event) { + pickRay = generalComputePickRay(event.x, event.y); + + // translate mode left/right based on view toward entity + var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + var vector = Vec3.subtract(newIntersection, lastPick); + + var projectionVector; + if (direction === SCALE_DIRECTION.LBN) { + projectionVector = { x:-1, y:-1, z:-1 }; + } else if (direction === SCALE_DIRECTION.RBN) { + projectionVector = { x:-1, y:-1, z:1 }; + } else if (direction === SCALE_DIRECTION.LBF) { + projectionVector = { x:1, y:-1, z:-1 }; + } else if (direction === SCALE_DIRECTION.RBF) { + projectionVector = { x:1, y:-1, z:1 }; + } else if (direction === SCALE_DIRECTION.LTN) { + projectionVector = { x:-1, y:1, z:-1 }; + } else if (direction === SCALE_DIRECTION.RTN) { + projectionVector = { x:-1, y:1, z:1 }; + } else if (direction === SCALE_DIRECTION.LTF) { + projectionVector = { x:1, y:1, z:-1 }; + } else if (direction === SCALE_DIRECTION.RTF) { + projectionVector = { x:1, y:1, z:1 }; + } + + var dotVector = Vec3.dot(vector, projectionVector); + vector = Vec3.multiply(dotVector, projectionVector); + vector = grid.snapToGrid(vector); var wantDebug = false; if (wantDebug) { - print(stretchMode); - // Vec3.print(" newIntersection:", newIntersection); + print("translateUpDown... "); + print(" event.y:" + event.y); + Vec3.print(" newIntersection:", newIntersection); Vec3.print(" vector:", vector); - // Vec3.print(" oldPOS:", oldPOS); - // Vec3.print(" newPOS:", newPOS); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); + // Vec3.print(" newPosition:", newPosition); } + + for (var i = 0; i < SelectionManager.selections.length; i++) { + var id = SelectionManager.selections[i]; + var properties = SelectionManager.savedProperties[id]; + var newPosition = Vec3.sum(properties.position, vector); + var difference = Vec3.subtract(newPosition, SelectionManager.worldPosition); + var differenceAvg = (difference.x + difference.y + difference.z) / 3; + var newDimensionsX = properties.dimensions.x + differenceAvg; + var newDimensionsY = properties.dimensions.y + differenceAvg; + var newDimensionsZ = properties.dimensions.z + differenceAvg; + if (newDimensionsX < SCALE_MINIMUM_DIMENSION) { + var differenceBelow = SCALE_MINIMUM_DIMENSION - newDimensionsX; + newDimensionsX += differenceBelow; + newDimensionsY += differenceBelow; + newDimensionsZ += differenceBelow; + } + if (newDimensionsY < SCALE_MINIMUM_DIMENSION) { + var differenceBelow = SCALE_MINIMUM_DIMENSION - newDimensionsY; + newDimensionsX += differenceBelow; + newDimensionsY += differenceBelow; + newDimensionsZ += differenceBelow; + } + if (newDimensionsZ < SCALE_MINIMUM_DIMENSION) { + var differenceBelow = SCALE_MINIMUM_DIMENSION - newDimensionsZ; + newDimensionsX += differenceBelow; + newDimensionsY += differenceBelow; + newDimensionsZ += differenceBelow; + } + Entities.editEntity(id, { dimensions: { x:newDimensionsX, y:newDimensionsY, z:newDimensionsZ }}); + } + + SelectionManager._update(); } - - SelectionManager._update(); - };// End of onMove def - - return { - mode: stretchMode, - onBegin: onBegin, - onMove: onMove, - onEnd: onEnd - }; - }; - - // Direction for the stretch tool when using hand controller - var directionsFor3DGrab = { - LBN: { - x: 1, - y: 1, - z: 1 - }, - RBN: { - x: -1, - y: 1, - z: 1 - }, - LBF: { - x: 1, - y: 1, - z: -1 - }, - RBF: { - x: -1, - y: 1, - z: -1 - }, - LTN: { - x: 1, - y: -1, - z: 1 - }, - RTN: { - x: -1, - y: -1, - z: 1 - }, - LTF: { - x: 1, - y: -1, - z: -1 - }, - RTF: { - x: -1, - y: -1, - z: -1 - } - }; - - // FUNCTION: GET DIRECTION FOR 3D STRETCH - // Returns a vector with directions for the stretch tool in 3D using hand controllers - function getDirectionsFor3DStretch(mode) { - if (mode === "STRETCH_LBN") { - return directionsFor3DGrab.LBN; - } else if (mode === "STRETCH_RBN") { - return directionsFor3DGrab.RBN; - } else if (mode === "STRETCH_LBF") { - return directionsFor3DGrab.LBF; - } else if (mode === "STRETCH_RBF") { - return directionsFor3DGrab.RBF; - } else if (mode === "STRETCH_LTN") { - return directionsFor3DGrab.LTN; - } else if (mode === "STRETCH_RTN") { - return directionsFor3DGrab.RTN; - } else if (mode === "STRETCH_LTF") { - return directionsFor3DGrab.LTF; - } else if (mode === "STRETCH_RTF") { - return directionsFor3DGrab.RTF; - } else { - return null; - } - } - - - // FUNCTION: ADD STRETCH TOOL - function addStretchTool(overlay, mode, pivot, direction, offset, handleMove) { - if (!pivot) { - pivot = direction; - } - var tool = makeStretchTool(mode, direction, pivot, offset, handleMove); - - return addGrabberTool(overlay, tool); - } - - // FUNCTION: CUTOFF STRETCH FUNC - function cutoffStretchFunc(vector, change) { - vector = change; - var wantDebug = false; - if (wantDebug) { - Vec3.print("Radius stretch: ", vector); - } - var length = vector.x + vector.y + vector.z; - var props = SelectionManager.savedProperties[SelectionManager.selections[0]]; - - var radius = props.dimensions.z / 2; - var originalCutoff = props.cutoff; - - var originalSize = radius * Math.tan(originalCutoff * (Math.PI / 180)); - var newSize = originalSize + length; - var cutoff = Math.atan2(newSize, radius) * 180 / Math.PI; - - Entities.editEntity(SelectionManager.selections[0], { - cutoff: cutoff }); - - SelectionManager._update(); } - // FUNCTION: RADIUS STRETCH FUNC - function radiusStretchFunc(vector, change) { - var props = SelectionManager.savedProperties[SelectionManager.selections[0]]; - - // Find the axis being adjusted - var size; - if (Math.abs(change.x) > 0) { - size = props.dimensions.x + change.x; - } else if (Math.abs(change.y) > 0) { - size = props.dimensions.y + change.y; - } else if (Math.abs(change.z) > 0) { - size = props.dimensions.z + change.z; - } - - var newDimensions = { - x: size, - y: size, - z: size - }; - - Entities.editEntity(SelectionManager.selections[0], { - dimensions: newDimensions - }); - - SelectionManager._update(); - } - - // STRETCH TOOL DEF SECTION - addStretchTool(grabberNEAR, "STRETCH_NEAR", { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: -1 - }); - addStretchTool(grabberFAR, "STRETCH_FAR", { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: 0, - z: 1 - }); - addStretchTool(grabberTOP, "STRETCH_TOP", { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }); - addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }); - addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { - x: -1, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }); - addStretchTool(grabberLEFT, "STRETCH_LEFT", { - x: 1, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }); - - addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: -1 - }); - addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, cutoffStretchFunc); - addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, cutoffStretchFunc); - addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { - x: 0, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, cutoffStretchFunc); - addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { - x: 0, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, cutoffStretchFunc); - - addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { - x: 0, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { - x: 0, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: -1 - }, radiusStretchFunc); - - addStretchTool(grabberLBN, "STRETCH_LBN", null, { - x: 1, - y: 0, - z: 1 - }, { - x: -1, - y: -1, - z: -1 - }); - addStretchTool(grabberRBN, "STRETCH_RBN", null, { - x: -1, - y: 0, - z: 1 - }, { - x: 1, - y: -1, - z: -1 - }); - addStretchTool(grabberLBF, "STRETCH_LBF", null, { - x: 1, - y: 0, - z: -1 - }, { - x: -1, - y: -1, - z: 1 - }); - addStretchTool(grabberRBF, "STRETCH_RBF", null, { - x: -1, - y: 0, - z: -1 - }, { - x: 1, - y: -1, - z: 1 - }); - addStretchTool(grabberLTN, "STRETCH_LTN", null, { - x: 1, - y: 0, - z: 1 - }, { - x: -1, - y: 1, - z: -1 - }); - addStretchTool(grabberRTN, "STRETCH_RTN", null, { - x: -1, - y: 0, - z: 1 - }, { - x: 1, - y: 1, - z: -1 - }); - addStretchTool(grabberLTF, "STRETCH_LTF", null, { - x: 1, - y: 0, - z: -1 - }, { - x: -1, - y: 1, - z: 1 - }); - addStretchTool(grabberRTF, "STRETCH_RTF", null, { - x: -1, - y: 0, - z: -1 - }, { - x: 1, - y: 1, - z: 1 - }); - - addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, { - x: 1, - y: 1, - z: 0 - }, { - x: 1, - y: 1, - z: 0 - }); - addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, { - x: -1, - y: 1, - z: 0 - }, { - x: -1, - y: 1, - z: 0 - }); - addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, { - x: 0, - y: 1, - z: -1 - }, { - x: 0, - y: 1, - z: -1 - }); - addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, { - x: 0, - y: 1, - z: 1 - }, { - x: 0, - y: 1, - z: 1 - }); - addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, { - x: -1, - y: 0, - z: 0 - }, { - x: 1, - y: -1, - z: 0 - }); - addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, { - x: 1, - y: 0, - z: 0 - }, { - x: -1, - y: -1, - z: 0 - }); - addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: -1, - z: -1 - }); - addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: -1, - z: 1 - }); - addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, { - x: -1, - y: 0, - z: 1 - }, { - x: 1, - y: 0, - z: -1 - }); - addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, { - x: 1, - y: 0, - z: 1 - }, { - x: -1, - y: 0, - z: -1 - }); - addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, { - x: -1, - y: 0, - z: -1 - }, { - x: 1, - y: 0, - z: 1 - }); - addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, { - x: 1, - y: 0, - z: -1 - }, { - x: -1, - y: 0, - z: 1 - }); - // FUNCTION: UPDATE ROTATION DEGREES OVERLAY - function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { - var wantDebug = false; - if (wantDebug) { - print("---> updateRotationDegreesOverlay ---"); - print(" AngleFromZero: " + angleFromZero); - print(" HandleRotation - X: " + handleRotation.x + " Y: " + handleRotation.y + " Z: " + handleRotation.z); - print(" CenterPos - " + centerPosition.x + " Y: " + centerPosition.y + " Z: " + centerPosition.z); - } - + function updateRotationDegreesOverlay(angleFromZero, direction, centerPosition) { var angle = angleFromZero * (Math.PI / 180); var position = { - x: Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - y: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + x: Math.cos(angle) * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + y: Math.sin(angle) * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, z: 0 }; - if (wantDebug) { - print(" Angle: " + angle); - print(" InitialPos: " + position.x + ", " + position.y + ", " + position.z); - } - - position = Vec3.multiplyQbyV(handleRotation, position); + if (direction === ROTATE_DIRECTION.PITCH) + position = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -90, 0), position); + else if (direction === ROTATE_DIRECTION.YAW) + position = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(90, 0, 0), position); + else if (direction === ROTATE_DIRECTION.ROLL) + position = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 180, 0), position); position = Vec3.sum(centerPosition, position); var overlayProps = { position: position, dimensions: { - x: innerRadius * ROTATION_DISPLAY_SIZE_X_MULTIPLIER, - y: innerRadius * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER + x: ROTATION_DISPLAY_SIZE_X_MULTIPLIER, + y: ROTATION_DISPLAY_SIZE_Y_MULTIPLIER }, - lineHeight: innerRadius * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, + lineHeight: ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, text: normalizeDegrees(-angleFromZero) + "°" }; - if (wantDebug) { - print(" TranslatedPos: " + position.x + ", " + position.y + ", " + position.z); - print(" OverlayDim - X: " + overlayProps.dimensions.x + " Y: " + overlayProps.dimensions.y + " Z: " + overlayProps.dimensions.z); - print(" OverlayLineHeight: " + overlayProps.lineHeight); - print(" OverlayText: " + overlayProps.text); - } - Overlays.editOverlay(rotationDegreesDisplay, overlayProps); - if (wantDebug) { - print("<--- updateRotationDegreesOverlay ---"); - } } // FUNCTION DEF: updateSelectionsRotation @@ -3322,260 +979,422 @@ SelectionDisplay = (function() { } } - function helperRotationHandleOnBegin(event, pickRay, rotAroundAxis, rotCenter, handleRotation) { + function addGrabberRotateTool(overlay, mode, direction) { + var initialPosition = SelectionManager.worldPosition; + var selectedGrabber = null; + addGrabberTool(overlay, { + mode: mode, + onBegin: function(event, pickRay, pickResult) { + SelectionManager.saveProperties(); + + that.setGrabberTranslateVisible(false); + that.setGrabberRotatePitchVisible(direction === ROTATE_DIRECTION.PITCH); + that.setGrabberRotateYawVisible(direction === ROTATE_DIRECTION.YAW); + that.setGrabberRotateRollVisible(direction === ROTATE_DIRECTION.ROLL); + that.setGrabberStretchVisible(false); + that.setGrabberScaleVisible(false); + + initialPosition = SelectionManager.worldPosition; + + if (direction === ROTATE_DIRECTION.PITCH) { + rotationNormal = { x: 1, y: 0, z: 0 }; + selectedGrabber = grabberRotatePitchRing; + } else if (direction === ROTATE_DIRECTION.YAW) { + rotationNormal = { x: 0, y: 1, z: 0 }; + selectedGrabber = grabberRotateYawRing; + } else if (direction === ROTATE_DIRECTION.ROLL) { + rotationNormal = { x: 0, y: 0, z: 1 }; + selectedGrabber = grabberRotateRollRing; + } + + Overlays.editOverlay(selectedGrabber, { hasTickMarks: true }); + + var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + rotationNormal = Vec3.multiplyQbyV(rotation, rotationNormal); + + var rotCenter = SelectionManager.worldPosition; + + Overlays.editOverlay(rotationDegreesDisplay, { visible: true }); + Overlays.editOverlay(grabberRotateCurrentRing, { visible: true }); + updateRotationDegreesOverlay(0, direction, rotCenter); + + // editOverlays may not have committed rotation changes. + // Compute zero position based on where the overlay will be eventually. + var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); + // In case of a parallel ray, this will be null, which will cause early-out + // in the onMove helper. + rotZero = result; + }, + onEnd: function(event, reason) { + Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(selectedGrabber, { hasTickMarks: false }); + Overlays.editOverlay(grabberRotateCurrentRing, { visible: false }); + pushCommandForSelections(); + }, + onMove: function(event) { + if (!rotZero) { + print("ERROR: entitySelectionTool.handleRotationHandleOnMove - Invalid RotationZero Specified (missed rotation target plane?)"); + + // EARLY EXIT + return; + } + var pickRay = generalComputePickRay(event.x, event.y); + var rotCenter = SelectionManager.worldPosition; + var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); + if (result) { + var centerToZero = Vec3.subtract(rotZero, rotCenter); + var centerToIntersect = Vec3.subtract(result, rotCenter); + // Note: orientedAngle which wants normalized centerToZero and centerToIntersect + // handles that internally, so it's to pass unnormalized vectors here. + var angleFromZero = Math.floor((Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal))); + var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); + updateSelectionsRotation(rotChange); + updateRotationDegreesOverlay(-angleFromZero, direction, rotCenter); + + var worldRotation; + if (direction === ROTATE_DIRECTION.PITCH) { + worldRotation = worldRotationY; + } else if (direction === ROTATE_DIRECTION.YAW) { + worldRotation = worldRotationZ; + } else if (direction === ROTATE_DIRECTION.ROLL) { + worldRotation = worldRotationX; + } + + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + } + Overlays.editOverlay(grabberRotateCurrentRing, { + position: rotCenter, + rotation: worldRotation, + startAt: startAtCurrent, + endAt: endAtCurrent + }); + } + } + }); + } + + // @param: toolHandle: The overlayID associated with the tool + // that correlates to the tool you wish to query. + // @note: If toolHandle is null or undefined then activeTool + // will be checked against those values as opposed to + // the tool registered under toolHandle. Null & Undefined + // are treated as separate values. + // @return: bool - Indicates if the activeTool is that queried. + function isActiveTool(toolHandle) { + if (!toolHandle) { + // Allow isActiveTool(null) and similar to return true if there's + // no active tool + return (activeTool === toolHandle); + } + + if (!grabberTools.hasOwnProperty(toolHandle)) { + print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + toolHandle + ". Tools should be registered via addGrabberTool."); + // EARLY EXIT + return false; + } + + return (activeTool === grabberTools[ toolHandle ]); + } + + // @return string - The mode of the currently active tool; + // otherwise, "UNKNOWN" if there's no active tool. + function getMode() { + return (activeTool ? activeTool.mode : "UNKNOWN"); + } + + that.cleanup = function() { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.deleteOverlay(allOverlays[i]); + } + }; + + that.highlightSelectable = function(entityID) { + var properties = Entities.getEntityProperties(entityID); + }; + + that.unhighlightSelectable = function(entityID) { + }; + + that.select = function(entityID, event) { + var properties = Entities.getEntityProperties(SelectionManager.selections[0]); + + lastCameraPosition = Camera.getPosition(); + lastCameraOrientation = Camera.getOrientation(); + + if (event !== false) { + pickRay = generalComputePickRay(event.x, event.y); + + var wantDebug = false; + if (wantDebug) { + print("select() with EVENT...... "); + print(" event.y:" + event.y); + Vec3.print(" current position:", properties.position); + } + } + + /* + Overlays.editOverlay(highlightBox, { + visible: false + }); + */ + + that.updateHandles(); + }; + + // FUNCTION: UPDATE HANDLE POSITION ROTATION + that.updateHandlePositionRotation = function() { + if (SelectionManager.hasSelection()) { + var worldPosition = SelectionManager.worldPosition; + var worldRotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + + var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); + worldRotationX = Quat.multiply(worldRotation, localRotationX); + var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); + worldRotationY = Quat.multiply(worldRotation, localRotationY); + var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); + worldRotationZ = Quat.multiply(worldRotation, localRotationZ); + + var cylinderXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET, y:0, z:0 })); + Overlays.editOverlay(grabberTranslateXCylinder, { position: cylinderXPos, rotation:worldRotationX }); + var coneXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_TRANSLATE_ARROW_CONE_OFFSET, y:0, z:0 })); + Overlays.editOverlay(grabberTranslateXCone, { position: coneXPos, rotation:worldRotationX }); + var stretchXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_STRETCH_SPHERE_OFFSET, y:0, z:0 })); + Overlays.editOverlay(grabberStretchXSphere, { position: stretchXPos }); + Overlays.editOverlay(grabberStretchXPanel, { position: stretchXPos, rotation:worldRotationY }); + + var cylinderYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET, z:0 })); + Overlays.editOverlay(grabberTranslateYCylinder, { position: cylinderYPos, rotation:worldRotationY }); + var coneYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CONE_OFFSET, z:0 })); + Overlays.editOverlay(grabberTranslateYCone, { position: coneYPos, rotation:worldRotationY }); + var stretchYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_STRETCH_SPHERE_OFFSET, z:0 })); + Overlays.editOverlay(grabberStretchYSphere, { position: stretchYPos }); + Overlays.editOverlay(grabberStretchYPanel, { position: stretchYPos, rotation:worldRotationZ }); + + var cylinderZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET })); + Overlays.editOverlay(grabberTranslateZCylinder, { position: cylinderZPos, rotation:worldRotationZ }); + var coneZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CONE_OFFSET })); + Overlays.editOverlay(grabberTranslateZCone, { position: coneZPos, rotation:worldRotationZ }); + var stretchZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_STRETCH_SPHERE_OFFSET })); + Overlays.editOverlay(grabberStretchZSphere, { position: stretchZPos }); + Overlays.editOverlay(grabberStretchZPanel, { position: stretchZPos, rotation:worldRotationX }); + + if (!isActiveTool(grabberRotatePitchRing)) { + Overlays.editOverlay(grabberRotatePitchRing, { position: SelectionManager.worldPosition, rotation: worldRotationY }); + } + if (!isActiveTool(grabberRotateYawRing)) { + Overlays.editOverlay(grabberRotateYawRing, { position: SelectionManager.worldPosition, rotation: worldRotationZ }); + } + if (!isActiveTool(grabberRotateRollRing)) { + Overlays.editOverlay(grabberRotateRollRing, { position: SelectionManager.worldPosition, rotation: worldRotationX }); + } + + var grabberScaleLBNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleLBNCube, { position:grabberScaleLBNCubePos }); + var grabberScaleRBNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleRBNCube, { position:grabberScaleRBNCubePos }); + var grabberScaleLBFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleLBFCube, { position:grabberScaleLBFCubePos }); + var grabberScaleRBFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleRBFCube, { position:grabberScaleRBFCubePos }); + var grabberScaleLTNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleLTNCube, { position:grabberScaleLTNCubePos }); + var grabberScaleRTNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleRTNCube, { position:grabberScaleRTNCubePos }); + var grabberScaleLTFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleLTFCube, { position:grabberScaleLTFCubePos }); + var grabberScaleRTFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); + Overlays.editOverlay(grabberScaleRTFCube, { position:grabberScaleRTFCubePos }); + + Overlays.editOverlay(grabberScaleTREdge, { start: grabberScaleRTNCubePos, end:grabberScaleRTFCubePos }); + Overlays.editOverlay(grabberScaleTLEdge, { start: grabberScaleLTNCubePos, end:grabberScaleLTFCubePos }); + Overlays.editOverlay(grabberScaleTFEdge, { start: grabberScaleLTFCubePos, end:grabberScaleRTFCubePos }); + Overlays.editOverlay(grabberScaleTNEdge, { start: grabberScaleLTNCubePos, end:grabberScaleRTNCubePos }); + Overlays.editOverlay(grabberScaleBREdge, { start: grabberScaleRBNCubePos, end:grabberScaleRBFCubePos }); + Overlays.editOverlay(grabberScaleBLEdge, { start: grabberScaleLBNCubePos, end:grabberScaleLBFCubePos }); + Overlays.editOverlay(grabberScaleBFEdge, { start: grabberScaleLBFCubePos, end:grabberScaleRBFCubePos }); + Overlays.editOverlay(grabberScaleBNEdge, { start: grabberScaleLBNCubePos, end:grabberScaleRBNCubePos }); + Overlays.editOverlay(grabberScaleNREdge, { start: grabberScaleRTNCubePos, end:grabberScaleRBNCubePos }); + Overlays.editOverlay(grabberScaleNLEdge, { start: grabberScaleLTNCubePos, end:grabberScaleLBNCubePos }); + Overlays.editOverlay(grabberScaleFREdge, { start: grabberScaleRTFCubePos, end:grabberScaleRBFCubePos }); + Overlays.editOverlay(grabberScaleFLEdge, { start: grabberScaleLTFCubePos, end:grabberScaleLBFCubePos }); + } + }; + Script.update.connect(that.updateHandlePositionRotation); + + // FUNCTION: SET SPACE MODE + that.setSpaceMode = function(newSpaceMode) { var wantDebug = false; if (wantDebug) { - print("================== " + getMode() + "(rotation helper onBegin) -> ======================="); + print("======> SetSpaceMode called. ========"); } - SelectionManager.saveProperties(); - that.setRotationHandlesVisible(false); - that.setStretchHandlesVisible(false); - that.setGrabberMoveUpVisible(false); - - initialPosition = SelectionManager.worldPosition; - rotationNormal = { x: 0, y: 0, z: 0 }; - rotationNormal[rotAroundAxis] = 1; - //get the correct axis according to the avatar referencial - var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ - x: 0, - y: 0, - z: 0 - })); - rotationNormal = Vec3.multiplyQbyV(avatarReferential, rotationNormal); - - // Size the overlays to the current selection size - var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; - var halfDimensions = Vec3.multiply(SelectionManager.worldDimensions, 0.5); - innerRadius = diagonal; - outerRadius = diagonal * 1.15; - var innerAlpha = 0.2; - var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, { - visible: true, - rotation: handleRotation, - position: rotCenter, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); - - Overlays.editOverlay(rotateOverlayOuter, { - visible: true, - rotation: handleRotation, - position: rotCenter, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha - }); - - Overlays.editOverlay(rotateOverlayCurrent, { - visible: true, - rotation: handleRotation, - position: rotCenter, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9 - }); - - Overlays.editOverlay(rotationDegreesDisplay, { - visible: true - }); - - updateRotationDegreesOverlay(0, handleRotation, rotCenter); - - // editOverlays may not have committed rotation changes. - // Compute zero position based on where the overlay will be eventually. - var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); - // In case of a parallel ray, this will be null, which will cause early-out - // in the onMove helper. - rotZero = result; - + if (spaceMode !== newSpaceMode) { + if (wantDebug) { + print(" Updating SpaceMode From: " + spaceMode + " To: " + newSpaceMode); + } + spaceMode = newSpaceMode; + that.updateHandles(); + } else if (wantDebug) { + print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + spaceMode + " DesiredMode: " + newSpaceMode); + } if (wantDebug) { - print("================== " + getMode() + "(rotation helper onBegin) <- ======================="); + print("====== SetSpaceMode called. <========"); } - }// End_Function(helperRotationHandleOnBegin) + }; - function helperRotationHandleOnMove(event, rotAroundAxis, rotCenter, handleRotation) { - - if (!rotZero) { - print("ERROR: entitySelectionTool.handleRotationHandleOnMove - Invalid RotationZero Specified (missed rotation target plane?)"); - - // EARLY EXIT + // FUNCTION: UPDATE HANDLES + that.updateHandles = function() { + var wantDebug = false; + if (wantDebug) { + print("======> Update Handles ======="); + print(" Selections Count: " + SelectionManager.selections.length); + print(" SpaceMode: " + spaceMode); + print(" DisplayMode: " + getMode()); + } + if (SelectionManager.selections.length === 0) { + that.setOverlaysVisible(false); return; } - var wantDebug = false; - if (wantDebug) { - print("================== "+ getMode() + "(rotation helper onMove) -> ======================="); - Vec3.print(" rotZero: ", rotZero); - } - var pickRay = generalComputePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { - visible: false - }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { - visible: false - }); + var isSingleSelection = (SelectionManager.selections.length === 1); + if (isSingleSelection) { + }// end of isSingleSelection - var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); - if (result) { - var centerToZero = Vec3.subtract(rotZero, rotCenter); - var centerToIntersect = Vec3.subtract(result, rotCenter); - if (wantDebug) { - Vec3.print(" RotationNormal: ", rotationNormal); - Vec3.print(" rotZero: ", rotZero); - Vec3.print(" rotCenter: ", rotCenter); - Vec3.print(" intersect: ", result); - Vec3.print(" centerToZero: ", centerToZero); - Vec3.print(" centerToIntersect: ", centerToIntersect); - } - // Note: orientedAngle which wants normalized centerToZero and centerToIntersect - // handles that internally, so it's to pass unnormalized vectors here. - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); - - var distanceFromCenter = Vec3.length(centerToIntersect); - var snapToInner = distanceFromCenter < innerRadius; - var snapAngle = snapToInner ? innerSnapAngle : 1.0; - angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - - - var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); - updateSelectionsRotation(rotChange); - //present angle in avatar referencial - angleFromZero = -angleFromZero; - updateRotationDegreesOverlay(angleFromZero, handleRotation, rotCenter); - - // update the rotation display accordingly... - var startAtCurrent = 0; - var endAtCurrent = angleFromZero; - var startAtRemainder = angleFromZero; - var endAtRemainder = 360; - if (angleFromZero < 0) { - startAtCurrent = 360 + angleFromZero; - endAtCurrent = 360; - startAtRemainder = 0; - endAtRemainder = startAtCurrent; - } - if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { - startAt: 0, - endAt: 360 - }); - Overlays.editOverlay(rotateOverlayInner, { - startAt: startAtRemainder, - endAt: endAtRemainder - }); - Overlays.editOverlay(rotateOverlayCurrent, { - startAt: startAtCurrent, - endAt: endAtCurrent, - size: innerRadius, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0 - }); - } else { - Overlays.editOverlay(rotateOverlayInner, { - startAt: 0, - endAt: 360 - }); - Overlays.editOverlay(rotateOverlayOuter, { - startAt: startAtRemainder, - endAt: endAtRemainder - }); - Overlays.editOverlay(rotateOverlayCurrent, { - startAt: startAtCurrent, - endAt: endAtCurrent, - size: outerRadius, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1 - }); - } - }// End_If(results.intersects) + that.setGrabberTranslateXVisible(!activeTool || isActiveTool(grabberTranslateXCone) || isActiveTool(grabberTranslateXCylinder)); + that.setGrabberTranslateYVisible(!activeTool || isActiveTool(grabberTranslateYCone) || isActiveTool(grabberTranslateYCylinder)); + that.setGrabberTranslateZVisible(!activeTool || isActiveTool(grabberTranslateZCone) || isActiveTool(grabberTranslateZCylinder)); + that.setGrabberRotatePitchVisible(!activeTool || isActiveTool(grabberRotatePitchRing)); + that.setGrabberRotateYawVisible(!activeTool || isActiveTool(grabberRotateYawRing)); + that.setGrabberRotateRollVisible(!activeTool || isActiveTool(grabberRotateRollRing)); + that.setGrabberStretchXVisible(!activeTool || isActiveTool(grabberStretchXSphere)); + that.setGrabberStretchYVisible(!activeTool || isActiveTool(grabberStretchYSphere)); + that.setGrabberStretchZVisible(!activeTool || isActiveTool(grabberStretchZSphere)); + that.setGrabberScaleVisible(!activeTool || isActiveTool(grabberScaleLBNCube) || isActiveTool(grabberScaleRBNCube) || isActiveTool(grabberScaleLBFCube) || isActiveTool(grabberScaleRBFCube) + || isActiveTool(grabberScaleLTNCube) || isActiveTool(grabberScaleRTNCube) || isActiveTool(grabberScaleLTFCube) || isActiveTool(grabberScaleRTFCube)); if (wantDebug) { - print("================== "+ getMode() + "(rotation helper onMove) <- ======================="); + print("====== Update Handles <======="); } - }// End_Function(helperRotationHandleOnMove) + }; - function helperRotationHandleOnEnd() { - var wantDebug = false; - if (wantDebug) { - print("================== " + getMode() + "(onEnd) -> ======================="); + // FUNCTION: SET OVERLAYS VISIBLE + that.setOverlaysVisible = function(isVisible) { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.editOverlay(allOverlays[i], { visible: isVisible }); } - Overlays.editOverlay(rotateOverlayInner, { - visible: false - }); - Overlays.editOverlay(rotateOverlayOuter, { - visible: false - }); - Overlays.editOverlay(rotateOverlayCurrent, { - visible: false - }); - Overlays.editOverlay(rotationDegreesDisplay, { - visible: false - }); + }; - pushCommandForSelections(); + // FUNCTION: SET GRABBER TRANSLATE VISIBLE + that.setGrabberTranslateVisible = function(isVisible) { + that.setGrabberTranslateXVisible(isVisible); + that.setGrabberTranslateYVisible(isVisible); + that.setGrabberTranslateZVisible(isVisible); + }; - if (wantDebug) { - print("================== " + getMode() + "(onEnd) <- ======================="); - } - }// End_Function(helperRotationHandleOnEnd) + that.setGrabberTranslateXVisible = function(isVisible) { + Overlays.editOverlay(grabberTranslateXCone, { visible: isVisible }); + Overlays.editOverlay(grabberTranslateXCylinder, { visible: isVisible }); + }; + that.setGrabberTranslateYVisible = function(isVisible) { + Overlays.editOverlay(grabberTranslateYCone, { visible: isVisible }); + Overlays.editOverlay(grabberTranslateYCylinder, { visible: isVisible }); + }; - // YAW GRABBER TOOL DEFINITION - var initialPosition = SelectionManager.worldPosition; - addGrabberTool(yawHandle, { - mode: "ROTATE_YAW", - onBegin: function(event, pickRay, pickResult) { - helperRotationHandleOnBegin(event, pickRay, "y", yawCenter, yawHandleRotation); - }, - onEnd: function(event, reason) { - helperRotationHandleOnEnd(); - }, - onMove: function(event) { - helperRotationHandleOnMove(event, "y", yawCenter, yawHandleRotation); - } - }); + that.setGrabberTranslateZVisible = function(isVisible) { + Overlays.editOverlay(grabberTranslateZCone, { visible: isVisible }); + Overlays.editOverlay(grabberTranslateZCylinder, { visible: isVisible }); + }; + // FUNCTION: SET GRABBER ROTATION VISIBLE + that.setGrabberRotateVisible = function(isVisible) { + that.setGrabberRotatePitchVisible(isVisible); + that.setGrabberRotateYawVisible(isVisible); + that.setGrabberRotateRollVisible(isVisible); + }; - // PITCH GRABBER TOOL DEFINITION - addGrabberTool(pitchHandle, { - mode: "ROTATE_PITCH", - onBegin: function(event, pickRay, pickResult) { - helperRotationHandleOnBegin(event, pickRay, "x", pitchCenter, pitchHandleRotation); - }, - onEnd: function(event, reason) { - helperRotationHandleOnEnd(); - }, - onMove: function (event) { - helperRotationHandleOnMove(event, "x", pitchCenter, pitchHandleRotation); - } - }); + that.setGrabberRotatePitchVisible = function(isVisible) { + Overlays.editOverlay(grabberRotatePitchRing, { visible: isVisible }); + }; + that.setGrabberRotateYawVisible = function(isVisible) { + Overlays.editOverlay(grabberRotateYawRing, { visible: isVisible }); + }; - // ROLL GRABBER TOOL DEFINITION - addGrabberTool(rollHandle, { - mode: "ROTATE_ROLL", - onBegin: function(event, pickRay, pickResult) { - helperRotationHandleOnBegin(event, pickRay, "z", rollCenter, rollHandleRotation); - }, - onEnd: function (event, reason) { - helperRotationHandleOnEnd(); - }, - onMove: function(event) { - helperRotationHandleOnMove(event, "z", rollCenter, rollHandleRotation); - } - }); + that.setGrabberRotateRollVisible = function(isVisible) { + Overlays.editOverlay(grabberRotateRollRing, { visible: isVisible }); + }; + + // FUNCTION: SET GRABBER STRETCH VISIBLE + that.setGrabberStretchVisible = function(isVisible) { + that.setGrabberStretchXVisible(isVisible); + that.setGrabberStretchYVisible(isVisible); + that.setGrabberStretchZVisible(isVisible); + }; + + that.setGrabberStretchXVisible = function(isVisible) { + Overlays.editOverlay(grabberStretchXSphere, { visible: isVisible }); + }; + + that.setGrabberStretchYVisible = function(isVisible) { + Overlays.editOverlay(grabberStretchYSphere, { visible: isVisible }); + }; + + that.setGrabberStretchZVisible = function(isVisible) { + Overlays.editOverlay(grabberStretchZSphere, { visible: isVisible }); + }; + + // FUNCTION: SET GRABBER SCALE VISIBLE + that.setGrabberScaleVisible = function(isVisible) { + Overlays.editOverlay(grabberScaleLBNCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleRBNCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleLBFCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleRBFCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleLTNCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleRTNCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleLTFCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleRTFCube, { visible: isVisible }); + Overlays.editOverlay(grabberScaleTREdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleTLEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleTFEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleTNEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleBREdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleBLEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleBFEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleBNEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleNREdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleNLEdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleFREdge, { visible: isVisible }); + Overlays.editOverlay(grabberScaleFLEdge, { visible: isVisible }); + }; + + addGrabberTranslateTool(grabberTranslateXCone, "TRANSLATE_X", TRANSLATE_DIRECTION.X); + addGrabberTranslateTool(grabberTranslateXCylinder, "TRANSLATE_X", TRANSLATE_DIRECTION.X); + addGrabberTranslateTool(grabberTranslateYCone, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); + addGrabberTranslateTool(grabberTranslateYCylinder, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); + addGrabberTranslateTool(grabberTranslateZCone, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); + addGrabberTranslateTool(grabberTranslateZCylinder, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); + + addGrabberRotateTool(grabberRotatePitchRing, "ROTATE_PITCH", ROTATE_DIRECTION.PITCH); + addGrabberRotateTool(grabberRotateYawRing, "ROTATE_YAW", ROTATE_DIRECTION.YAW); + addGrabberRotateTool(grabberRotateRollRing, "ROTATE_ROLL", ROTATE_DIRECTION.ROLL); + + addGrabberStretchTool(grabberStretchXSphere, "STRETCH_X", TRANSLATE_DIRECTION.X); + addGrabberStretchTool(grabberStretchYSphere, "STRETCH_Y", TRANSLATE_DIRECTION.Y); + addGrabberStretchTool(grabberStretchZSphere, "STRETCH_Z", TRANSLATE_DIRECTION.Z); + + addGrabberScaleTool(grabberScaleLBNCube, "SCALE_LBN", SCALE_DIRECTION.LBN); + addGrabberScaleTool(grabberScaleRBNCube, "SCALE_RBN", SCALE_DIRECTION.RBN); + addGrabberScaleTool(grabberScaleLBFCube, "SCALE_LBF", SCALE_DIRECTION.LBF); + addGrabberScaleTool(grabberScaleRBFCube, "SCALE_RBF", SCALE_DIRECTION.RBF); + addGrabberScaleTool(grabberScaleLTNCube, "SCALE_LTN", SCALE_DIRECTION.LTN); + addGrabberScaleTool(grabberScaleRTNCube, "SCALE_RTN", SCALE_DIRECTION.RTN); + addGrabberScaleTool(grabberScaleLTFCube, "SCALE_LTF", SCALE_DIRECTION.LTF); + addGrabberScaleTool(grabberScaleRTFCube, "SCALE_RTF", SCALE_DIRECTION.RTF); // FUNCTION: CHECK MOVE that.checkMove = function() { @@ -3587,7 +1406,7 @@ SelectionDisplay = (function() { if (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation)) { - that.updateRotationHandles(); + //that.updateRotationHandles(); } } }; @@ -3606,7 +1425,6 @@ SelectionDisplay = (function() { } }; - // FUNCTION DEF(s): Intersection Check Helpers function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { var wantDebug = false; @@ -3650,7 +1468,7 @@ SelectionDisplay = (function() { var pickRay = generalComputePickRay(event.x, event.y); // TODO_Case6491: Move this out to setup just to make it once - var interactiveOverlays = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, selectionBox]; + var interactiveOverlays = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]; for (var key in grabberTools) { if (grabberTools.hasOwnProperty(key)) { interactiveOverlays.push(key); @@ -3717,125 +1535,6 @@ SelectionDisplay = (function() { return true; } - // if no tool is active, then just look for handles to highlight... - var pickRay = generalComputePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(pickRay); - var pickedColor; - var pickedAlpha; - var highlightNeeded = false; - - if (result.intersects) { - switch (result.overlayID) { - case yawHandle: - case pitchHandle: - case rollHandle: - pickedColor = handleColor; - pickedAlpha = handleAlpha; - highlightNeeded = true; - break; - - case grabberMoveUp: - pickedColor = handleColor; - pickedAlpha = handleAlpha; - highlightNeeded = true; - break; - - case grabberLBN: - case grabberLBF: - case grabberRBN: - case grabberRBF: - case grabberLTN: - case grabberLTF: - case grabberRTN: - case grabberRTF: - pickedColor = grabberColorCorner; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberTOP: - case grabberBOTTOM: - case grabberLEFT: - case grabberRIGHT: - case grabberNEAR: - case grabberFAR: - pickedColor = grabberColorFace; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberEdgeTR: - case grabberEdgeTL: - case grabberEdgeTF: - case grabberEdgeTN: - case grabberEdgeBR: - case grabberEdgeBL: - case grabberEdgeBF: - case grabberEdgeBN: - case grabberEdgeNR: - case grabberEdgeNL: - case grabberEdgeFR: - case grabberEdgeFL: - case grabberSpotLightRadius: - case grabberSpotLightT: - case grabberSpotLightB: - case grabberSpotLightL: - case grabberSpotLightR: - case grabberPointLightT: - case grabberPointLightB: - case grabberPointLightR: - case grabberPointLightL: - case grabberPointLightN: - case grabberPointLightF: - pickedColor = grabberColorEdge; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberCloner: - pickedColor = grabberColorCloner; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - default: - if (previousHandle) { - Overlays.editOverlay(previousHandle, { - color: previousHandleColor, - alpha: previousHandleAlpha - }); - previousHandle = false; - } - break; - } - - if (highlightNeeded) { - if (previousHandle) { - Overlays.editOverlay(previousHandle, { - color: previousHandleColor, - alpha: previousHandleAlpha - }); - previousHandle = false; - } - Overlays.editOverlay(result.overlayID, { - color: highlightedHandleColor, - alpha: highlightedHandleAlpha - }); - previousHandle = result.overlayID; - previousHandleColor = pickedColor; - previousHandleAlpha = pickedAlpha; - } - - } else { - if (previousHandle) { - Overlays.editOverlay(previousHandle, { - color: previousHandleColor, - alpha: previousHandleAlpha - }); - previousHandle = false; - } - } - if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); } @@ -3859,23 +1558,6 @@ SelectionDisplay = (function() { print(" ActiveTool(" + activeTool.mode + ")'s missing onEnd"); } } - - // hide our rotation overlays..., and show our handles - if (isActiveTool(yawHandle) || isActiveTool(pitchHandle) || isActiveTool(rollHandle)) { - if (wantDebug) { - print(" Triggering hide of RotateOverlays"); - } - Overlays.editOverlay(rotateOverlayInner, { - visible: false - }); - Overlays.editOverlay(rotateOverlayOuter, { - visible: false - }); - Overlays.editOverlay(rotateOverlayCurrent, { - visible: false - }); - - } showHandles = activeTool; // base on prior tool value activeTool = null; @@ -3900,7 +1582,5 @@ SelectionDisplay = (function() { // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); - return that; - }()); From 7a9740d25878a8f516329eeefbca7f68f0c30b31 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 18 Jan 2018 02:18:46 -0800 Subject: [PATCH 045/381] visible-perview --- interface/src/Application.cpp | 2 +- interface/src/SecondaryCamera.cpp | 2 +- .../render-utils/src/CauterizedModel.cpp | 2 +- .../render-utils/src/MeshPartPayload.cpp | 5 ++++- libraries/render-utils/src/MeshPartPayload.h | 2 +- .../render-utils/src/RenderShadowTask.cpp | 2 +- libraries/render-utils/src/RenderViewTask.cpp | 4 ++-- libraries/render-utils/src/RenderViewTask.h | 2 +- libraries/render/src/render/Item.h | 19 +++++++++++++------ .../src/render/RenderFetchCullSortTask.cpp | 18 +++++++++--------- .../src/render/RenderFetchCullSortTask.h | 2 +- 11 files changed, 35 insertions(+), 25 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6849bd07b5..e9ea9a0295 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2200,7 +2200,7 @@ void Application::initializeGL() { bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); _renderEngine->addJob("UpdateScene"); _renderEngine->addJob("SecondaryCameraJob", cullFunctor); - _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred); + _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred, render::ItemKey::VISIBLE_MASK_0, render::ItemKey::VISIBLE_MASK_0); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 4cfa4d6156..35eff68ab3 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -20,7 +20,7 @@ using RenderArgsPointer = std::shared_ptr; void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { task.addJob("RenderShadowTask", cullFunctor); - const auto items = task.addJob("FetchCullSort", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::VISIBLE_MASK_1, render::ItemKey::VISIBLE_MASK_1); assert(items.canCast()); if (!isDeferred) { task.addJob("Forward", items); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index e3f26a43d8..09b6ada8e4 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -247,7 +247,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, enableCauterization); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c506887fc4..ead2a767db 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -389,13 +389,16 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render _worldBound.transform(boundTransform); } -void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered) { +void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, bool isCauterized) { ItemKey::Builder builder; builder.withTypeShape(); if (!isVisible) { builder.withInvisible(); } + else if (isCauterized) { + builder.withInvisible(0); // hide these items in the vibility mask #0 + } if (isLayered) { builder.withLayered(); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 8160b9f009..ee515c7b3d 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -103,7 +103,7 @@ public: render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) override; - void setKey(bool isVisible, bool isLayered); + void setKey(bool isVisible, bool isLayered, bool isCauterized = false); void setLayer(bool isLayeredInFront, bool isLayeredInHUD); void setShapeKey(bool invalidateShapeKey, bool isWireframe); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 378b164e67..7a7714e1e0 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -264,7 +264,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit1() = ItemFilter::Builder::visibleWorldItems(0x08).withTypeShape().withOpaque().withoutLayered(); + output.edit1() = ItemFilter::Builder::visibleWorldItems(0x00, 0x00).withTypeShape().withOpaque().withoutLayered(); globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 434e069d28..83868e1443 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -14,7 +14,7 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" -void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask) { +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask, uint8_t visibilityMaskTouched) { // auto items = input.get(); // Shadows use an orthographic projection because they are linked to sunlights @@ -30,7 +30,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: return true; }); - const auto items = task.addJob("FetchCullSort", cullFunctor, visibilityMask); + const auto items = task.addJob("FetchCullSort", cullFunctor, visibilityMask, visibilityMaskTouched); assert(items.canCast()); if (isDeferred) { diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index 9c2bbe0281..faace7349e 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -23,7 +23,7 @@ public: RenderViewTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask = 0xFF); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask = 0xFF, uint8_t visibilityMaskTouched = 0x00); }; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 8594bc2954..3da7b07d7d 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -65,6 +65,10 @@ public: // Beware that the visibility mask is the oposite of what stored in the key vals. const static uint8_t NUM_VISIBLE_MASK_INDICES{ 4 }; const static uint8_t VISIBLE_MASK_ALL{ 0x0F }; + const static uint8_t VISIBLE_MASK_0{ 0x01 }; + const static uint8_t VISIBLE_MASK_1{ 0x02 }; + const static uint8_t VISIBLE_MASK_2{ 0x04 }; + const static uint8_t VISIBLE_MASK_3{ 0x08 }; // The key is the Flags Flags _flags; @@ -184,12 +188,15 @@ public: Builder& withVisible(uint8_t maskIndex) { _value.reset(ItemKey::INVISIBLE0 + maskIndex); _mask.set(ItemKey::INVISIBLE0 + maskIndex); return (*this); } Builder& withInvisible(uint8_t maskIndex) { _value.set(ItemKey::INVISIBLE0 + maskIndex); _mask.set(ItemKey::INVISIBLE0 + maskIndex); return (*this); } - Builder& withVisibilityMask(uint8_t mask) { + Builder& withVisibilityMask(uint8_t mask, uint8_t touchBits) { for (int i = 0; i < ItemKey::NUM_VISIBLE_MASK_INDICES; i++) { - if ((1 << i) && mask) { - withVisible(i); - } else { - withInvisible(i); + if ((1 << i) & touchBits) { + if ((1 << i) & mask) { + withVisible(i); + } + else { + withInvisible(i); + } } } return (*this); @@ -206,7 +213,7 @@ public: Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place - static Builder visibleWorldItems(uint8_t visibilityMask) { return Builder().withVisibilityMask(visibilityMask).withWorldSpace(); } + static Builder visibleWorldItems(uint8_t visibilityMask, uint8_t visibilityMaskTouched) { return Builder().withVisibilityMask(visibilityMask, visibilityMaskTouched).withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 25c40ea7f7..b4e1a618dc 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -17,12 +17,12 @@ using namespace render; -void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor, uint8_t visibilityMask) { +void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor, uint8_t visibilityMask, uint8_t visibilityMaskTouched) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // CPU jobs: // Fetch and cull the items from the scene - const ItemFilter filter = ItemFilter::Builder::visibleWorldItems(visibilityMask).withoutLayered(); + const ItemFilter filter = ItemFilter::Builder::visibleWorldItems(visibilityMask, visibilityMaskTouched).withoutLayered(); const auto spatialFilter = render::Varying(filter); const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); @@ -40,15 +40,15 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const int META_BUCKET = 3; const int BACKGROUND_BUCKET = 2; MultiFilterItems::ItemFilterArray spatialFilters = { { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::light(), - ItemFilter::Builder::meta() + ItemFilter::Builder::opaqueShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), + ItemFilter::Builder::transparentShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), + ItemFilter::Builder::light().withVisibilityMask(visibilityMask, visibilityMaskTouched), + ItemFilter::Builder::meta().withVisibilityMask(visibilityMask, visibilityMaskTouched) } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::background() + ItemFilter::Builder::opaqueShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), + ItemFilter::Builder::transparentShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), + ItemFilter::Builder::background().withVisibilityMask(visibilityMask, visibilityMaskTouched) } }; const auto filteredSpatialBuckets = task.addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 3e5ad2bad8..9aa235b3fb 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -36,7 +36,7 @@ public: RenderFetchCullSortTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t visibilityMask = 0xFF); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t visibilityMask = 0xFF, uint8_t visibilityMaskTouched = 0x00); }; #endif // hifi_RenderFetchCullSortTask_h From 97a4f141eb6048c3d245a02716f58c0bb4f67cdc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Jan 2018 08:12:14 -0800 Subject: [PATCH 046/381] fix link trouble --- libraries/render/src/render/Item.cpp | 8 ++++++++ libraries/render/src/render/Item.h | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 036c7d3a99..75c38ec615 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -34,6 +34,14 @@ const int Item::LAYER_3D = 1; const int Item::LAYER_3D_FRONT = 2; const int Item::LAYER_3D_HUD = 3; +const uint8_t ItemKey::NUM_VISIBLE_MASK_INDICES { 4 }; +const uint8_t ItemKey::VISIBLE_MASK_ALL { 0x0F }; +const uint8_t ItemKey::VISIBLE_MASK_0 { 0x01 }; +const uint8_t ItemKey::VISIBLE_MASK_1 { 0x02 }; +const uint8_t ItemKey::VISIBLE_MASK_2 { 0x04 }; +const uint8_t ItemKey::VISIBLE_MASK_3 { 0x08 }; + + void Item::Status::Value::setScale(float scale) { _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 3da7b07d7d..ebf3980489 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -63,12 +63,12 @@ public: // VISIBLE MASK is defined from several bits in the Key. // An Item can be visible in some mask bits and not other allowing for per view rendering // Beware that the visibility mask is the oposite of what stored in the key vals. - const static uint8_t NUM_VISIBLE_MASK_INDICES{ 4 }; - const static uint8_t VISIBLE_MASK_ALL{ 0x0F }; - const static uint8_t VISIBLE_MASK_0{ 0x01 }; - const static uint8_t VISIBLE_MASK_1{ 0x02 }; - const static uint8_t VISIBLE_MASK_2{ 0x04 }; - const static uint8_t VISIBLE_MASK_3{ 0x08 }; + const static uint8_t NUM_VISIBLE_MASK_INDICES; + const static uint8_t VISIBLE_MASK_ALL; + const static uint8_t VISIBLE_MASK_0; + const static uint8_t VISIBLE_MASK_1; + const static uint8_t VISIBLE_MASK_2; + const static uint8_t VISIBLE_MASK_3; // The key is the Flags Flags _flags; From 1c819c84224b077fa1e203429478a0916d9b4805 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Jan 2018 09:12:37 -0800 Subject: [PATCH 047/381] formatting --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 4 ++-- libraries/render-utils/src/MeshPartPayload.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index f3ef956146..0988c696dd 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -187,8 +187,8 @@ void EntityRenderer::render(RenderArgs* args) { auto& renderMode = args->_renderMode; bool cauterized = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && - renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && - _cauterized; + renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE && + _cauterized); if (_visible && !cauterized) { doRender(args); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ead2a767db..de03f2e03d 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -395,8 +395,7 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, bool isCauteri if (!isVisible) { builder.withInvisible(); - } - else if (isCauterized) { + } else if (isCauterized) { builder.withInvisible(0); // hide these items in the vibility mask #0 } From fb974b0b9c93f77d75fe460ae7fcfa03c83e43ad Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Jan 2018 10:09:23 -0800 Subject: [PATCH 048/381] attempt to straighten out use of view-visibility masks --- interface/src/avatar/MyAvatar.cpp | 9 +++++---- interface/src/ui/overlays/ModelOverlay.cpp | 3 ++- .../src/RenderableModelEntityItem.cpp | 10 +++++++--- libraries/render-utils/src/CauterizedModel.cpp | 2 +- libraries/render-utils/src/MeshPartPayload.cpp | 6 +++--- libraries/render-utils/src/MeshPartPayload.h | 2 +- libraries/render-utils/src/Model.cpp | 16 ++++++++-------- libraries/render-utils/src/Model.h | 2 +- 8 files changed, 28 insertions(+), 22 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0b5bdad6ae..e9205157b4 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1078,7 +1078,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene()); + _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_ALL); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1428,7 +1428,7 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); - _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); + _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_ALL); _headBoneSet.clear(); emit skeletonChanged(); @@ -1742,7 +1742,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) { if (model->isActive() && model->isRenderable()) { - model->setVisibleInScene(visible, scene); + model->setVisibleInScene(visible, scene, render::ItemKey::VISIBLE_MASK_ALL); } } @@ -1937,7 +1937,8 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { - _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene()); + _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(), + render::ItemKey::VISIBLE_MASK_ALL); } } } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ec586771af..f556bc9e24 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -86,7 +86,8 @@ void ModelOverlay::update(float deltatime) { } if (_visibleDirty) { _visibleDirty = false; - _model->setVisibleInScene(getVisible(), scene); + // don't show overlays in mirrors + _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_ALL & ~render::ItemKey::VISIBLE_MASK_1); } if (_drawInFrontDirty) { _drawInFrontDirty = false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index f7d7eb2a06..421f8769b5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1333,12 +1333,16 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); entity->stopModelOverrideIfNoParent(); - bool visible = _visible && !_cauterized; - if (model->isVisible() != visible) { + if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(visible, scene); + if (_cauterized) { + // draw this in every view except the main one + model->setVisibleInScene(_visible, scene, render::ItemKey::VISIBLE_MASK_ALL & ~render::ItemKey::VISIBLE_MASK_0); + } else { + model->setVisibleInScene(_visible, scene, render::ItemKey::VISIBLE_MASK_ALL); + } } // TODO? early exit here when not visible? diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 09b6ada8e4..809be09436 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -247,7 +247,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, enableCauterization); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index de03f2e03d..797f385695 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -389,14 +389,14 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render _worldBound.transform(boundTransform); } -void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, bool isCauterized) { +void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVisiblityMask) { ItemKey::Builder builder; builder.withTypeShape(); if (!isVisible) { builder.withInvisible(); - } else if (isCauterized) { - builder.withInvisible(0); // hide these items in the vibility mask #0 + } else { + builder.withInvisible(viewVisiblityMask); } if (isLayered) { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ee515c7b3d..cf64879b3c 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -103,7 +103,7 @@ public: render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) override; - void setKey(bool isVisible, bool isLayered, bool isCauterized = false); + void setKey(bool isVisible, bool isLayered, uint8_t viewVisiblityMask); void setLayer(bool isLayeredInFront, bool isLayeredInHUD); void setShapeKey(bool invalidateShapeKey, bool isWireframe); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6922e95d39..b04ff00f98 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -297,7 +297,7 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); @@ -659,7 +659,7 @@ void Model::calculateTriangleSets() { } } -void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene) { +void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t visibleMask) { if (_isVisible != isVisible) { _isVisible = isVisible; @@ -669,12 +669,12 @@ void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene) render::Transaction transaction; foreach (auto item, _modelMeshRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); }); } scene->enqueueTransaction(transaction); @@ -692,13 +692,13 @@ void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } @@ -716,13 +716,13 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 560aa82f0c..8d2619bf13 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,7 +86,7 @@ public: const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support - void setVisibleInScene(bool isVisible, const render::ScenePointer& scene); + void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t visibleMask); void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene); void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene); bool needsFixupInScene() const; From cede8ed597d0b096eda2b9bfe502972a8f622f87 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Thu, 18 Jan 2018 19:56:55 +0000 Subject: [PATCH 049/381] add focused color keyboard navigation uses focusedColor --- interface/resources/qml/controls-uit/Button.qml | 8 ++++++-- interface/resources/qml/dialogs/MessageDialog.qml | 8 ++++---- interface/resources/qml/dialogs/QueryDialog.qml | 4 ++-- interface/resources/qml/styles-uit/HifiConstants.qml | 1 + 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index dde07f45ea..e4360f8cf5 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -57,8 +57,10 @@ Original.Button { hifi.buttons.disabledColorStart[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered || control.focus) { + } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorStart[control.color] } @@ -71,8 +73,10 @@ Original.Button { hifi.buttons.disabledColorFinish[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered || control.focus) { + } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorFinish[control.color] } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 79d4b009db..351df6dc8a 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -37,14 +37,14 @@ ModalWindow { return OffscreenUi.waitForMessageBoxResult(root); } - Keys.onRightPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + Keys.onRightPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) { yesButton.forceActiveFocus() - } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + } else if (defaultButton === OriginalDialogs.StandardButton.Ok) { okButton.forceActiveFocus() } - Keys.onTabPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + Keys.onTabPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) { yesButton.forceActiveFocus() - } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + } else if (defaultButton === OriginalDialogs.StandardButton.Ok) { okButton.forceActiveFocus() } property alias detailedText: detailedText.text diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index a5ec9828e2..b5de5362f2 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -200,9 +200,9 @@ ModalWindow { case Qt.Key_Enter: if (acceptButton.focus) { acceptAction.trigger() - } else if(cancelButton.focus) { + } else if (cancelButton.focus) { cancelAction.trigger() - } else if(comboBox.focus || comboBox.popup.focus) { + } else if (comboBox.focus || comboBox.popup.focus) { comboBox.showList() } event.accepted = true; diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index cf800cb62e..5da587ea57 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -219,6 +219,7 @@ Item { readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] + readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight] readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow] readonly property var disabledTextColor: [ colors.lightGrayText, colors.baseGrayShadow] From 0ad4cdc41b47618bdb712fad907b54e980281c12 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Jan 2018 13:15:47 -0800 Subject: [PATCH 050/381] works now --- .../src/RenderableModelEntityItem.cpp | 13 +++--- .../render-utils/src/MeshPartPayload.cpp | 13 +++++- libraries/render-utils/src/Model.cpp | 46 ++++++++++++------- libraries/render-utils/src/Model.h | 4 +- 4 files changed, 50 insertions(+), 26 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 421f8769b5..80c5c97799 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1333,16 +1333,15 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); entity->stopModelOverrideIfNoParent(); - if (model->isVisible() != _visible) { + uint32_t viewVisiblityMask = _cauterized ? + (render::ItemKey::VISIBLE_MASK_ALL & ~render::ItemKey::VISIBLE_MASK_0) : // draw in every view except the main one + render::ItemKey::VISIBLE_MASK_ALL; + + if (model->isVisible() != _visible || model->getViewVisibilityMask() != viewVisiblityMask) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - if (_cauterized) { - // draw this in every view except the main one - model->setVisibleInScene(_visible, scene, render::ItemKey::VISIBLE_MASK_ALL & ~render::ItemKey::VISIBLE_MASK_0); - } else { - model->setVisibleInScene(_visible, scene, render::ItemKey::VISIBLE_MASK_ALL); - } + model->setVisibleInScene(_visible, scene, viewVisiblityMask); } // TODO? early exit here when not visible? diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 797f385695..b917f3df03 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -396,7 +396,18 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi if (!isVisible) { builder.withInvisible(); } else { - builder.withInvisible(viewVisiblityMask); + if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_0)) { + builder.withInvisible(render::ItemKey::INVISIBLE0 - render::ItemKey::INVISIBLE0); + } + if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_1)) { + builder.withInvisible(render::ItemKey::INVISIBLE1 - render::ItemKey::INVISIBLE0); + } + if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_2)) { + builder.withInvisible(render::ItemKey::INVISIBLE2 - render::ItemKey::INVISIBLE0); + } + if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_3)) { + builder.withInvisible(render::ItemKey::INVISIBLE3 - render::ItemKey::INVISIBLE0); + } } if (isLayered) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b04ff00f98..9e6ec064b4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -268,6 +268,7 @@ void Model::updateRenderItems() { bool isWireframe = self->isWireframe(); bool isVisible = self->isVisible(); + uint8_t viewVisibilityMask = self->getViewVisibilityMask(); bool isLayeredInFront = self->isLayeredInFront(); bool isLayeredInHUD = self->isLayeredInHUD(); @@ -280,8 +281,10 @@ void Model::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey, - isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { + transaction.updateItem(itemID, [modelTransform, clusterTransforms, + invalidatePayloadShapeKey, isWireframe, isVisible, + viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { data.updateClusterBuffer(clusterTransforms); Transform renderTransform = modelTransform; @@ -297,7 +300,7 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); @@ -659,22 +662,25 @@ void Model::calculateTriangleSets() { } } -void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t visibleMask) { - if (_isVisible != isVisible) { +void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewVisibilityMask) { + if (_isVisible != isVisible || _viewVisibilityMask != viewVisibilityMask) { _isVisible = isVisible; + _viewVisibilityMask = viewVisibilityMask; bool isLayeredInFront = _isLayeredInFront; bool isLayeredInHUD = _isLayeredInHUD; render::Transaction transaction; foreach (auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, 14); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, 14); }); } scene->enqueueTransaction(transaction); @@ -687,18 +693,21 @@ void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& _isLayeredInFront = isLayeredInFront; bool isVisible = _isVisible; + uint8_t viewVisibilityMask = _viewVisibilityMask; bool isLayeredInHUD = _isLayeredInHUD; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } @@ -711,18 +720,21 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce _isLayeredInHUD = isLayeredInHUD; bool isVisible = _isVisible; + uint8_t viewVisibilityMask = _viewVisibilityMask; bool isLayeredInFront = _isLayeredInFront; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 8d2619bf13..c2f8a3d972 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,7 +86,7 @@ public: const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support - void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t visibleMask); + void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewVisiblityMask); void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene); void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene); bool needsFixupInScene() const; @@ -104,6 +104,7 @@ public: bool isRenderable() const; bool isVisible() const { return _isVisible; } + uint8_t getViewVisibilityMask() const { return _viewVisibilityMask; } bool isLayeredInFront() const { return _isLayeredInFront; } bool isLayeredInHUD() const { return _isLayeredInHUD; } @@ -388,6 +389,7 @@ protected: QUrl _url; bool _isVisible; + uint32_t _viewVisibilityMask { 0 }; gpu::Buffers _blendedVertexBuffers; From 7c32d3c5360497bb8e531ad1fc1e49edc2879136 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Jan 2018 13:49:17 -0800 Subject: [PATCH 051/381] flip logic back around --- interface/src/ui/overlays/ModelOverlay.cpp | 2 +- .../entities-renderer/src/RenderableModelEntityItem.cpp | 4 ++-- libraries/render-utils/src/MeshPartPayload.cpp | 8 ++++---- libraries/render-utils/src/Model.cpp | 4 ++-- libraries/render-utils/src/Model.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index f556bc9e24..7ba8694683 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -87,7 +87,7 @@ void ModelOverlay::update(float deltatime) { if (_visibleDirty) { _visibleDirty = false; // don't show overlays in mirrors - _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_ALL & ~render::ItemKey::VISIBLE_MASK_1); + _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_1); } if (_drawInFrontDirty) { _drawInFrontDirty = false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 80c5c97799..8257e53eea 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1334,8 +1334,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->stopModelOverrideIfNoParent(); uint32_t viewVisiblityMask = _cauterized ? - (render::ItemKey::VISIBLE_MASK_ALL & ~render::ItemKey::VISIBLE_MASK_0) : // draw in every view except the main one - render::ItemKey::VISIBLE_MASK_ALL; + render::ItemKey::VISIBLE_MASK_0 : // draw in every view except the main one + render::ItemKey::VISIBLE_MASK_ALL; // draw in all views if (model->isVisible() != _visible || model->getViewVisibilityMask() != viewVisiblityMask) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index b917f3df03..f0412d8332 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -396,16 +396,16 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi if (!isVisible) { builder.withInvisible(); } else { - if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_0)) { + if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_0) { builder.withInvisible(render::ItemKey::INVISIBLE0 - render::ItemKey::INVISIBLE0); } - if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_1)) { + if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_1) { builder.withInvisible(render::ItemKey::INVISIBLE1 - render::ItemKey::INVISIBLE0); } - if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_2)) { + if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_2) { builder.withInvisible(render::ItemKey::INVISIBLE2 - render::ItemKey::INVISIBLE0); } - if (!(viewVisiblityMask & render::ItemKey::VISIBLE_MASK_3)) { + if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_3) { builder.withInvisible(render::ItemKey::INVISIBLE3 - render::ItemKey::INVISIBLE0); } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9e6ec064b4..60eb0c4280 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -674,13 +674,13 @@ void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, foreach (auto item, _modelMeshRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, 14); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, 14); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); }); } scene->enqueueTransaction(transaction); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index c2f8a3d972..5f58906e9f 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -389,7 +389,7 @@ protected: QUrl _url; bool _isVisible; - uint32_t _viewVisibilityMask { 0 }; + uint8_t _viewVisibilityMask { 0 }; gpu::Buffers _blendedVertexBuffers; From 31c007d1672a970dc5a1bfbe11b88801fb36f55b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Jan 2018 14:15:30 -0800 Subject: [PATCH 052/381] more backwards logic, added withViewVisibilityMask call in ItemKey --- interface/src/avatar/MyAvatar.cpp | 8 ++++---- .../src/RenderableModelEntityItem.cpp | 4 ++-- libraries/entities/src/EntityItem.h | 2 +- libraries/render-utils/src/CauterizedModel.cpp | 2 +- libraries/render-utils/src/MeshPartPayload.cpp | 13 +------------ libraries/render/src/render/Item.cpp | 1 + libraries/render/src/render/Item.h | 16 ++++++++++++++++ 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e9205157b4..317eede6e9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1078,7 +1078,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_ALL); + _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_NONE); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1428,7 +1428,7 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); - _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_ALL); + _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_NONE); _headBoneSet.clear(); emit skeletonChanged(); @@ -1742,7 +1742,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) { if (model->isActive() && model->isRenderable()) { - model->setVisibleInScene(visible, scene, render::ItemKey::VISIBLE_MASK_ALL); + model->setVisibleInScene(visible, scene, render::ItemKey::VISIBLE_MASK_NONE); } } @@ -1938,7 +1938,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(), - render::ItemKey::VISIBLE_MASK_ALL); + render::ItemKey::VISIBLE_MASK_NONE); } } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8257e53eea..a6991f101c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1334,8 +1334,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->stopModelOverrideIfNoParent(); uint32_t viewVisiblityMask = _cauterized ? - render::ItemKey::VISIBLE_MASK_0 : // draw in every view except the main one - render::ItemKey::VISIBLE_MASK_ALL; // draw in all views + render::ItemKey::VISIBLE_MASK_0 : // draw in every view except the main one (view zero) + render::ItemKey::VISIBLE_MASK_NONE; // draw in all views if (model->isVisible() != _visible || model->getViewVisibilityMask() != viewVisiblityMask) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 68639e0d3a..3024d646ff 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -625,7 +625,7 @@ protected: quint64 _lastUpdatedAccelerationTimestamp { 0 }; quint64 _lastUpdatedQueryAACubeTimestamp { 0 }; - bool _cauterized { false }; // it true, don't draw because it would obscure 1st-person camera + bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera }; #endif // hifi_EntityItem_h diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 809be09436..f67f5ef358 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -247,7 +247,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_NONE); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index f0412d8332..da636eed06 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -396,18 +396,7 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi if (!isVisible) { builder.withInvisible(); } else { - if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_0) { - builder.withInvisible(render::ItemKey::INVISIBLE0 - render::ItemKey::INVISIBLE0); - } - if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_1) { - builder.withInvisible(render::ItemKey::INVISIBLE1 - render::ItemKey::INVISIBLE0); - } - if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_2) { - builder.withInvisible(render::ItemKey::INVISIBLE2 - render::ItemKey::INVISIBLE0); - } - if (viewVisiblityMask & render::ItemKey::VISIBLE_MASK_3) { - builder.withInvisible(render::ItemKey::INVISIBLE3 - render::ItemKey::INVISIBLE0); - } + builder.withViewVisibilityMask(viewVisiblityMask); } if (isLayered) { diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 75c38ec615..0e4502a47c 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -36,6 +36,7 @@ const int Item::LAYER_3D_HUD = 3; const uint8_t ItemKey::NUM_VISIBLE_MASK_INDICES { 4 }; const uint8_t ItemKey::VISIBLE_MASK_ALL { 0x0F }; +const uint8_t ItemKey::VISIBLE_MASK_NONE { 0x00 }; const uint8_t ItemKey::VISIBLE_MASK_0 { 0x01 }; const uint8_t ItemKey::VISIBLE_MASK_1 { 0x02 }; const uint8_t ItemKey::VISIBLE_MASK_2 { 0x04 }; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index ebf3980489..552e721073 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -65,6 +65,7 @@ public: // Beware that the visibility mask is the oposite of what stored in the key vals. const static uint8_t NUM_VISIBLE_MASK_INDICES; const static uint8_t VISIBLE_MASK_ALL; + const static uint8_t VISIBLE_MASK_NONE; const static uint8_t VISIBLE_MASK_0; const static uint8_t VISIBLE_MASK_1; const static uint8_t VISIBLE_MASK_2; @@ -96,6 +97,21 @@ public: Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } Builder& withInvisible(uint8_t maskIndex = 0) { _flags.set(INVISIBLE0 + maskIndex); return (*this); } + Builder& withViewVisibilityMask(uint8_t mask) { + if (mask & render::ItemKey::VISIBLE_MASK_0) { + _flags.set(INVISIBLE0); + } + if (mask & render::ItemKey::VISIBLE_MASK_1) { + _flags.set(INVISIBLE1); + } + if (mask & render::ItemKey::VISIBLE_MASK_2) { + _flags.set(INVISIBLE2); + } + if (mask & render::ItemKey::VISIBLE_MASK_3) { + _flags.set(INVISIBLE3); + } + return (*this); + } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } Builder& withPickable() { _flags.set(PICKABLE); return (*this); } Builder& withLayered() { _flags.set(LAYERED); return (*this); } From 25f5eb6b4fc3288d23b193e274996f72153cdec5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 18 Jan 2018 15:08:13 -0800 Subject: [PATCH 053/381] named property example --- libraries/entities/src/EntityEditFilters.cpp | 3 +++ scripts/tutorials/entity_edit_filters/position-example.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 2b0dcc9f73..a69a8ce7d1 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -299,6 +299,9 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { filterData.wantsZoneProperties = !stringValue.isEmpty(); if (filterData.wantsZoneProperties) { EntityPropertyFlagsFromScriptValue(wantsZonePropertiesValue, filterData.includedZoneProperties); + if (stringValue == "boundingBox") { + filterData.wantsZoneBoundingBox = true; + } } } else if (wantsZonePropertiesValue.isArray()) { auto length = wantsZonePropertiesValue.property("length").toInteger(); diff --git a/scripts/tutorials/entity_edit_filters/position-example.js b/scripts/tutorials/entity_edit_filters/position-example.js index 01eabee7db..314ba0fd9f 100644 --- a/scripts/tutorials/entity_edit_filters/position-example.js +++ b/scripts/tutorials/entity_edit_filters/position-example.js @@ -41,5 +41,5 @@ function filter(properties, type, originalProperties) { return properties; } -filter.wantsOriginalProperties = true; +filter.wantsOriginalProperties = "position"; filter; \ No newline at end of file From faf18fcaa89f58cf6b7b3826aa28c7919d4a19f2 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 01:49:24 +0000 Subject: [PATCH 054/381] improve file dialog navigation --- .../resources/qml/dialogs/FileDialog.qml | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 581ac37a74..42d6bf4261 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -102,7 +102,17 @@ ModalWindow { } }); - fileTableView.forceActiveFocus(); + focusTimer.start(); + } + + Timer { + id: focusTimer + interval: 10 + running: false + repeat: false + onTriggered: { + fileTableView.contentItem.forceActiveFocus(); + } } Item { @@ -122,7 +132,9 @@ ModalWindow { drag.target: root onClicked: { d.clearSelection(); - frame.forceActiveFocus(); // Defocus text field so that the keyboard gets hidden. + // Defocus text field so that the keyboard gets hidden. + // Clicking also breaks keyboard navigation apart from backtabbing to cancel + frame.forceActiveFocus(); } } @@ -142,6 +154,12 @@ ModalWindow { size: 30 enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" onClicked: d.navigateUp(); + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: fileTableView.contentItem + KeyNavigation.tab: fileTableView.contentItem + KeyNavigation.backtab: fileTableView.contentItem + KeyNavigation.left: fileTableView.contentItem + KeyNavigation.right: fileTableView.contentItem } GlyphButton { @@ -152,6 +170,12 @@ ModalWindow { width: height enabled: d.homeDestination ? true : false onClicked: d.navigateHome(); + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: fileTableView.contentItem + KeyNavigation.tab: fileTableView.contentItem + KeyNavigation.backtab: fileTableView.contentItem + KeyNavigation.left: fileTableView.contentItem + KeyNavigation.right: fileTableView.contentItem } } @@ -220,9 +244,15 @@ ModalWindow { d.currentSelectionUrl = helper.pathToUrl(currentText); } fileTableModel.folder = folder; - fileTableView.forceActiveFocus(); } } + + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: fileTableView.contentItem + KeyNavigation.tab: fileTableView.contentItem + KeyNavigation.backtab: fileTableView.contentItem + KeyNavigation.left: fileTableView.contentItem + KeyNavigation.right: fileTableView.contentItem } QtObject { @@ -638,6 +668,8 @@ ModalWindow { break; } } + + KeyNavigation.tab: root.saveDialog ? currentSelection : openButton } TextField { @@ -654,6 +686,10 @@ ModalWindow { activeFocusOnTab: !readOnly onActiveFocusChanged: if (activeFocus) { selectAll(); } onAccepted: okAction.trigger(); + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: openButton + KeyNavigation.tab: openButton + KeyNavigation.backtab: fileTableView.contentItem } FileTypeSelection { @@ -664,8 +700,6 @@ ModalWindow { right: parent.right } visible: !selectDirectory && filtersCount > 1 - KeyNavigation.left: fileTableView - KeyNavigation.right: openButton } Keyboard { @@ -693,18 +727,18 @@ ModalWindow { color: hifi.buttons.blue action: okAction Keys.onReturnPressed: okAction.trigger() - KeyNavigation.up: selectionType - KeyNavigation.left: selectionType KeyNavigation.right: cancelButton + KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem + KeyNavigation.tab: cancelButton } Button { id: cancelButton action: cancelAction - KeyNavigation.up: selectionType - KeyNavigation.left: openButton - KeyNavigation.right: fileTableView.contentItem Keys.onReturnPressed: { cancelAction.trigger() } + KeyNavigation.left: openButton + KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem + KeyNavigation.backtab: openButton } } From e69fdba6ad5879dadc2340b276a40383c40427a6 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:18:29 +0000 Subject: [PATCH 055/381] add padding to sorting glyph the file dialog sorting glyphs are clipping on the right margin for me, adding 10 extra pixels fixes it --- interface/resources/qml/controls-uit/Table.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 1443cd5d6e..a3e4113d08 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -93,7 +93,7 @@ TableView { size: hifi.fontSizes.tableHeadingIcon anchors { left: titleText.right - leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 5 : 0) + leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 15 : 10) right: parent.right rightMargin: hifi.dimensions.tablePadding verticalCenter: titleText.verticalCenter From 44740d5716cba750ff515910f1836833e4bf423a Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:27:49 +0000 Subject: [PATCH 056/381] add sound on button focus changed Feels more consistent with hovering --- interface/resources/qml/controls-uit/Button.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index e4360f8cf5..926e9c4fe5 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -32,6 +32,12 @@ Original.Button { Tablet.playSound(TabletEnums.ButtonHover); } } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } onClicked: { Tablet.playSound(TabletEnums.ButtonClick); From f1147948cb50ef3a28f2c4ff56a44723cd358699 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:28:27 +0000 Subject: [PATCH 057/381] add focused color to GlyphButton --- interface/resources/qml/controls-uit/GlyphButton.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index 9c23171ee1..024c131a09 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -31,6 +31,12 @@ Original.Button { } } + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + onClicked: { Tablet.playSound(TabletEnums.ButtonClick); } @@ -50,6 +56,8 @@ Original.Button { hifi.buttons.pressedColor[control.color] } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorStart[control.color] } @@ -64,6 +72,8 @@ Original.Button { hifi.buttons.pressedColor[control.color] } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorFinish[control.color] } From 4c27538fa2ad39f82322d2038fc0e514f75174f8 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:30:00 +0000 Subject: [PATCH 058/381] change file dialog keyboard navigation Allowed navigating with keyboard to home and up buttons in file dialog. --- .../resources/qml/dialogs/FileDialog.qml | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 42d6bf4261..145c0b2a30 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -154,12 +154,11 @@ ModalWindow { size: 30 enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" onClicked: d.navigateUp(); - KeyNavigation.up: fileTableView.contentItem - KeyNavigation.down: fileTableView.contentItem - KeyNavigation.tab: fileTableView.contentItem - KeyNavigation.backtab: fileTableView.contentItem - KeyNavigation.left: fileTableView.contentItem - KeyNavigation.right: fileTableView.contentItem + Keys.onReturnPressed: { d.navigateUp(); } + KeyNavigation.tab: homeButton + KeyNavigation.backtab: upButton + KeyNavigation.left: upButton + KeyNavigation.right: homeButton } GlyphButton { @@ -170,12 +169,10 @@ ModalWindow { width: height enabled: d.homeDestination ? true : false onClicked: d.navigateHome(); - KeyNavigation.up: fileTableView.contentItem - KeyNavigation.down: fileTableView.contentItem + Keys.onReturnPressed: { d.navigateHome(); } KeyNavigation.tab: fileTableView.contentItem - KeyNavigation.backtab: fileTableView.contentItem - KeyNavigation.left: fileTableView.contentItem - KeyNavigation.right: fileTableView.contentItem + KeyNavigation.backtab: upButton + KeyNavigation.left: upButton } } @@ -505,7 +502,6 @@ ModalWindow { } headerVisible: !selectDirectory onDoubleClicked: navigateToRow(row); - focus: true Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); @@ -582,7 +578,7 @@ ModalWindow { resizable: true } TableViewColumn { - id: fileMofifiedColumn + id: fileModifiedColumn role: "fileModified" title: "Date" width: 0.3 * fileTableView.width @@ -593,7 +589,7 @@ ModalWindow { TableViewColumn { role: "fileSize" title: "Size" - width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width + width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width movable: false resizable: true visible: !selectDirectory @@ -668,7 +664,7 @@ ModalWindow { break; } } - + KeyNavigation.tab: root.saveDialog ? currentSelection : openButton } From 6aa389cad08f42b0e434dd20bbe17338adce9e78 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 19 Jan 2018 09:53:41 -0800 Subject: [PATCH 059/381] trying to now show overlays in mirrors --- interface/src/ui/overlays/OverlaysPayload.cpp | 7 +++++++ libraries/render/src/render/Item.h | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index fceb261503..4bbe21f9a6 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -44,6 +44,13 @@ namespace render { } else { builder.withViewSpace(); } + + if (overlay->getVisible()) { + builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_1); // don't draw overlays in mirror + } else { + builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_ALL); + } + return builder.build(); } template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 552e721073..9ea8421f1a 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -96,7 +96,17 @@ public: Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } - Builder& withInvisible(uint8_t maskIndex = 0) { _flags.set(INVISIBLE0 + maskIndex); return (*this); } + Builder& withInvisible(uint8_t maskIndex = NUM_VISIBLE_MASK_INDICES) { + if (maskIndex == NUM_VISIBLE_MASK_INDICES) { + _flags.set(INVISIBLE0); + _flags.set(INVISIBLE1); + _flags.set(INVISIBLE2); + _flags.set(INVISIBLE3); + } else { + _flags.set(INVISIBLE0 + maskIndex); + } + return (*this); + } Builder& withViewVisibilityMask(uint8_t mask) { if (mask & render::ItemKey::VISIBLE_MASK_0) { _flags.set(INVISIBLE0); From 0f7f58417bc2ffbaa1a31e918a1b5b928edb0fbf Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 19 Jan 2018 19:11:12 +0100 Subject: [PATCH 060/381] JS scripting console auto-complete --- interface/CMakeLists.txt | 10 + interface/src/ui/JSConsole.cpp | 279 ++++++++++++++---- interface/src/ui/JSConsole.h | 16 +- .../src/UsersScriptingInterface.h | 2 +- tools/jsdoc/config.json | 5 +- tools/jsdoc/package.json | 7 + tools/jsdoc/plugins/hifi.js | 8 + tools/jsdoc/plugins/hifiJSONExport.js | 14 + 8 files changed, 285 insertions(+), 56 deletions(-) create mode 100644 tools/jsdoc/package.json create mode 100644 tools/jsdoc/plugins/hifiJSONExport.js diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 21225756b4..fbc40f70c2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -349,6 +349,16 @@ endif() add_bugsplat() +# generate the JSDoc JSON for the JSConsole auto-completer +add_custom_command(TARGET ${TARGET_NAME} #POST_BUILD + COMMAND npm install + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc +) +add_custom_command(TARGET ${TARGET_NAME} + COMMAND node_modules/.bin/jsdoc . -c config.json + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc +) + if (WIN32) set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index c5f8b54ebd..7f68a205e6 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -12,10 +12,11 @@ #include "JSConsole.h" #include -#include #include #include #include +#include +#include #include #include @@ -61,12 +62,64 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } +void JSConsole::readAPI() { + QFile file(PathUtils::resourcesPath() + "auto-complete/export.json"); + file.open(QFile::ReadOnly); + auto json = QTextStream(&file).readAll().toUtf8(); + _apiDocs = QJsonDocument::fromJson(json).array(); +} + +QStandardItem* getAutoCompleteItem(QJsonValue propertyObject) { + auto propertyItem = new QStandardItem(propertyObject.toObject().value("name").toString()); + propertyItem->setData(propertyObject.toVariant()); + return propertyItem; +} + +QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { + QString memberOfProperty = nullptr; + + auto model = new QStandardItemModel(this); + + if (memberOf != nullptr) { + foreach(auto doc, _apiDocs) { + auto object = doc.toObject(); + if (object.value("name").toString() == memberOf && object.value("scope").toString() == "global" && + object.value("kind").toString() == "namespace") { + + memberOfProperty = object.value("longname").toString(); + + auto properties = doc.toObject().value("properties").toArray(); + foreach(auto propertyObject, properties) { + model->appendRow(getAutoCompleteItem(propertyObject)); + } + } + } + if (memberOfProperty == nullptr) { + return nullptr; + } + } + + foreach(auto doc, _apiDocs) { + auto object = doc.toObject(); + auto scope = object.value("scope"); + if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty)) { + model->appendRow(getAutoCompleteItem(doc)); + } + } + model->sort(0); + return model; +} + JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), - _commandHistory(_readLines(_savedHistoryFilename)) { + _commandHistory(_readLines(_savedHistoryFilename)), + _completer(new QCompleter(this)) { + + readAPI(); + _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); _ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap); @@ -78,38 +131,75 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : setStyleSheet(styleSheet.readAll()); } - connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom())); - connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput())); + connect(_ui->scrollArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, &JSConsole::scrollToBottom); + connect(_ui->promptTextEdit, &QTextEdit::textChanged, this, &JSConsole::resizeTextInput); + + _completer->setWidget(_ui->promptTextEdit); + _completer->setModel(getAutoCompleteModel(nullptr)); + _completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + _completer->setMaxVisibleItems(12); + _completer->setFilterMode(Qt::MatchStartsWith); + _completer->setWrapAround(false); + _completer->setCompletionMode(QCompleter::PopupCompletion); + _completer->setCaseSensitivity(Qt::CaseSensitive); + + QListView *listView = new QListView(); + listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView->setSelectionBehavior(QAbstractItemView::SelectRows); + listView->setSelectionMode(QAbstractItemView::SingleSelection); + listView->setModelColumn(_completer->completionColumn()); + + _completer->setPopup(listView); + _completer->popup()->installEventFilter(this); + QObject::connect(_completer, static_cast(&QCompleter::activated), this, + &JSConsole::insertCompletion); + + QObject::connect(_completer, static_cast(&QCompleter::highlighted), this, + &JSConsole::highlightedCompletion); setScriptEngine(scriptEngine); resizeTextInput(); - connect(&_executeWatcher, SIGNAL(finished()), this, SLOT(commandFinished())); + connect(&_executeWatcher, &QFutureWatcher::finished, this, &JSConsole::commandFinished); +} + +void JSConsole::insertCompletion(const QModelIndex& completion) { + auto completionString = completion.data().toString(); + QTextCursor tc = _ui->promptTextEdit->textCursor(); + int extra = completionString.length() - _completer->completionPrefix().length(); + tc.movePosition(QTextCursor::Left); + tc.movePosition(QTextCursor::EndOfWord); + tc.insertText(completionString.right(extra)); + _ui->promptTextEdit->setTextCursor(tc); +} + +void JSConsole::highlightedCompletion(const QModelIndex& completion) { + qDebug() << "Highlighted " << completion.data().toString(); + auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); + + // qDebug() << "Highlighted data " << QJsonDocument(jsdocObject).toJson(QJsonDocument::Compact); } JSConsole::~JSConsole() { if (_scriptEngine) { - disconnect(_scriptEngine.data(), SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - disconnect(_scriptEngine.data(), SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + disconnect(_scriptEngine.data(), nullptr, this, nullptr); _scriptEngine.reset(); } delete _ui; } void JSConsole::setScriptEngine(const ScriptEnginePointer& scriptEngine) { - if (_scriptEngine == scriptEngine && scriptEngine != NULL) { + if (_scriptEngine == scriptEngine && scriptEngine != nullptr) { return; } - if (_scriptEngine != NULL) { - disconnect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - disconnect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - disconnect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - disconnect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); + if (_scriptEngine != nullptr) { + disconnect(_scriptEngine.data(), nullptr, this, nullptr); _scriptEngine.reset(); } - // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine + // if scriptEngine is nullptr then create one and keep track of it using _ownScriptEngine if (scriptEngine.isNull()) { _scriptEngine = DependencyManager::get()->loadScript(_consoleFileName, false); } else { @@ -199,45 +289,132 @@ void JSConsole::showEvent(QShowEvent* event) { } bool JSConsole::eventFilter(QObject* sender, QEvent* event) { - if (sender == _ui->promptTextEdit) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); - int key = keyEvent->key(); + if ((sender == _ui->promptTextEdit || sender == _completer->popup()) && event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + int key = keyEvent->key(); - if ((key == Qt::Key_Return || key == Qt::Key_Enter)) { - if (keyEvent->modifiers() & Qt::ShiftModifier) { - // If the shift key is being used then treat it as a regular return/enter. If this isn't done, - // a new QTextBlock isn't created. - keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier); - } else { - QString command = _ui->promptTextEdit->toPlainText().replace("\r\n","\n").trimmed(); - - if (!command.isEmpty()) { - QTextCursor cursor = _ui->promptTextEdit->textCursor(); - _ui->promptTextEdit->clear(); - - executeCommand(command); - } - - return true; - } - } else if (key == Qt::Key_Down) { - // Go to the next command in history if the cursor is at the last line of the current command. - int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); - int blockCount = _ui->promptTextEdit->document()->blockCount(); - if (blockNumber == blockCount - 1) { - setToNextCommandInHistory(); - return true; - } - } else if (key == Qt::Key_Up) { - // Go to the previous command in history if the cursor is at the first line of the current command. - int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); - if (blockNumber == 0) { - setToPreviousCommandInHistory(); - return true; - } + if (_completer->popup()->isVisible()) { + // The following keys are forwarded by the completer to the widget + switch (key) { + case Qt::Key_Space: + case Qt::Key_Enter: + case Qt::Key_Return: + insertCompletion(_completer->currentIndex()); + _completer->popup()->hide(); + return true; + case Qt::Key_Escape: + case Qt::Key_Tab: + case Qt::Key_Backtab: + qDebug() << "test"; + keyEvent->ignore();//setAccepted(false); + return false; // let the completer do default behavior + default: + return false; } } + + if ((key == Qt::Key_Return || key == Qt::Key_Enter)) { + if (keyEvent->modifiers() & Qt::ShiftModifier) { + // If the shift key is being used then treat it as a regular return/enter. If this isn't done, + // a new QTextBlock isn't created. + keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier); + } else { + QString command = _ui->promptTextEdit->toPlainText().replace("\r\n", "\n").trimmed(); + + if (!command.isEmpty()) { + QTextCursor cursor = _ui->promptTextEdit->textCursor(); + _ui->promptTextEdit->clear(); + + executeCommand(command); + } + + return true; + } + } else if (key == Qt::Key_Down) { + // Go to the next command in history if the cursor is at the last line of the current command. + int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); + int blockCount = _ui->promptTextEdit->document()->blockCount(); + if (blockNumber == blockCount - 1) { + setToNextCommandInHistory(); + return true; + } + } else if (key == Qt::Key_Up) { + // Go to the previous command in history if the cursor is at the first line of the current command. + int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); + if (blockNumber == 0) { + setToPreviousCommandInHistory(); + return true; + } + } + } else if ((sender == _ui->promptTextEdit || sender == _completer->popup()) && event->type() == QEvent::KeyRelease) { + QKeyEvent* keyEvent = static_cast(event); + int key = keyEvent->key(); + + // completer shortcut (CTRL + SPACE) + bool isCompleterShortcut = ((keyEvent->modifiers() & Qt::ControlModifier) && key == Qt::Key_Space) || + key == Qt::Key_Period; + if (_completer->popup()->isVisible() || isCompleterShortcut) { + + const bool ctrlOrShift = keyEvent->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier); + if (ctrlOrShift && keyEvent->text().isEmpty()) { + return false; + } + + static QString eow("~!@#$%^&*()+{}|:\"<>?,/;'[]\\-="); // end of word + bool hasModifier = (keyEvent->modifiers() != Qt::NoModifier) && !ctrlOrShift; + + + if (!isCompleterShortcut && (!keyEvent->text().isEmpty() && eow.contains(keyEvent->text().right(1)))) { + qDebug() << "eow contains " << keyEvent->text().right(1) << " full text: " << keyEvent->text(); + _completer->popup()->hide(); + return false; + } + qDebug() << "auto completing"; + + auto textCursor = _ui->promptTextEdit->textCursor(); + + textCursor.select(QTextCursor::WordUnderCursor); + + QString completionPrefix = textCursor.selectedText(); + + auto leftOfCursor = _ui->promptTextEdit->toPlainText().left(textCursor.position()); + qDebug() << "leftOfCursor" << leftOfCursor; + + // RegEx [3] [4] + // (Module.subModule).(property/subModule) + + const int MODULE_INDEX = 3; + const int PROPERTY_INDEX = 4; + // TODO: disallow invalid characters on left of property + QRegExp regExp("((([A-Za-z0-9_\\.]+)\\.)|(?!\\.))([a-zA-Z0-9_]*)$"); + int pos = regExp.indexIn(leftOfCursor); + auto rexExpCapturedTexts = regExp.capturedTexts(); + auto memberOf = rexExpCapturedTexts[MODULE_INDEX]; + completionPrefix = rexExpCapturedTexts[PROPERTY_INDEX]; + bool switchedModule = false; + if (memberOf != _completerModule) { + _completerModule = memberOf; + auto autoCompleteModel = getAutoCompleteModel(memberOf); + if (autoCompleteModel == nullptr) { + _completer->popup()->hide(); + return false; + } + _completer->setModel(autoCompleteModel); + _completer->popup()->installEventFilter(this); + switchedModule = true; + } + + if (switchedModule || completionPrefix != _completer->completionPrefix()) { + _completer->setCompletionPrefix(completionPrefix); + qDebug() << "Set completion prefix to:" << completionPrefix; + _completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0)); + } + auto cursorRect = _ui->promptTextEdit->cursorRect(); + cursorRect.setWidth(_completer->popup()->sizeHintForColumn(0) + + _completer->popup()->verticalScrollBar()->sizeHint().width()); + _completer->complete(cursorRect); + return false; + } } return false; } @@ -321,7 +498,7 @@ void JSConsole::appendMessage(const QString& gutter, const QString& message) { void JSConsole::clear() { QLayoutItem* item; - while ((item = _ui->logArea->layout()->takeAt(0)) != NULL) { + while ((item = _ui->logArea->layout()->takeAt(0)) != nullptr) { delete item->widget(); delete item; } diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 4b6409a76f..eeb3601886 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -12,12 +12,11 @@ #ifndef hifi_JSConsole_h #define hifi_JSConsole_h -#include -#include #include #include -#include #include +#include +#include #include "ui_console.h" #include "ScriptEngine.h" @@ -54,12 +53,20 @@ protected slots: void handleError(const QString& message, const QString& scriptName); void commandFinished(); +private slots: + void insertCompletion(const QModelIndex& completion); + void highlightedCompletion(const QModelIndex& completion); + private: void appendMessage(const QString& gutter, const QString& message); void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); + void readAPI(); + + QStandardItemModel* getAutoCompleteModel(const QString& memberOf = nullptr); + QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; @@ -68,6 +75,9 @@ private: QString _rootCommand; ScriptEnginePointer _scriptEngine; static const QString _consoleFileName; + QJsonArray _apiDocs; + QCompleter* _completer; + QString _completerModule {""}; }; diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 2d33bbca14..a4e741a797 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -150,7 +150,7 @@ signals: /**jsdoc * Notifies scripts that a user has disconnected from the domain - * @function Users.avatar.avatarDisconnected + * @function Users.avatarDisconnected * @param {nodeID} NodeID The session ID of the avatar that has disconnected */ void avatarDisconnected(const QUuid& nodeID); diff --git a/tools/jsdoc/config.json b/tools/jsdoc/config.json index 0fb833d015..a24e248661 100644 --- a/tools/jsdoc/config.json +++ b/tools/jsdoc/config.json @@ -4,5 +4,8 @@ "outputSourceFiles": false } }, - "plugins": ["plugins/hifi"] + "plugins": [ + "plugins/hifi", + "plugins/hifiJSONExport" + ] } diff --git a/tools/jsdoc/package.json b/tools/jsdoc/package.json new file mode 100644 index 0000000000..215ceec177 --- /dev/null +++ b/tools/jsdoc/package.json @@ -0,0 +1,7 @@ +{ + "name": "hifiJSDoc", + "dependencies": { + "jsdoc": "^3.5.5" + }, + "private": true +} diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 3af5fbeee3..afa3285c37 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -47,5 +47,13 @@ exports.handlers = { } }); }); + + fs.writeFile("out/hifiJSDoc.js", e.source, function(err) { + if (err) { + return console.log(err); + } + + console.log("The Hifi JSDoc JS was saved!"); + }); } }; diff --git a/tools/jsdoc/plugins/hifiJSONExport.js b/tools/jsdoc/plugins/hifiJSONExport.js new file mode 100644 index 0000000000..7948fd2673 --- /dev/null +++ b/tools/jsdoc/plugins/hifiJSONExport.js @@ -0,0 +1,14 @@ +exports.handlers = { + processingComplete: function(e) { + var doclets = e.doclets.map(doclet => Object.assign({}, doclet)); + const fs = require('fs'); + doclets.map(doclet => {delete doclet.meta; delete doclet.comment}); + fs.writeFile("out/hifiJSDoc.json", JSON.stringify(doclets, null, 4), function(err) { + if (err) { + return console.log(err); + } + + console.log("The Hifi JSDoc JSON was saved!"); + }); + } +}; From a31fe7546ad2500a2544fc694697a92634c3c8bd Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 19 Jan 2018 19:45:25 +0100 Subject: [PATCH 061/381] - remove debug - fix API export JSON filename - fix warnings --- interface/src/ui/JSConsole.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 7f68a205e6..3100b47882 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -63,7 +63,7 @@ void _writeLines(const QString& filename, const QList& lines) { } void JSConsole::readAPI() { - QFile file(PathUtils::resourcesPath() + "auto-complete/export.json"); + QFile file(PathUtils::resourcesPath() + "auto-complete/hifiJSDoc.json"); file.open(QFile::ReadOnly); auto json = QTextStream(&file).readAll().toUtf8(); _apiDocs = QJsonDocument::fromJson(json).array(); @@ -302,18 +302,12 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { insertCompletion(_completer->currentIndex()); _completer->popup()->hide(); return true; - case Qt::Key_Escape: - case Qt::Key_Tab: - case Qt::Key_Backtab: - qDebug() << "test"; - keyEvent->ignore();//setAccepted(false); - return false; // let the completer do default behavior default: return false; } } - if ((key == Qt::Key_Return || key == Qt::Key_Enter)) { + if (key == Qt::Key_Return || key == Qt::Key_Enter) { if (keyEvent->modifiers() & Qt::ShiftModifier) { // If the shift key is being used then treat it as a regular return/enter. If this isn't done, // a new QTextBlock isn't created. @@ -361,15 +355,11 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { } static QString eow("~!@#$%^&*()+{}|:\"<>?,/;'[]\\-="); // end of word - bool hasModifier = (keyEvent->modifiers() != Qt::NoModifier) && !ctrlOrShift; - if (!isCompleterShortcut && (!keyEvent->text().isEmpty() && eow.contains(keyEvent->text().right(1)))) { - qDebug() << "eow contains " << keyEvent->text().right(1) << " full text: " << keyEvent->text(); _completer->popup()->hide(); return false; } - qDebug() << "auto completing"; auto textCursor = _ui->promptTextEdit->textCursor(); @@ -378,7 +368,6 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { QString completionPrefix = textCursor.selectedText(); auto leftOfCursor = _ui->promptTextEdit->toPlainText().left(textCursor.position()); - qDebug() << "leftOfCursor" << leftOfCursor; // RegEx [3] [4] // (Module.subModule).(property/subModule) @@ -387,7 +376,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { const int PROPERTY_INDEX = 4; // TODO: disallow invalid characters on left of property QRegExp regExp("((([A-Za-z0-9_\\.]+)\\.)|(?!\\.))([a-zA-Z0-9_]*)$"); - int pos = regExp.indexIn(leftOfCursor); + regExp.indexIn(leftOfCursor); auto rexExpCapturedTexts = regExp.capturedTexts(); auto memberOf = rexExpCapturedTexts[MODULE_INDEX]; completionPrefix = rexExpCapturedTexts[PROPERTY_INDEX]; @@ -399,14 +388,13 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { _completer->popup()->hide(); return false; } - _completer->setModel(autoCompleteModel); + _completer->setModel(autoCompleteModel); _completer->popup()->installEventFilter(this); switchedModule = true; } if (switchedModule || completionPrefix != _completer->completionPrefix()) { _completer->setCompletionPrefix(completionPrefix); - qDebug() << "Set completion prefix to:" << completionPrefix; _completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0)); } auto cursorRect = _ui->promptTextEdit->cursorRect(); From c4359de859a0cf0e1edd112ea828f807cbb7b99e Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 19 Jan 2018 18:20:36 -0800 Subject: [PATCH 062/381] entity edit tools wip update --- scripts/system/edit.js | 2 +- .../system/libraries/entitySelectionTool.js | 1269 ++++++++++++----- 2 files changed, 883 insertions(+), 388 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 87cd3e0faf..2b886d2540 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -65,7 +65,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); selectionManager.addEventListener(function () { - selectionDisplay.updateHandles(); + selectionDisplay.updateGrabbers(); entityIconOverlayManager.updatePositions(); // Update particle explorer diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 220a7b7c70..253c67bfca 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -138,16 +138,16 @@ SelectionManager = (function() { that.localPosition = null; that.worldDimensions = null; that.worldPosition = null; + that.worldRotation = null; } else if (that.selections.length === 1) { properties = Entities.getEntityProperties(that.selections[0]); that.localDimensions = properties.dimensions; that.localPosition = properties.position; that.localRotation = properties.rotation; that.localRegistrationPoint = properties.registrationPoint; - - that.worldDimensions = properties.boundingBox.dimensions; - that.worldPosition = properties.boundingBox.center; - + that.worldDimensions = properties.dimensions; // properties.boundingbox.dimensions; + that.worldPosition = properties.position; + that.worldRotation = properties.rotation; SelectionDisplay.setSpaceMode(SPACE_LOCAL); } else { that.localRotation = null; @@ -194,7 +194,6 @@ SelectionManager = (function() { print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); } } - }; return that; @@ -230,16 +229,23 @@ SelectionDisplay = (function() { var COLOR_BLUE = { red:0, green:0, blue:255 }; var COLOR_RED = { red:255, green:0, blue:0 }; - var GRABBER_TRANSLATE_ARROW_CONE_OFFSET = 0.3625; - var GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET = 0.3; - var GRABBER_STRETCH_SPHERE_OFFSET = 0.2; - var GRABBER_SCALE_CUBE_OFFSET = 0.2; + var GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET = 1.35; + var GRABBER_TRANSLATE_ARROW_CYLINDER_DIMENSION_MULTIPLE = 0.05; + var GRABBER_TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE = 7.5; + var GRABBER_TRANSLATE_ARROW_CONE_DIMENSION_MULTIPLE = 0.25; + var GRABBER_ROTATE_RINGS_DIMENSION_MULTIPLE = 2.0; + var GRABBER_STRETCH_SPHERE_OFFSET = 1.0; + var GRABBER_STRETCH_SPHERE_DIMENSION_MULTIPLE = 0.1; + var GRABBER_SCALE_CUBE_OFFSET = 1.0; + var GRABBER_SCALE_CUBE_DIMENSION_MULTIPLE = 0.15; + var GRABBER_CLONER_OFFSET = { x:0.9, y:-0.9, z:0.9 }; var GRABBER_SCALE_CUBE_IDLE_COLOR = { red:120, green:120, blue:120 }; var GRABBER_SCALE_CUBE_SELECTED_COLOR = { red:0, green:0, blue:0 }; var GRABBER_SCALE_EDGE_COLOR = { red:120, green:120, blue:120 }; var SCALE_MINIMUM_DIMENSION = 0.02; + var STRETCH_MINIMUM_DIMENSION = 0.001; // These are multipliers for sizing the rotation degrees display while rotating an entity var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.0; @@ -253,6 +259,13 @@ SelectionDisplay = (function() { Z : 2 } + var STRETCH_DIRECTION = { + X : 0, + Y : 1, + Z : 2, + ALL : 3 + } + var ROTATE_DIRECTION = { PITCH : 0, YAW : 1, @@ -291,7 +304,6 @@ SelectionDisplay = (function() { var grabberPropertiesTranslateArrowCones = { shape: "Cone", - dimensions: { x:0.05, y:0.05, z:0.05 }, solid: true, visible: false, ignoreRayIntersection: false, @@ -299,7 +311,6 @@ SelectionDisplay = (function() { }; var grabberPropertiesTranslateArrowCylinders = { shape: "Cylinder", - dimensions: { x:0.01, y:0.075, z:0.01 }, solid: true, visible: false, ignoreRayIntersection: false, @@ -319,7 +330,6 @@ SelectionDisplay = (function() { Overlays.editOverlay(grabberTranslateZCylinder, { color : COLOR_BLUE }); var grabberPropertiesRotateRings = { - size: 0.5, alpha: 1, innerRadius: 0.9, startAt: 0, @@ -347,9 +357,8 @@ SelectionDisplay = (function() { }); var grabberRotateCurrentRing = Overlays.addOverlay("circle3d", { - size: 0.5, alpha: 1, - color: { red: 224, green: 67, blue: 36 }, + color: { red: 255, green: 99, blue: 9 }, solid: true, innerRadius: 0.9, visible: false, @@ -377,7 +386,6 @@ SelectionDisplay = (function() { var grabberPropertiesStretchSpheres = { shape: "Sphere", - dimensions: { x:0.02, y:0.02, z:0.02 }, solid: true, visible: false, ignoreRayIntersection: false, @@ -393,7 +401,6 @@ SelectionDisplay = (function() { var grabberPropertiesStretchPanel = { shape: "Quad", alpha: 0.5, - dimensions: { x:GRABBER_SCALE_CUBE_OFFSET * 2, y:GRABBER_SCALE_CUBE_OFFSET * 2, z:0.01 }, solid: true, visible: false, ignoreRayIntersection: true, @@ -444,6 +451,25 @@ SelectionDisplay = (function() { var grabberScaleFREdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); var grabberScaleFLEdge = Overlays.addOverlay("line3d", grabberPropertiesScaleEdge); + var grabberCloner = Overlays.addOverlay("cube", { + size: 0.05, + color: COLOR_GREEN, + solid: true, + visible: false, + ignoreRayIntersection: false, + drawInFront: true, + borderSize: 1.4 + }); + + var selectionBox = Overlays.addOverlay("cube", { + size: 1, + color: COLOR_RED, + alpha: 1, + solid: false, + visible: false, + dashed: false + }); + var allOverlays = [ grabberTranslateXCone, grabberTranslateXCylinder, @@ -481,7 +507,9 @@ SelectionDisplay = (function() { grabberScaleNREdge, grabberScaleNLEdge, grabberScaleFREdge, - grabberScaleFLEdge + grabberScaleFLEdge, + grabberCloner, + selectionBox ]; overlayNames[grabberTranslateXCone] = "grabberTranslateXCone"; @@ -521,6 +549,8 @@ SelectionDisplay = (function() { overlayNames[grabberScaleNLEdge] = "grabberScaleNLEdge"; overlayNames[grabberScaleFREdge] = "grabberScaleFREdge"; overlayNames[grabberScaleFLEdge] = "grabberScaleFLEdge"; + overlayNames[grabberCloner] = "grabberCloner"; + overlayNames[selectionBox] = "selectionBox"; // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we dont' get mousePressEvents. @@ -587,7 +617,7 @@ SelectionDisplay = (function() { pickNormal = { x:0, y:1, z:0 }; } - var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + var rotation = SelectionManager.worldRotation; pickNormal = Vec3.multiplyQbyV(rotation, pickNormal); lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); @@ -600,6 +630,7 @@ SelectionDisplay = (function() { that.setGrabberRotateVisible(false); that.setGrabberStretchVisible(false); that.setGrabberScaleVisible(false); + that.setGrabberClonerVisible(false); // Duplicate entities if alt is pressed. This will make a // copy of the selected entities and move the _original_ entities, not @@ -638,7 +669,7 @@ SelectionDisplay = (function() { projectionVector = { x:0, y:0, z:1 }; } - var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + var rotation = SelectionManager.worldRotation; projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); var dotVector = Vec3.dot(vector, projectionVector); @@ -666,258 +697,388 @@ SelectionDisplay = (function() { }); } - function addGrabberStretchTool(overlay, mode, direction) { - var pickNormal = null; + // FUNCTION: VEC 3 MULT + var vec3Mult = function(v1, v2) { + return { + x: v1.x * v2.x, + y: v1.y * v2.y, + z: v1.z * v2.z + }; + }; + + function makeStretchTool(stretchMode, directionEnum, directionVec, pivot, offset) { + var directionFor3DStretch = directionVec; + var distanceFor3DStretch = 0; + var DISTANCE_INFLUENCE_THRESHOLD = 1.2; + + var signs = { + x: directionVec.x < 0 ? -1 : (directionVec.x > 0 ? 1 : 0), + y: directionVec.y < 0 ? -1 : (directionVec.y > 0 ? 1 : 0), + z: directionVec.z < 0 ? -1 : (directionVec.z > 0 ? 1 : 0) + }; + + var mask = { + x: Math.abs(directionVec.x) > 0 ? 1 : 0, + y: Math.abs(directionVec.y) > 0 ? 1 : 0, + z: Math.abs(directionVec.z) > 0 ? 1 : 0 + }; + + var numDimensions = mask.x + mask.y + mask.z; + + var planeNormal = null; var lastPick = null; - var projectionVector = null; - var stretchPanel = null; - addGrabberTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - if (direction === TRANSLATE_DIRECTION.X) { - stretchPanel = grabberStretchXPanel; - pickNormal = { x:0, y:0, z:1 }; - } else if (direction === TRANSLATE_DIRECTION.Y) { - stretchPanel = grabberStretchYPanel; - pickNormal = { x:1, y:0, z:0 }; - } else if (direction === TRANSLATE_DIRECTION.Z) { - stretchPanel = grabberStretchZPanel; - pickNormal = { x:0, y:1, z:0 }; - } + var lastPick3D = null; + var initialPosition = null; + var initialDimensions = null; + var initialIntersection = null; + var initialProperties = null; + var registrationPoint = null; + var deltaPivot = null; + var deltaPivot3D = null; + var pickRayPosition = null; + var pickRayPosition3D = null; + var rotation = null; - Overlays.editOverlay(stretchPanel, { visible:true }); + var onBegin = function(event, pickRay, pickResult) { + var properties = Entities.getEntityProperties(SelectionManager.selections[0]); + initialProperties = properties; + rotation = (spaceMode === SPACE_LOCAL) ? properties.rotation : Quat.IDENTITY; - var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; - pickNormal = Vec3.multiplyQbyV(rotation, pickNormal); - - lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); - - SelectionManager.saveProperties(); - - that.setGrabberTranslateVisible(false); - that.setGrabberRotateVisible(false); - that.setGrabberStretchXVisible(direction === TRANSLATE_DIRECTION.X); - that.setGrabberStretchYVisible(direction === TRANSLATE_DIRECTION.Y); - that.setGrabberStretchZVisible(direction === TRANSLATE_DIRECTION.Z); - - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } - } - } else { - duplicatedEntityIDs = null; - } - }, - onEnd: function(event, reason) { - Overlays.editOverlay(stretchPanel, { visible:false }); - pushCommandForSelections(duplicatedEntityIDs); - }, - onMove: function(event) { - pickRay = generalComputePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); - var vector = Vec3.subtract(newIntersection, lastPick); - - if (direction === TRANSLATE_DIRECTION.X) { - projectionVector = { x:1, y:0, z:0 }; - } else if (direction === TRANSLATE_DIRECTION.Y) { - projectionVector = { x:0, y:1, z:0 }; - } else if (direction === TRANSLATE_DIRECTION.Z) { - projectionVector = { x:0, y:0, z:1 }; - } - - var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; - projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); - - var dotVector = Vec3.dot(vector, projectionVector); - vector = Vec3.multiply(dotVector, projectionVector); - vector = grid.snapToGrid(vector); - - var wantDebug = false; - if (wantDebug) { - print("translateUpDown... "); - print(" event.y:" + event.y); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - // Vec3.print(" newPosition:", newPosition); - } - - for (var i = 0; i < SelectionManager.selections.length; i++) { - var id = SelectionManager.selections[i]; - var properties = SelectionManager.savedProperties[id]; - var newPosition = Vec3.sum(properties.position, vector); - var difference = Vec3.subtract(newPosition, SelectionManager.worldPosition); - var halfDifference = Vec3.multiply(difference, 0.5); - var quarterDifference = Vec3.multiply(halfDifference, 0.5); - var newDimensions = properties.dimensions; - var actualNewPosition = properties.position; - - if (direction == TRANSLATE_DIRECTION.X) { - newDimensions.x += halfDifference.x; - actualNewPosition.x += quarterDifference.x; - } else if (direction == TRANSLATE_DIRECTION.Y) { - newDimensions.y += halfDifference.y; - actualNewPosition.y += quarterDifference.y; - } else if (direction == TRANSLATE_DIRECTION.Z) { - newDimensions.z += halfDifference.z; - actualNewPosition.z += quarterDifference.z; - } - - Entities.editEntity(id, { - position: actualNewPosition, - dimensions: newDimensions - }); - } - - SelectionManager._update(); + if (spaceMode === SPACE_LOCAL) { + rotation = SelectionManager.localRotation; + initialPosition = SelectionManager.localPosition; + initialDimensions = SelectionManager.localDimensions; + registrationPoint = SelectionManager.localRegistrationPoint; + } else { + rotation = SelectionManager.worldRotation; + initialPosition = SelectionManager.worldPosition; + initialDimensions = SelectionManager.worldDimensions; + registrationPoint = SelectionManager.worldRegistrationPoint; } - }); + + // Modify range of registrationPoint to be [-0.5, 0.5] + var centeredRP = Vec3.subtract(registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); + + // Scale pivot to be in the same range as registrationPoint + var scaledPivot = Vec3.multiply(0.5, pivot); + deltaPivot = Vec3.subtract(centeredRP, scaledPivot); + + var scaledOffset = Vec3.multiply(0.5, offset); + + // Offset from the registration point + offsetRP = Vec3.subtract(scaledOffset, centeredRP); + + // Scaled offset in world coordinates + var scaledOffsetWorld = vec3Mult(initialDimensions, offsetRP); + + pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); + + if (directionFor3DStretch) { + // pivot, offset and pickPlanePosition for 3D manipulation + var scaledPivot3D = Vec3.multiply(0.5, Vec3.multiply(1.0, directionFor3DStretch)); + deltaPivot3D = Vec3.subtract(centeredRP, scaledPivot3D); + + var scaledOffsetWorld3D = vec3Mult(initialDimensions, + Vec3.subtract(Vec3.multiply(0.5, Vec3.multiply(-1.0, directionFor3DStretch)), centeredRP)); + + pickRayPosition3D = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); + } + var start = null; + var end = null; + if ((numDimensions === 1) && mask.x) { + start = Vec3.multiplyQbyV(rotation, { + x: -10000, + y: 0, + z: 0 + }); + start = Vec3.sum(start, properties.position); + end = Vec3.multiplyQbyV(rotation, { + x: 10000, + y: 0, + z: 0 + }); + end = Vec3.sum(end, properties.position); + } + if ((numDimensions === 1) && mask.y) { + start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: -10000, + z: 0 + }); + start = Vec3.sum(start, properties.position); + end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 10000, + z: 0 + }); + end = Vec3.sum(end, properties.position); + } + if ((numDimensions === 1) && mask.z) { + start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: -10000 + }); + start = Vec3.sum(start, properties.position); + end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: 10000 + }); + end = Vec3.sum(end, properties.position); + } + if (numDimensions === 1) { + if (mask.x === 1) { + planeNormal = { + x: 0, + y: 1, + z: 0 + }; + } else if (mask.y === 1) { + planeNormal = { + x: 1, + y: 0, + z: 0 + }; + } else { + planeNormal = { + x: 0, + y: 1, + z: 0 + }; + } + } else if (numDimensions === 2) { + if (mask.x === 0) { + planeNormal = { + x: 1, + y: 0, + z: 0 + }; + } else if (mask.y === 0) { + planeNormal = { + x: 0, + y: 1, + z: 0 + }; + } else { + planeNormal = { + x: 0, + y: 0, + z: 1 + }; + } + } + + planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); + lastPick = rayPlaneIntersection(pickRay, + pickRayPosition, + planeNormal); + + var planeNormal3D = { + x: 0, + y: 0, + z: 0 + }; + if (directionFor3DStretch) { + lastPick3D = rayPlaneIntersection(pickRay, + pickRayPosition3D, + planeNormal3D); + distanceFor3DStretch = Vec3.length(Vec3.subtract(pickRayPosition3D, pickRay.origin)); + } + + that.setGrabberTranslateVisible(false); + that.setGrabberRotateVisible(false); + that.setGrabberScaleVisible(directionEnum === STRETCH_DIRECTION.ALL); + that.setGrabberStretchXVisible(directionEnum === STRETCH_DIRECTION.X); + that.setGrabberStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); + that.setGrabberStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); + that.setGrabberClonerVisible(false); + + SelectionManager.saveProperties(); + }; + + var onEnd = function(event, reason) { + pushCommandForSelections(); + }; + + var onMove = function(event) { + var proportional = (spaceMode === SPACE_WORLD) || event.isShifted || directionEnum === STRETCH_DIRECTION.ALL; + + var position, dimensions, rotation; + if (spaceMode === SPACE_LOCAL) { + position = SelectionManager.localPosition; + dimensions = SelectionManager.localDimensions; + rotation = SelectionManager.localRotation; + } else { + position = SelectionManager.worldPosition; + dimensions = SelectionManager.worldDimensions; + rotation = SelectionManager.worldRotation; + } + + var localDeltaPivot = deltaPivot; + var localSigns = signs; + + var pickRay = generalComputePickRay(event.x, event.y); + + // Are we using handControllers or Mouse - only relevant for 3D tools + var controllerPose = getControllerWorldLocation(activeHand, true); + var vector = null; + if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && + controllerPose.valid && that.triggered && directionFor3DStretch) { + localDeltaPivot = deltaPivot3D; + + newPick = pickRay.origin; + + vector = Vec3.subtract(newPick, lastPick3D); + + vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); + + if (distanceFor3DStretch > DISTANCE_INFLUENCE_THRESHOLD) { + // Range of Motion + vector = Vec3.multiply(distanceFor3DStretch , vector); + } + + localSigns = directionFor3DStretch; + + } else { + newPick = rayPlaneIntersection(pickRay, + pickRayPosition, + planeNormal); + vector = Vec3.subtract(newPick, lastPick); + + vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); + + vector = vec3Mult(mask, vector); + + } + + vector = grid.snapToSpacing(vector); + + var changeInDimensions = Vec3.multiply(-1, vec3Mult(localSigns, vector)); + var newDimensions; + if (proportional) { + var absX = Math.abs(changeInDimensions.x); + var absY = Math.abs(changeInDimensions.y); + var absZ = Math.abs(changeInDimensions.z); + var pctChange = 0; + if (absX > absY && absX > absZ) { + pctChange = changeInDimensions.x / initialProperties.dimensions.x; + pctChange = changeInDimensions.x / initialDimensions.x; + } else if (absY > absZ) { + pctChange = changeInDimensions.y / initialProperties.dimensions.y; + pctChange = changeInDimensions.y / initialDimensions.y; + } else { + pctChange = changeInDimensions.z / initialProperties.dimensions.z; + pctChange = changeInDimensions.z / initialDimensions.z; + } + pctChange += 1.0; + newDimensions = Vec3.multiply(pctChange, initialDimensions); + } else { + newDimensions = Vec3.sum(initialDimensions, changeInDimensions); + } + + newDimensions.x = Math.max(newDimensions.x, STRETCH_MINIMUM_DIMENSION); + newDimensions.y = Math.max(newDimensions.y, STRETCH_MINIMUM_DIMENSION); + newDimensions.z = Math.max(newDimensions.z, STRETCH_MINIMUM_DIMENSION); + + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(localDeltaPivot, changeInDimensions)); + if (directionEnum === STRETCH_DIRECTION.ALL) { + changeInPosition = { x:0, y:0, z:0 }; + } + var newPosition = Vec3.sum(initialPosition, changeInPosition); + + for (var i = 0; i < SelectionManager.selections.length; i++) { + Entities.editEntity(SelectionManager.selections[i], { + position: newPosition, + dimensions: newDimensions + }); + } + + var wantDebug = false; + if (wantDebug) { + print(stretchMode); + // Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + // Vec3.print(" oldPOS:", oldPOS); + // Vec3.print(" newPOS:", newPOS); + Vec3.print(" changeInDimensions:", changeInDimensions); + Vec3.print(" newDimensions:", newDimensions); + + Vec3.print(" changeInPosition:", changeInPosition); + Vec3.print(" newPosition:", newPosition); + } + + SelectionManager._update(); + };// End of onMove def + + return { + mode: stretchMode, + onBegin: onBegin, + onMove: onMove, + onEnd: onEnd + }; } - function addGrabberScaleTool(overlay, mode, direction) { - var pickNormal = null; - var lastPick = null; - var selectedGrabber = null; - addGrabberTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - pickNormal = { x:-1, y:1, z:-1 }; + function addGrabberStretchTool(overlay, mode, directionEnum) { + var directionVec, pivot, offset; + if (directionEnum === STRETCH_DIRECTION.X) { + directionVec = { x:-1, y:0, z:0 }; + pivot = { x:-1, y:0, z:0 }; + offset = { x:1, y:0, z:0 }; + } else if (directionEnum === STRETCH_DIRECTION.Y) { + directionVec = { x:0, y:-1, z:0 }; + pivot = { x:0, y:-1, z:0 }; + offset = { x:0, y:1, z:0 }; + } else if (directionEnum === STRETCH_DIRECTION.Z) { + directionVec = { x:0, y:0, z:-1 }; + pivot = { x:0, y:0, z:-1 }; + offset = { x:0, y:0, z:1 }; + } + var tool = makeStretchTool(mode, directionEnum, directionVec, pivot, offset); + return addGrabberTool(overlay, tool); + } - if (direction === SCALE_DIRECTION.LBN) { - selectedGrabber = grabberScaleLBNCube; - } else if (direction === SCALE_DIRECTION.RBN) { - selectedGrabber = grabberScaleRBNCube; - } else if (direction === SCALE_DIRECTION.LBF) { - selectedGrabber = grabberScaleLBFCube; - } else if (direction === SCALE_DIRECTION.RBF) { - selectedGrabber = grabberScaleRBFCube; - } else if (direction === SCALE_DIRECTION.LTN) { - selectedGrabber = grabberScaleLTNCube; - } else if (direction === SCALE_DIRECTION.RTN) { - selectedGrabber = grabberScaleRTNCube; - } else if (direction === SCALE_DIRECTION.LTF) { - selectedGrabber = grabberScaleLTFCube; - } else if (direction === SCALE_DIRECTION.RTF) { - selectedGrabber = grabberScaleRTFCube; - } - Overlays.editOverlay(selectedGrabber, { color: GRABBER_SCALE_CUBE_SELECTED_COLOR }); - - lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); - - SelectionManager.saveProperties(); - - that.setGrabberTranslateVisible(false); - that.setGrabberRotateVisible(false); - that.setGrabberStretchVisible(false); - that.setGrabberScaleVisible(true); - - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } - } - } else { - duplicatedEntityIDs = null; - } - }, - onEnd: function(event, reason) { - Overlays.editOverlay(selectedGrabber, { color: GRABBER_SCALE_CUBE_IDLE_COLOR }); - pushCommandForSelections(duplicatedEntityIDs); - }, - onMove: function(event) { - pickRay = generalComputePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); - var vector = Vec3.subtract(newIntersection, lastPick); - - var projectionVector; - if (direction === SCALE_DIRECTION.LBN) { - projectionVector = { x:-1, y:-1, z:-1 }; - } else if (direction === SCALE_DIRECTION.RBN) { - projectionVector = { x:-1, y:-1, z:1 }; - } else if (direction === SCALE_DIRECTION.LBF) { - projectionVector = { x:1, y:-1, z:-1 }; - } else if (direction === SCALE_DIRECTION.RBF) { - projectionVector = { x:1, y:-1, z:1 }; - } else if (direction === SCALE_DIRECTION.LTN) { - projectionVector = { x:-1, y:1, z:-1 }; - } else if (direction === SCALE_DIRECTION.RTN) { - projectionVector = { x:-1, y:1, z:1 }; - } else if (direction === SCALE_DIRECTION.LTF) { - projectionVector = { x:1, y:1, z:-1 }; - } else if (direction === SCALE_DIRECTION.RTF) { - projectionVector = { x:1, y:1, z:1 }; - } - - var dotVector = Vec3.dot(vector, projectionVector); - vector = Vec3.multiply(dotVector, projectionVector); - vector = grid.snapToGrid(vector); - - var wantDebug = false; - if (wantDebug) { - print("translateUpDown... "); - print(" event.y:" + event.y); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - // Vec3.print(" newPosition:", newPosition); - } - - for (var i = 0; i < SelectionManager.selections.length; i++) { - var id = SelectionManager.selections[i]; - var properties = SelectionManager.savedProperties[id]; - var newPosition = Vec3.sum(properties.position, vector); - var difference = Vec3.subtract(newPosition, SelectionManager.worldPosition); - var differenceAvg = (difference.x + difference.y + difference.z) / 3; - var newDimensionsX = properties.dimensions.x + differenceAvg; - var newDimensionsY = properties.dimensions.y + differenceAvg; - var newDimensionsZ = properties.dimensions.z + differenceAvg; - if (newDimensionsX < SCALE_MINIMUM_DIMENSION) { - var differenceBelow = SCALE_MINIMUM_DIMENSION - newDimensionsX; - newDimensionsX += differenceBelow; - newDimensionsY += differenceBelow; - newDimensionsZ += differenceBelow; - } - if (newDimensionsY < SCALE_MINIMUM_DIMENSION) { - var differenceBelow = SCALE_MINIMUM_DIMENSION - newDimensionsY; - newDimensionsX += differenceBelow; - newDimensionsY += differenceBelow; - newDimensionsZ += differenceBelow; - } - if (newDimensionsZ < SCALE_MINIMUM_DIMENSION) { - var differenceBelow = SCALE_MINIMUM_DIMENSION - newDimensionsZ; - newDimensionsX += differenceBelow; - newDimensionsY += differenceBelow; - newDimensionsZ += differenceBelow; - } - Entities.editEntity(id, { dimensions: { x:newDimensionsX, y:newDimensionsY, z:newDimensionsZ }}); - } - - SelectionManager._update(); - } - }); + function addGrabberScaleTool(overlay, mode, directionEnum) { + var directionVec, pivot, offset; + if (directionEnum === SCALE_DIRECTION.LBN) { + directionVec = { x:1, y:1, z:1 }; + pivot = { x:1, y:1, z:1 }; + offset = { x:-1, y:-1, z:-1 }; + } else if (directionEnum === SCALE_DIRECTION.RBN) { + directionVec = { x:1, y:1, z:-1 }; + pivot = { x:1, y:1, z:-1 }; + offset = { x:-1, y:-1, z:1 }; + } else if (directionEnum === SCALE_DIRECTION.LBF) { + directionVec = { x:-1, y:1, z:1 }; + pivot = { x:-1, y:1, z:1 }; + offset = { x:1, y:-1, z:-1 }; + } else if (directionEnum === SCALE_DIRECTION.RBF) { + directionVec = { x:-1, y:1, z:-1 }; + pivot = { x:-1, y:1, z:-1 }; + offset = { x:1, y:-1, z:1 }; + } else if (directionEnum === SCALE_DIRECTION.LTN) { + directionVec = { x:1, y:-1, z:1 }; + pivot = { x:1, y:-1, z:1 }; + offset = { x:-1, y:1, z:-1 }; + } else if (directionEnum === SCALE_DIRECTION.RTN) { + directionVec = { x:1, y:-1, z:-1 }; + pivot = { x:1, y:-1, z:-1 }; + offset = { x:-1, y:1, z:1 }; + } else if (directionEnum === SCALE_DIRECTION.LTF) { + directionVec = { x:-1, y:-1, z:1 }; + pivot = { x:-1, y:-1, z:1 }; + offset = { x:1, y:1, z:-1 }; + } else if (directionEnum === SCALE_DIRECTION.RTF) { + directionVec = { x:-1, y:-1, z:-1 }; + pivot = { x:-1, y:-1, z:-1 }; + offset = { x:1, y:1, z:1 }; + } + var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVec, pivot, offset); + return addGrabberTool(overlay, tool); } // FUNCTION: UPDATE ROTATION DEGREES OVERLAY @@ -970,9 +1131,9 @@ SelectionDisplay = (function() { }; if (reposition) { - var dPos = Vec3.subtract(initialProperties.position, initialPosition); + var dPos = Vec3.subtract(initialProperties.position, SelectionManager.worldPosition); dPos = Vec3.multiplyQbyV(rotationChange, dPos); - newProperties.position = Vec3.sum(initialPosition, dPos); + newProperties.position = Vec3.sum(SelectionManager.worldPosition, dPos); } Entities.editEntity(entityID, newProperties); @@ -980,8 +1141,8 @@ SelectionDisplay = (function() { } function addGrabberRotateTool(overlay, mode, direction) { - var initialPosition = SelectionManager.worldPosition; var selectedGrabber = null; + var worldRotation = null; addGrabberTool(overlay, { mode: mode, onBegin: function(event, pickRay, pickResult) { @@ -993,29 +1154,37 @@ SelectionDisplay = (function() { that.setGrabberRotateRollVisible(direction === ROTATE_DIRECTION.ROLL); that.setGrabberStretchVisible(false); that.setGrabberScaleVisible(false); - - initialPosition = SelectionManager.worldPosition; + that.setGrabberClonerVisible(false); if (direction === ROTATE_DIRECTION.PITCH) { rotationNormal = { x: 1, y: 0, z: 0 }; + worldRotation = worldRotationY; selectedGrabber = grabberRotatePitchRing; } else if (direction === ROTATE_DIRECTION.YAW) { rotationNormal = { x: 0, y: 1, z: 0 }; + worldRotation = worldRotationZ; selectedGrabber = grabberRotateYawRing; } else if (direction === ROTATE_DIRECTION.ROLL) { rotationNormal = { x: 0, y: 0, z: 1 }; + worldRotation = worldRotationX; selectedGrabber = grabberRotateRollRing; } Overlays.editOverlay(selectedGrabber, { hasTickMarks: true }); - var rotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; + var rotation = SelectionManager.worldRotation; rotationNormal = Vec3.multiplyQbyV(rotation, rotationNormal); var rotCenter = SelectionManager.worldPosition; Overlays.editOverlay(rotationDegreesDisplay, { visible: true }); - Overlays.editOverlay(grabberRotateCurrentRing, { visible: true }); + Overlays.editOverlay(grabberRotateCurrentRing, { + position: rotCenter, + rotation: worldRotation, + startAt: 0, + endAt: 0, + visible: true + }); updateRotationDegreesOverlay(0, direction, rotCenter); // editOverlays may not have committed rotation changes. @@ -1051,15 +1220,6 @@ SelectionDisplay = (function() { updateSelectionsRotation(rotChange); updateRotationDegreesOverlay(-angleFromZero, direction, rotCenter); - var worldRotation; - if (direction === ROTATE_DIRECTION.PITCH) { - worldRotation = worldRotationY; - } else if (direction === ROTATE_DIRECTION.YAW) { - worldRotation = worldRotationZ; - } else if (direction === ROTATE_DIRECTION.ROLL) { - worldRotation = worldRotationX; - } - var startAtCurrent = 0; var endAtCurrent = angleFromZero; if (angleFromZero < 0) { @@ -1067,11 +1227,13 @@ SelectionDisplay = (function() { endAtCurrent = 360; } Overlays.editOverlay(grabberRotateCurrentRing, { - position: rotCenter, - rotation: worldRotation, startAt: startAtCurrent, endAt: endAtCurrent }); + // not sure why but this seems to be needed to fix an reverse rotation for yaw ring only + if (direction === ROTATE_DIRECTION.YAW) { + Overlays.editOverlay(grabberRotateCurrentRing, { rotation: worldRotationZ }); + } } } }); @@ -1142,89 +1304,9 @@ SelectionDisplay = (function() { }); */ - that.updateHandles(); + that.updateGrabbers(); }; - // FUNCTION: UPDATE HANDLE POSITION ROTATION - that.updateHandlePositionRotation = function() { - if (SelectionManager.hasSelection()) { - var worldPosition = SelectionManager.worldPosition; - var worldRotation = Entities.getEntityProperties(SelectionManager.selections[0]).rotation; - - var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); - worldRotationX = Quat.multiply(worldRotation, localRotationX); - var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); - worldRotationY = Quat.multiply(worldRotation, localRotationY); - var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); - worldRotationZ = Quat.multiply(worldRotation, localRotationZ); - - var cylinderXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET, y:0, z:0 })); - Overlays.editOverlay(grabberTranslateXCylinder, { position: cylinderXPos, rotation:worldRotationX }); - var coneXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_TRANSLATE_ARROW_CONE_OFFSET, y:0, z:0 })); - Overlays.editOverlay(grabberTranslateXCone, { position: coneXPos, rotation:worldRotationX }); - var stretchXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_STRETCH_SPHERE_OFFSET, y:0, z:0 })); - Overlays.editOverlay(grabberStretchXSphere, { position: stretchXPos }); - Overlays.editOverlay(grabberStretchXPanel, { position: stretchXPos, rotation:worldRotationY }); - - var cylinderYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET, z:0 })); - Overlays.editOverlay(grabberTranslateYCylinder, { position: cylinderYPos, rotation:worldRotationY }); - var coneYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CONE_OFFSET, z:0 })); - Overlays.editOverlay(grabberTranslateYCone, { position: coneYPos, rotation:worldRotationY }); - var stretchYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_STRETCH_SPHERE_OFFSET, z:0 })); - Overlays.editOverlay(grabberStretchYSphere, { position: stretchYPos }); - Overlays.editOverlay(grabberStretchYPanel, { position: stretchYPos, rotation:worldRotationZ }); - - var cylinderZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET })); - Overlays.editOverlay(grabberTranslateZCylinder, { position: cylinderZPos, rotation:worldRotationZ }); - var coneZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CONE_OFFSET })); - Overlays.editOverlay(grabberTranslateZCone, { position: coneZPos, rotation:worldRotationZ }); - var stretchZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_STRETCH_SPHERE_OFFSET })); - Overlays.editOverlay(grabberStretchZSphere, { position: stretchZPos }); - Overlays.editOverlay(grabberStretchZPanel, { position: stretchZPos, rotation:worldRotationX }); - - if (!isActiveTool(grabberRotatePitchRing)) { - Overlays.editOverlay(grabberRotatePitchRing, { position: SelectionManager.worldPosition, rotation: worldRotationY }); - } - if (!isActiveTool(grabberRotateYawRing)) { - Overlays.editOverlay(grabberRotateYawRing, { position: SelectionManager.worldPosition, rotation: worldRotationZ }); - } - if (!isActiveTool(grabberRotateRollRing)) { - Overlays.editOverlay(grabberRotateRollRing, { position: SelectionManager.worldPosition, rotation: worldRotationX }); - } - - var grabberScaleLBNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleLBNCube, { position:grabberScaleLBNCubePos }); - var grabberScaleRBNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleRBNCube, { position:grabberScaleRBNCubePos }); - var grabberScaleLBFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleLBFCube, { position:grabberScaleLBFCubePos }); - var grabberScaleRBFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:-GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleRBFCube, { position:grabberScaleRBFCubePos }); - var grabberScaleLTNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleLTNCube, { position:grabberScaleLTNCubePos }); - var grabberScaleRTNCubePos = Vec3.sum(worldPosition, { x:-GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleRTNCube, { position:grabberScaleRTNCubePos }); - var grabberScaleLTFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:-GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleLTFCube, { position:grabberScaleLTFCubePos }); - var grabberScaleRTFCubePos = Vec3.sum(worldPosition, { x:GRABBER_SCALE_CUBE_OFFSET, y:GRABBER_SCALE_CUBE_OFFSET, z:GRABBER_SCALE_CUBE_OFFSET }); - Overlays.editOverlay(grabberScaleRTFCube, { position:grabberScaleRTFCubePos }); - - Overlays.editOverlay(grabberScaleTREdge, { start: grabberScaleRTNCubePos, end:grabberScaleRTFCubePos }); - Overlays.editOverlay(grabberScaleTLEdge, { start: grabberScaleLTNCubePos, end:grabberScaleLTFCubePos }); - Overlays.editOverlay(grabberScaleTFEdge, { start: grabberScaleLTFCubePos, end:grabberScaleRTFCubePos }); - Overlays.editOverlay(grabberScaleTNEdge, { start: grabberScaleLTNCubePos, end:grabberScaleRTNCubePos }); - Overlays.editOverlay(grabberScaleBREdge, { start: grabberScaleRBNCubePos, end:grabberScaleRBFCubePos }); - Overlays.editOverlay(grabberScaleBLEdge, { start: grabberScaleLBNCubePos, end:grabberScaleLBFCubePos }); - Overlays.editOverlay(grabberScaleBFEdge, { start: grabberScaleLBFCubePos, end:grabberScaleRBFCubePos }); - Overlays.editOverlay(grabberScaleBNEdge, { start: grabberScaleLBNCubePos, end:grabberScaleRBNCubePos }); - Overlays.editOverlay(grabberScaleNREdge, { start: grabberScaleRTNCubePos, end:grabberScaleRBNCubePos }); - Overlays.editOverlay(grabberScaleNLEdge, { start: grabberScaleLTNCubePos, end:grabberScaleLBNCubePos }); - Overlays.editOverlay(grabberScaleFREdge, { start: grabberScaleRTFCubePos, end:grabberScaleRBFCubePos }); - Overlays.editOverlay(grabberScaleFLEdge, { start: grabberScaleLTFCubePos, end:grabberScaleLBFCubePos }); - } - }; - Script.update.connect(that.updateHandlePositionRotation); - // FUNCTION: SET SPACE MODE that.setSpaceMode = function(newSpaceMode) { var wantDebug = false; @@ -1237,7 +1319,7 @@ SelectionDisplay = (function() { print(" Updating SpaceMode From: " + spaceMode + " To: " + newSpaceMode); } spaceMode = newSpaceMode; - that.updateHandles(); + that.updateGrabbers(); } else if (wantDebug) { print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + spaceMode + " DesiredMode: " + newSpaceMode); } @@ -1246,23 +1328,243 @@ SelectionDisplay = (function() { } }; - // FUNCTION: UPDATE HANDLES - that.updateHandles = function() { + // FUNCTION: UPDATE GRABBERS + that.updateGrabbers = function() { var wantDebug = false; if (wantDebug) { - print("======> Update Handles ======="); + print("======> Update Grabbers ======="); print(" Selections Count: " + SelectionManager.selections.length); print(" SpaceMode: " + spaceMode); print(" DisplayMode: " + getMode()); } + if (SelectionManager.selections.length === 0) { that.setOverlaysVisible(false); return; } - var isSingleSelection = (SelectionManager.selections.length === 1); - if (isSingleSelection) { - }// end of isSingleSelection + if (SelectionManager.hasSelection()) { + var worldPosition = SelectionManager.worldPosition; + var worldRotation = SelectionManager.worldRotation; + var worldDimensions = SelectionManager.worldDimensions; + + var worldDimensionsX = worldDimensions.x; + var worldDimensionsY = worldDimensions.y; + var worldDimensionsZ = worldDimensions.z; + var dimensionAverage = (worldDimensionsX + worldDimensionsY + worldDimensionsZ) / 3; + + var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); + worldRotationX = Quat.multiply(worldRotation, localRotationX); + var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); + worldRotationY = Quat.multiply(worldRotation, localRotationY); + var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); + worldRotationZ = Quat.multiply(worldRotation, localRotationZ); + + var arrowCylinderDimension = dimensionAverage * GRABBER_TRANSLATE_ARROW_CYLINDER_DIMENSION_MULTIPLE; + var arrowCylinderDimensions = { x:arrowCylinderDimension, y:arrowCylinderDimension * GRABBER_TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE, z:arrowCylinderDimension }; + var arrowConeDimension = dimensionAverage * GRABBER_TRANSLATE_ARROW_CONE_DIMENSION_MULTIPLE; + var arrowConeDimensions = { x:arrowConeDimension, y:arrowConeDimension, z:arrowConeDimension }; + var cylinderXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * dimensionAverage, y:0, z:0 })); + Overlays.editOverlay(grabberTranslateXCylinder, { + position: cylinderXPos, + rotation: worldRotationX, + dimensions: arrowCylinderDimensions + }); + var cylinderXDiff = Vec3.subtract(cylinderXPos, worldPosition); + var coneXPos = Vec3.sum(cylinderXPos, Vec3.multiply(Vec3.normalize(cylinderXDiff), arrowCylinderDimensions.y * 0.83)); + Overlays.editOverlay(grabberTranslateXCone, { + position: coneXPos, + rotation: worldRotationX, + dimensions: arrowConeDimensions + }); + var cylinderYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * dimensionAverage, z:0 })); + Overlays.editOverlay(grabberTranslateYCylinder, { + position: cylinderYPos, + rotation: worldRotationY, + dimensions: arrowCylinderDimensions + }); + var cylinderYDiff = Vec3.subtract(cylinderYPos, worldPosition); + var coneYPos = Vec3.sum(cylinderYPos, Vec3.multiply(Vec3.normalize(cylinderYDiff), arrowCylinderDimensions.y * 0.83)); + Overlays.editOverlay(grabberTranslateYCone, { + position: coneYPos, + rotation: worldRotationY, + dimensions: arrowConeDimensions + }); + var cylinderZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * dimensionAverage })); + Overlays.editOverlay(grabberTranslateZCylinder, { + position: cylinderZPos, + rotation: worldRotationZ, + dimensions: arrowCylinderDimensions + }); + var cylinderZDiff = Vec3.subtract(cylinderZPos, worldPosition); + var coneZPos = Vec3.sum(cylinderZPos, Vec3.multiply(Vec3.normalize(cylinderZDiff), arrowCylinderDimensions.y * 0.83)); + Overlays.editOverlay(grabberTranslateZCone, { + position: coneZPos, + rotation: worldRotationZ, + dimensions: arrowConeDimensions + }); + + var grabberScaleCubeOffsetX = GRABBER_SCALE_CUBE_OFFSET * worldDimensionsX; + var grabberScaleCubeOffsetY = GRABBER_SCALE_CUBE_OFFSET * worldDimensionsY; + var grabberScaleCubeOffsetZ = GRABBER_SCALE_CUBE_OFFSET * worldDimensionsZ; + var scaleDimension = dimensionAverage * GRABBER_SCALE_CUBE_DIMENSION_MULTIPLE; + var scaleDimensions = { x:scaleDimension, y:scaleDimension, z:scaleDimension }; + var grabberScaleLBNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleLBNCube, { + position: grabberScaleLBNCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleRBNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleRBNCube, { + position: grabberScaleRBNCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleLBFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleLBFCube, { + position: grabberScaleLBFCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleRBFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleRBFCube, { + position: grabberScaleRBFCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleLTNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleLTNCube, { + position: grabberScaleLTNCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleRTNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleRTNCube, { + position: grabberScaleRTNCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleLTFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleLTFCube, { + position: grabberScaleLTFCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + var grabberScaleRTFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + Overlays.editOverlay(grabberScaleRTFCube, { + position: grabberScaleRTFCubePos, + rotation: worldRotation, + dimensions: scaleDimensions + }); + + Overlays.editOverlay(grabberScaleTREdge, { start: grabberScaleRTNCubePos, end: grabberScaleRTFCubePos }); + Overlays.editOverlay(grabberScaleTLEdge, { start: grabberScaleLTNCubePos, end: grabberScaleLTFCubePos }); + Overlays.editOverlay(grabberScaleTFEdge, { start: grabberScaleLTFCubePos, end: grabberScaleRTFCubePos }); + Overlays.editOverlay(grabberScaleTNEdge, { start: grabberScaleLTNCubePos, end: grabberScaleRTNCubePos }); + Overlays.editOverlay(grabberScaleBREdge, { start: grabberScaleRBNCubePos, end: grabberScaleRBFCubePos }); + Overlays.editOverlay(grabberScaleBLEdge, { start: grabberScaleLBNCubePos, end: grabberScaleLBFCubePos }); + Overlays.editOverlay(grabberScaleBFEdge, { start: grabberScaleLBFCubePos, end: grabberScaleRBFCubePos }); + Overlays.editOverlay(grabberScaleBNEdge, { start: grabberScaleLBNCubePos, end: grabberScaleRBNCubePos }); + Overlays.editOverlay(grabberScaleNREdge, { start: grabberScaleRTNCubePos, end: grabberScaleRBNCubePos }); + Overlays.editOverlay(grabberScaleNLEdge, { start: grabberScaleLTNCubePos, end: grabberScaleLBNCubePos }); + Overlays.editOverlay(grabberScaleFREdge, { start: grabberScaleRTFCubePos, end: grabberScaleRBFCubePos }); + Overlays.editOverlay(grabberScaleFLEdge, { start: grabberScaleLTFCubePos, end: grabberScaleLBFCubePos }); + + var stretchSphereDimension = dimensionAverage * GRABBER_STRETCH_SPHERE_DIMENSION_MULTIPLE; + var stretchSphereDimensions = { x:stretchSphereDimension, y:stretchSphereDimension, z:stretchSphereDimension }; + var stretchXPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:GRABBER_STRETCH_SPHERE_OFFSET * worldDimensionsX, y:0, z:0 })); + Overlays.editOverlay(grabberStretchXSphere, { + position: stretchXPos, + dimensions: stretchSphereDimensions + }); + var stretchPanelXDimensions = Vec3.subtract(grabberScaleLTFCubePos, grabberScaleRBFCubePos); + var tempY = Math.abs(stretchPanelXDimensions.y); + stretchPanelXDimensions.x = 0.01; + stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); + stretchPanelXDimensions.z = tempY; + Overlays.editOverlay(grabberStretchXPanel, { + position: stretchXPos, + rotation: worldRotationZ, + dimensions: stretchPanelXDimensions + }); + var stretchYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_STRETCH_SPHERE_OFFSET * worldDimensionsY, z:0 })); + Overlays.editOverlay(grabberStretchYSphere, { + position: stretchYPos, + dimensions: stretchSphereDimensions + }); + var stretchPanelYDimensions = Vec3.subtract(grabberScaleLTFCubePos, grabberScaleRTNCubePos); + var tempX = Math.abs(stretchPanelYDimensions.x); + stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); + stretchPanelYDimensions.y = 0.01; + stretchPanelYDimensions.z = tempX; + Overlays.editOverlay(grabberStretchYPanel, { + position: stretchYPos, + rotation: worldRotationY, + dimensions: stretchPanelYDimensions + }); + var stretchZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_STRETCH_SPHERE_OFFSET * worldDimensionsZ })); + Overlays.editOverlay(grabberStretchZSphere, { + position: stretchZPos, + dimensions: stretchSphereDimensions + }); + var stretchPanelZDimensions = Vec3.subtract(grabberScaleRTFCubePos, grabberScaleRBNCubePos); + var tempX = Math.abs(stretchPanelZDimensions.x); + stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); + stretchPanelZDimensions.y = tempX; + stretchPanelZDimensions.z = 0.01; + Overlays.editOverlay(grabberStretchZPanel, { + position: stretchZPos, + rotation: worldRotationX, + dimensions: stretchPanelZDimensions + }); + + var rotateDimension = dimensionAverage * GRABBER_ROTATE_RINGS_DIMENSION_MULTIPLE; + var rotateDimensions = { x:rotateDimension, y:rotateDimension, z:rotateDimension }; + if (!isActiveTool(grabberRotatePitchRing)) { + Overlays.editOverlay(grabberRotatePitchRing, { + position: SelectionManager.worldPosition, + rotation: worldRotationY, + dimensions: rotateDimensions + }); + } + if (!isActiveTool(grabberRotateYawRing)) { + Overlays.editOverlay(grabberRotateYawRing, { + position: SelectionManager.worldPosition, + rotation: worldRotationZ, + dimensions: rotateDimensions + }); + } + if (!isActiveTool(grabberRotateRollRing)) { + Overlays.editOverlay(grabberRotateRollRing, { + position: SelectionManager.worldPosition, + rotation: worldRotationX, + dimensions: rotateDimensions + }); + } + Overlays.editOverlay(grabberRotateCurrentRing, { dimensions: rotateDimensions }); + + var inModeRotate = isActiveTool(grabberRotatePitchRing) || isActiveTool(grabberRotateYawRing) || isActiveTool(grabberRotateRollRing); + var inModeTranslate = isActiveTool(grabberTranslateXCone) || isActiveTool(grabberTranslateXCylinder) || + isActiveTool(grabberTranslateYCone) || isActiveTool(grabberTranslateYCylinder) || + isActiveTool(grabberTranslateZCone) || isActiveTool(grabberTranslateZCylinder) || + isActiveTool(grabberCloner) || isActiveTool(selectionBox); + + Overlays.editOverlay(selectionBox, { + position: worldPosition, + rotation: worldRotation, + dimensions: worldDimensions, + visible: !inModeRotate + }); + + var grabberClonerOffset = { x:GRABBER_CLONER_OFFSET.x * worldDimensionsX, y:GRABBER_CLONER_OFFSET.y * worldDimensionsY, z:GRABBER_CLONER_OFFSET.z * worldDimensionsZ }; + var grabberClonerPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, grabberClonerOffset)); + Overlays.editOverlay(grabberCloner, { + position: grabberClonerPos, + rotation: worldRotation, + dimensions: scaleDimensions, + }); + } that.setGrabberTranslateXVisible(!activeTool || isActiveTool(grabberTranslateXCone) || isActiveTool(grabberTranslateXCylinder)); that.setGrabberTranslateYVisible(!activeTool || isActiveTool(grabberTranslateYCone) || isActiveTool(grabberTranslateYCylinder)); @@ -1275,9 +1577,10 @@ SelectionDisplay = (function() { that.setGrabberStretchZVisible(!activeTool || isActiveTool(grabberStretchZSphere)); that.setGrabberScaleVisible(!activeTool || isActiveTool(grabberScaleLBNCube) || isActiveTool(grabberScaleRBNCube) || isActiveTool(grabberScaleLBFCube) || isActiveTool(grabberScaleRBFCube) || isActiveTool(grabberScaleLTNCube) || isActiveTool(grabberScaleRTNCube) || isActiveTool(grabberScaleLTFCube) || isActiveTool(grabberScaleRTFCube)); + that.setGrabberClonerVisible(!activeTool || isActiveTool(grabberCloner)); if (wantDebug) { - print("====== Update Handles <======="); + print("====== Update Grabbers <======="); } }; @@ -1310,7 +1613,7 @@ SelectionDisplay = (function() { Overlays.editOverlay(grabberTranslateZCylinder, { visible: isVisible }); }; - // FUNCTION: SET GRABBER ROTATION VISIBLE + // FUNCTION: SET GRABBER ROTATE VISIBLE that.setGrabberRotateVisible = function(isVisible) { that.setGrabberRotatePitchVisible(isVisible); that.setGrabberRotateYawVisible(isVisible); @@ -1372,6 +1675,189 @@ SelectionDisplay = (function() { Overlays.editOverlay(grabberScaleFLEdge, { visible: isVisible }); }; + // FUNCTION: SET GRABBER CLONER VISIBLE + that.setGrabberClonerVisible = function(isVisible) { + Overlays.editOverlay(grabberCloner, { visible: isVisible }); + }; + + var initialXZPick = null; + var isConstrained = false; + var constrainMajorOnly = false; + var startPosition = null; + var duplicatedEntityIDs = null; + + // TOOL DEFINITION: TRANSLATE XZ TOOL + var translateXZTool = addGrabberTool(selectionBox, { + mode: 'TRANSLATE_XZ', + pickPlanePosition: { x: 0, y: 0, z: 0 }, + greatestDimension: 0.0, + startingDistance: 0.0, + startingElevation: 0.0, + onBegin: function(event, pickRay, pickResult, doClone) { + var wantDebug = false; + if (wantDebug) { + print("================== TRANSLATE_XZ(Beg) -> ======================="); + Vec3.print(" pickRay", pickRay); + Vec3.print(" pickRay.origin", pickRay.origin); + Vec3.print(" pickResult.intersection", pickResult.intersection); + } + + SelectionManager.saveProperties(); + + that.setGrabberTranslateVisible(false); + that.setGrabberRotateVisible(false); + that.setGrabberScaleVisible(false); + that.setGrabberStretchVisible(false); + that.setGrabberClonerVisible(false); + + startPosition = SelectionManager.worldPosition; + + translateXZTool.pickPlanePosition = pickResult.intersection; + translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, SelectionManager.worldDimensions.y), SelectionManager.worldDimensions.z); + translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); + translateXZTool.startingElevation = translateXZTool.elevation(pickRay.origin, translateXZTool.pickPlanePosition); + if (wantDebug) { + print(" longest dimension: " + translateXZTool.greatestDimension); + print(" starting distance: " + translateXZTool.startingDistance); + print(" starting elevation: " + translateXZTool.startingElevation); + } + + initialXZPick = rayPlaneIntersection(pickRay, translateXZTool.pickPlanePosition, { + x: 0, + y: 1, + z: 0 + }); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt || doClone) { + duplicatedEntityIDs = []; + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + if (!properties.locked) { + var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties + }); + } + } + } else { + duplicatedEntityIDs = null; + } + + isConstrained = false; + if (wantDebug) { + print("================== TRANSLATE_XZ(End) <- ======================="); + } + }, + onEnd: function(event, reason) { + pushCommandForSelections(duplicatedEntityIDs); + }, + elevation: function(origin, intersection) { + return (origin.y - intersection.y) / Vec3.distance(origin, intersection); + }, + onMove: function(event) { + var wantDebug = false; + pickRay = generalComputePickRay(event.x, event.y); + + var pick = rayPlaneIntersection2(pickRay, translateXZTool.pickPlanePosition, { + x: 0, + y: 1, + z: 0 + }); + + // If the pick ray doesn't hit the pick plane in this direction, do nothing. + // this will happen when someone drags across the horizon from the side they started on. + if (!pick) { + if (wantDebug) { + print(" "+ translateXZTool.mode + "Pick ray does not intersect XZ plane."); + } + + // EARLY EXIT--(Invalid ray detected.) + return; + } + + var vector = Vec3.subtract(pick, initialXZPick); + + // If the mouse is too close to the horizon of the pick plane, stop moving + var MIN_ELEVATION = 0.02; // largest dimension of object divided by distance to it + var elevation = translateXZTool.elevation(pickRay.origin, pick); + if (wantDebug) { + print("Start Elevation: " + translateXZTool.startingElevation + ", elevation: " + elevation); + } + if ((translateXZTool.startingElevation > 0.0 && elevation < MIN_ELEVATION) || + (translateXZTool.startingElevation < 0.0 && elevation > -MIN_ELEVATION)) { + if (wantDebug) { + print(" "+ translateXZTool.mode + " - too close to horizon!"); + } + + // EARLY EXIT--(Don't proceed past the reached limit.) + return; + } + + // If the angular size of the object is too small, stop moving + var MIN_ANGULAR_SIZE = 0.01; // Radians + if (translateXZTool.greatestDimension > 0) { + var angularSize = Math.atan(translateXZTool.greatestDimension / Vec3.distance(pickRay.origin, pick)); + if (wantDebug) { + print("Angular size = " + angularSize); + } + if (angularSize < MIN_ANGULAR_SIZE) { + return; + } + } + + // If shifted, constrain to one axis + if (event.isShifted) { + if (Math.abs(vector.x) > Math.abs(vector.z)) { + vector.z = 0; + } else { + vector.x = 0; + } + if (!isConstrained) { + isConstrained = true; + } + } else { + if (isConstrained) { + isConstrained = false; + } + } + + constrainMajorOnly = event.isControl; + var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, SelectionManager.worldDimensions)); + vector = Vec3.subtract( + grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), + cornerPosition); + + + for (var i = 0; i < SelectionManager.selections.length; i++) { + var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; + if (!properties) { + continue; + } + var newPosition = Vec3.sum(properties.position, { + x: vector.x, + y: 0, + z: vector.z + }); + Entities.editEntity(SelectionManager.selections[i], { + position: newPosition + }); + + if (wantDebug) { + print("translateXZ... "); + Vec3.print(" vector:", vector); + Vec3.print(" newPosition:", properties.position); + Vec3.print(" newPosition:", newPosition); + } + } + + SelectionManager._update(); + } + }); + addGrabberTranslateTool(grabberTranslateXCone, "TRANSLATE_X", TRANSLATE_DIRECTION.X); addGrabberTranslateTool(grabberTranslateXCylinder, "TRANSLATE_X", TRANSLATE_DIRECTION.X); addGrabberTranslateTool(grabberTranslateYCone, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); @@ -1383,9 +1869,9 @@ SelectionDisplay = (function() { addGrabberRotateTool(grabberRotateYawRing, "ROTATE_YAW", ROTATE_DIRECTION.YAW); addGrabberRotateTool(grabberRotateRollRing, "ROTATE_ROLL", ROTATE_DIRECTION.ROLL); - addGrabberStretchTool(grabberStretchXSphere, "STRETCH_X", TRANSLATE_DIRECTION.X); - addGrabberStretchTool(grabberStretchYSphere, "STRETCH_Y", TRANSLATE_DIRECTION.Y); - addGrabberStretchTool(grabberStretchZSphere, "STRETCH_Z", TRANSLATE_DIRECTION.Z); + addGrabberStretchTool(grabberStretchXSphere, "STRETCH_X", STRETCH_DIRECTION.X); + addGrabberStretchTool(grabberStretchYSphere, "STRETCH_Y", STRETCH_DIRECTION.Y); + addGrabberStretchTool(grabberStretchZSphere, "STRETCH_Z", STRETCH_DIRECTION.Z); addGrabberScaleTool(grabberScaleLBNCube, "SCALE_LBN", SCALE_DIRECTION.LBN); addGrabberScaleTool(grabberScaleRBNCube, "SCALE_RBN", SCALE_DIRECTION.RBN); @@ -1396,19 +1882,28 @@ SelectionDisplay = (function() { addGrabberScaleTool(grabberScaleLTFCube, "SCALE_LTF", SCALE_DIRECTION.LTF); addGrabberScaleTool(grabberScaleRTFCube, "SCALE_RTF", SCALE_DIRECTION.RTF); + // GRABBER TOOL: GRABBER CLONER + addGrabberTool(grabberCloner, { + mode: "CLONE", + onBegin: function(event, pickRay, pickResult) { + var doClone = true; + translateXZTool.onBegin(event,pickRay,pickResult,doClone); + }, + elevation: function (event) { + translateXZTool.elevation(event); + }, + + onEnd: function (event) { + translateXZTool.onEnd(event); + }, + + onMove: function (event) { + translateXZTool.onMove(event); + } + }); + // FUNCTION: CHECK MOVE that.checkMove = function() { - if (SelectionManager.hasSelection()) { - - // FIXME - this cause problems with editing in the entity properties window - // SelectionManager._update(); - - if (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || - !Quat.equal(Camera.getOrientation(), lastCameraOrientation)) { - - //that.updateRotationHandles(); - } - } }; that.checkControllerMove = function() { @@ -1583,4 +2078,4 @@ SelectionDisplay = (function() { Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); return that; -}()); +}()); \ No newline at end of file From d38d13cbb94d067f12d8fda6d4ff5f7ede15c95c Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Sun, 21 Jan 2018 23:47:45 -0800 Subject: [PATCH 063/381] Trying to address the overlays to not be visisble in 2nd camera --- interface/src/ui/overlays/ModelOverlay.cpp | 8 +++++--- interface/src/ui/overlays/OverlaysPayload.cpp | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 79a7df338b..34644735ed 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -87,7 +87,7 @@ void ModelOverlay::update(float deltatime) { if (_visibleDirty) { _visibleDirty = false; // don't show overlays in mirrors - _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_1); + _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_0 * getVisible()); } if (_drawInFrontDirty) { _drawInFrontDirty = false; @@ -121,8 +121,10 @@ void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::Scene } void ModelOverlay::setVisible(bool visible) { - Overlay::setVisible(visible); - _visibleDirty = true; + if (visible != getVisible()) { + Overlay::setVisible(visible); + _visibleDirty = true; + } } void ModelOverlay::setDrawInFront(bool drawInFront) { diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 30743964a4..c071c44d42 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -46,9 +46,9 @@ namespace render { } if (overlay->getVisible()) { - builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_1); // don't draw overlays in mirror + builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_0); // don't draw overlays in mirror } else { - builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_ALL); + builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_NONE); } return builder.build(); From 483074b11e2dfd80f144e74aa0287c623c7d401d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 17:59:01 +0100 Subject: [PATCH 064/381] jsdoc cmake fixes --- interface/CMakeLists.txt | 19 +++++++++---------- tools/CMakeLists.txt | 4 +++- tools/jsdoc/CMakeLists.txt | 14 ++++++++++++++ tools/jsdoc/plugins/hifi.js | 9 ++++++--- tools/jsdoc/plugins/hifiJSONExport.js | 4 +++- 5 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 tools/jsdoc/CMakeLists.txt diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fbc40f70c2..07788e5865 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -279,6 +279,9 @@ endif(UNIX) # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) +# require JSDoc to be build before interface is deployed (Console Auto-complete) +add_dependencies(${TARGET_NAME} jsdoc) + if (APPLE) # link in required OS X frameworks and include the right GL headers find_library(OpenGL OpenGL) @@ -299,6 +302,9 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/../Resources/auto-complete" ) # call the fixup_interface macro to add required bundling commands for installation @@ -313,6 +319,9 @@ else (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/resources/auto-complete" ) # link target to external libraries @@ -349,16 +358,6 @@ endif() add_bugsplat() -# generate the JSDoc JSON for the JSConsole auto-completer -add_custom_command(TARGET ${TARGET_NAME} #POST_BUILD - COMMAND npm install - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc -) -add_custom_command(TARGET ${TARGET_NAME} - COMMAND node_modules/.bin/jsdoc . -c config.json - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc -) - if (WIN32) set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 16446c5071..ac920d930d 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,6 +2,9 @@ add_subdirectory(scribe) set_target_properties(scribe PROPERTIES FOLDER "Tools") +add_subdirectory(jsdoc) +set_target_properties(jsdoc PROPERTIES FOLDER "Tools") + if (BUILD_TOOLS) add_subdirectory(udt-test) set_target_properties(udt-test PROPERTIES FOLDER "Tools") @@ -27,4 +30,3 @@ if (BUILD_TOOLS) add_subdirectory(auto-tester) set_target_properties(auto-tester PROPERTIES FOLDER "Tools") endif() - diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt new file mode 100644 index 0000000000..9a9883a6ad --- /dev/null +++ b/tools/jsdoc/CMakeLists.txt @@ -0,0 +1,14 @@ +set(TARGET_NAME jsdoc) + +add_custom_target(${TARGET_NAME}) + +SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) + +add_custom_command(TARGET ${TARGET_NAME} + COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH}) + WORKING_DIRECTORY ${JSDOC_WORKING_DIR} + COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" +) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index afa3285c37..c434ecc0d6 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -10,6 +10,8 @@ function endsWith(path, exts) { exports.handlers = { beforeParse: function(e) { + const pathTools = require('path'); + var rootFolder = pathTools.dirname(e.filename); console.log("Scanning hifi source for jsdoc comments..."); // directories to scan for jsdoc comments @@ -34,9 +36,10 @@ exports.handlers = { const fs = require('fs'); dirList.forEach(function (dir) { - var files = fs.readdirSync(dir) + var joinedDir = pathTools.join(rootFolder, dir); + var files = fs.readdirSync(joinedDir) files.forEach(function (file) { - var path = dir + "/" + file; + var path = pathTools.join(joinedDir, file); if (fs.lstatSync(path).isFile() && endsWith(path, exts)) { var data = fs.readFileSync(path, "utf8"); var reg = /(\/\*\*jsdoc(.|[\r\n])*?\*\/)/gm; @@ -48,7 +51,7 @@ exports.handlers = { }); }); - fs.writeFile("out/hifiJSDoc.js", e.source, function(err) { + fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.js"), e.source, function(err) { if (err) { return console.log(err); } diff --git a/tools/jsdoc/plugins/hifiJSONExport.js b/tools/jsdoc/plugins/hifiJSONExport.js index 7948fd2673..5531d3ff25 100644 --- a/tools/jsdoc/plugins/hifiJSONExport.js +++ b/tools/jsdoc/plugins/hifiJSONExport.js @@ -1,9 +1,11 @@ exports.handlers = { processingComplete: function(e) { + const pathTools = require('path'); + var rootFolder = pathTools.join(__dirname, '..'); var doclets = e.doclets.map(doclet => Object.assign({}, doclet)); const fs = require('fs'); doclets.map(doclet => {delete doclet.meta; delete doclet.comment}); - fs.writeFile("out/hifiJSDoc.json", JSON.stringify(doclets, null, 4), function(err) { + fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) { if (err) { return console.log(err); } From 58216b947c1de891a6b948000f9030412e0e2f09 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 18:54:24 +0100 Subject: [PATCH 065/381] removal of unnecessary JS export (for now) --- tools/jsdoc/plugins/hifi.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index c434ecc0d6..bb556814e8 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -50,13 +50,5 @@ exports.handlers = { } }); }); - - fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.js"), e.source, function(err) { - if (err) { - return console.log(err); - } - - console.log("The Hifi JSDoc JS was saved!"); - }); } }; From 4ff3c9e0468ca69daf67bc86016100ac238e9beb Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 19:00:45 +0100 Subject: [PATCH 066/381] fix jsdoc output folder --- tools/jsdoc/CMakeLists.txt | 3 ++- tools/jsdoc/plugins/hifiJSONExport.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 9a9883a6ad..265d8e9913 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -5,10 +5,11 @@ add_custom_target(${TARGET_NAME}) SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH}) + COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) diff --git a/tools/jsdoc/plugins/hifiJSONExport.js b/tools/jsdoc/plugins/hifiJSONExport.js index 5531d3ff25..cd14c9faad 100644 --- a/tools/jsdoc/plugins/hifiJSONExport.js +++ b/tools/jsdoc/plugins/hifiJSONExport.js @@ -1,11 +1,14 @@ exports.handlers = { processingComplete: function(e) { const pathTools = require('path'); - var rootFolder = pathTools.join(__dirname, '..'); + var outputFolder = pathTools.join(__dirname, '../out'); var doclets = e.doclets.map(doclet => Object.assign({}, doclet)); const fs = require('fs'); + if (!fs.existsSync(outputFolder)) { + fs.mkdirSync(outputFolder); + } doclets.map(doclet => {delete doclet.meta; delete doclet.comment}); - fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) { + fs.writeFile(pathTools.join(outputFolder, "hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) { if (err) { return console.log(err); } From f4e6606c8d407256686dd557a1bea82fc936ec5d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 19:01:13 +0100 Subject: [PATCH 067/381] fix MyAvatar JS API typo --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 58b49b61ff..086c76dd1a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -81,7 +81,7 @@ class MyAvatar : public Avatar { * and MyAvatar.customListenOrientation properties. * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position * of audio spatialization listener. - * @property customListenOreintation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation + * @property customListenOrientation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation * of the audio spatialization listener. * @property audioListenerModeHead {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener * around the avatar's head. From 5885569644b4290e29c58e9aaa16175b0f923d3e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 23 Jan 2018 01:29:52 +0100 Subject: [PATCH 068/381] fix UNIX jsdoc command order --- tools/jsdoc/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 265d8e9913..36c2af4265 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -9,7 +9,7 @@ file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) + COMMAND (npm --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) From 67effd1ad190ebdd0417591ac0e50fefe2031908 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 23 Jan 2018 02:15:31 +0100 Subject: [PATCH 069/381] create folder --- interface/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 07788e5865..130848fc08 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -302,6 +302,8 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/../Resources/auto-complete" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "$/../Resources/auto-complete" @@ -319,6 +321,8 @@ else (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/resources/auto-complete" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "$/resources/auto-complete" From 24692f13fdda4e3f6e0b36a6a871a7059592a196 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 23 Jan 2018 14:19:39 +0100 Subject: [PATCH 070/381] Added separate scribe function to evalGlobalLightingAlphaBlendedWithHaze to shorten shader C string --- libraries/render-utils/src/DeferredGlobalLight.slh | 6 ++++++ libraries/render-utils/src/model_normal_map.slf | 2 +- libraries/render-utils/src/model_translucent.slf | 2 +- libraries/render-utils/src/model_translucent_fade.slf | 2 +- libraries/render-utils/src/overlay3D_model_translucent.slf | 2 +- libraries/render-utils/src/simple_transparent_textured.slf | 2 +- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index fae645fdbc..51884ccbee 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -203,6 +203,12 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl return color; } +<@endfunc@> + +<@func declareEvalGlobalLightingAlphaBlendedWithHaze()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> vec3 evalGlobalLightingAlphaBlendedWithHaze( mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index bed85b4b15..b41e226e23 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -47,7 +47,7 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 761e702595..67a5651ab8 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index a2d271653c..316dae7aad 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> diff --git a/libraries/render-utils/src/overlay3D_model_translucent.slf b/libraries/render-utils/src/overlay3D_model_translucent.slf index 8dd3a81443..b66c114f4a 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent.slf @@ -11,7 +11,7 @@ // <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include graphics/Material.slh@> diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index b16b19c8b4..30c420233f 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -16,7 +16,7 @@ <@include DeferredBufferWrite.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> From 55abaf33fb95a5e5bafa7e8db3be3ad7c8d65e3a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 23 Jan 2018 17:35:01 +0100 Subject: [PATCH 071/381] Added shaders to support normal maps on translucent objects --- .../render-utils/src/RenderPipelines.cpp | 26 +++-- .../src/model_translucent_normal_map.slf | 91 ++++++++++++++++ ...e.slv => model_translucent_normal_map.slv} | 6 +- .../src/model_translucent_normal_map_fade.slf | 101 ++++++++++++++++++ .../src/simple_transparent_textured_fade.slf | 2 +- 5 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 libraries/render-utils/src/model_translucent_normal_map.slf rename libraries/render-utils/src/{model_translucent_fade.slv => model_translucent_normal_map.slv} (88%) create mode 100644 libraries/render-utils/src/model_translucent_normal_map_fade.slf diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 29efaf914a..1718a9b041 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -30,7 +30,7 @@ #include "model_lightmap_fade_vert.h" #include "model_lightmap_normal_map_fade_vert.h" #include "model_translucent_vert.h" -#include "model_translucent_fade_vert.h" +#include "model_translucent_normal_map_vert.h" #include "skin_model_fade_vert.h" #include "skin_model_normal_map_fade_vert.h" @@ -72,6 +72,7 @@ #include "model_lightmap_normal_specular_map_frag.h" #include "model_lightmap_specular_map_frag.h" #include "model_translucent_frag.h" +#include "model_translucent_normal_map_frag.h" #include "model_translucent_unlit_frag.h" #include "model_lightmap_fade_frag.h" @@ -79,6 +80,7 @@ #include "model_lightmap_normal_specular_map_fade_frag.h" #include "model_lightmap_specular_map_fade_frag.h" #include "model_translucent_fade_frag.h" +#include "model_translucent_normal_map_fade_frag.h" #include "model_translucent_unlit_fade_frag.h" #include "overlay3D_vert.h" @@ -191,7 +193,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); auto modelTranslucentVertex = gpu::Shader::createVertex(std::string(model_translucent_vert)); - auto modelTranslucentFadeVertex = gpu::Shader::createVertex(std::string(model_translucent_fade_vert)); + auto modelTranslucentNormalMapVertex = gpu::Shader::createVertex(std::string(model_translucent_normal_map_vert)); auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); @@ -220,6 +222,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); + auto modelTranslucentNormalMapPixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_frag)); auto modelTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_frag)); auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); @@ -238,6 +241,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_fade_frag)); auto modelShadowFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); auto modelTranslucentFadePixel = gpu::Shader::createPixel(std::string(model_translucent_fade_frag)); + auto modelTranslucentNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_fade_frag)); auto modelTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_fade_frag)); auto simpleFadePixel = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); auto simpleUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); @@ -307,13 +311,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleVertex, simpleTranslucentUnlitPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents(), - modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular(), modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular(), - modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap(), @@ -321,7 +325,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withTranslucent().withFade(), - modelTranslucentFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withTranslucent().withFade(), simpleFadeVertex, simpleTranslucentFadePixel, batchSetter, itemSetter); @@ -333,13 +337,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleFadeVertex, simpleTranslucentUnlitFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withFade(), - modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular().withFade(), modelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular().withFade(), - modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap().withFade(), @@ -405,26 +409,26 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents(), - skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular(), skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular(), - skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withFade(), skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withFade(), - skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular().withFade(), skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular().withFade(), - skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); // Depth-only addPipeline( diff --git a/libraries/render-utils/src/model_translucent_normal_map.slf b/libraries/render-utils/src/model_translucent_normal_map.slf new file mode 100644 index 0000000000..759007d93e --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map.slf @@ -0,0 +1,91 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_translucent_normal_map.frag +// fragment shader +// +// Created by Olivier Prat on 23/01/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include graphics/Material.slh@> + +<@include DeferredGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> + +<@include LightLocal.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec4 _worldPosition; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; +in float _alpha; + +out vec4 _fragColor; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + vec3 fragNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } + + _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + albedo, + fresnel, + metallic, + emissive, + surface, opacity, localLighting.rgb), + opacity); +} diff --git a/libraries/render-utils/src/model_translucent_fade.slv b/libraries/render-utils/src/model_translucent_normal_map.slv similarity index 88% rename from libraries/render-utils/src/model_translucent_fade.slv rename to libraries/render-utils/src/model_translucent_normal_map.slv index 3fb9ad2cb4..db824a3709 100644 --- a/libraries/render-utils/src/model_translucent_fade.slv +++ b/libraries/render-utils/src/model_translucent_normal_map.slv @@ -1,10 +1,10 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// model_translucent_fade.slv +// model_translucent_normal_map.slv // vertex shader // -// Created by Olivier Prat on 15/01/18. +// Created by Olivier Prat on 23/01/18. // Copyright 2018 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -25,6 +25,7 @@ out vec2 _texCoord1; out vec4 _position; out vec4 _worldPosition; out vec3 _normal; +out vec3 _tangent; out vec3 _color; void main(void) { @@ -41,4 +42,5 @@ void main(void) { <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> + <$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$> } diff --git a/libraries/render-utils/src/model_translucent_normal_map_fade.slf b/libraries/render-utils/src/model_translucent_normal_map_fade.slf new file mode 100644 index 0000000000..204b5ac56b --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map_fade.slf @@ -0,0 +1,101 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_translucent_normal_map_fade.frag +// fragment shader +// +// Created by Olivier Prat on 23/01/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include graphics/Material.slh@> + +<@include DeferredGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> + +<@include LightLocal.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +<@include Fade.slh@> +<$declareFadeFragment()$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; +in float _alpha; +in vec4 _worldPosition; + +out vec4 _fragColor; + +void main(void) { + vec3 fadeEmissive; + FadeObjectParams fadeParams; + + <$fetchFadeObjectParams(fadeParams)$> + applyFade(fadeParams, _worldPosition.xyz, fadeEmissive); + + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + // Lighting is done in world space + vec3 fragNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } + + _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + albedo, + fresnel, + metallic, + emissive+fadeEmissive, + surface, opacity, localLighting.rgb), + opacity); +} diff --git a/libraries/render-utils/src/simple_transparent_textured_fade.slf b/libraries/render-utils/src/simple_transparent_textured_fade.slf index ad260210a7..a8a5875a4b 100644 --- a/libraries/render-utils/src/simple_transparent_textured_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_fade.slf @@ -16,7 +16,7 @@ <@include DeferredBufferWrite.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> From 72ed5f6d91852e0463e69f5876ceb9deb81adaca Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 23 Jan 2018 13:40:31 -0500 Subject: [PATCH 072/381] [Case 4315] Fixes OK button hard to use within HMD mode (details below). * Shape Color picker dynamically updates entity color. * This removes the OK button which was hard to interact with in HMD mode. The user no longer needs to click the OK button to submit changes to the color; rather the color of the entity updates as the user manipulates the color wheel and/or slider. Known Issue/TODO(s): * Color Picker's Color Preview area has a bug where the original/starting color preview is black as opposed to reflecting the entity's color upon clicking the color picker. * Color preview restore color functionality isn't working. Clicking the initial color preview doesn't restore the color. * In HMD Mode: While interacting with the picker the user is able to control scrolling navigation for the edit menu. For example, when moving up or down to change the hue the edit menu behind the picker will scroll up and down correlatively. * In HMD Mode: When the picker is dismissed (user clicks outside the picker), the RGB fields for the color still receive input. Tested in Desktop & HMD Modes. Changes Committed: modified: scripts/system/html/js/entityProperties.js --- scripts/system/html/js/entityProperties.js | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 72092b66ca..3b6e207ccb 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1040,9 +1040,9 @@ function loaded() { elZoneAmbientLightURL.value = properties.ambientLight.ambientURL; // Haze - elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); + elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled'); - elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); + elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); elZoneHazeRange.value = properties.haze.hazeRange.toFixed(0); elZoneHazeColor.style.backgroundColor = "rgb(" + @@ -1308,15 +1308,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-color-control2').attr('active', 'true'); }, onHide: function(colpick) { $('#property-color-control2').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } })); @@ -1332,15 +1332,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-light-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-light-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } })); @@ -1387,15 +1387,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-text-text-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-text-text-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); $(el).attr('active', 'false'); emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); } @@ -1411,15 +1411,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-text-background-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-text-background-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); } })); @@ -1436,15 +1436,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-key-light-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-key-light-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); } })); @@ -1505,15 +1505,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-haze-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-haze-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('hazeColor', rgb.r, rgb.g, rgb.b, 'haze'); } })); @@ -1530,15 +1530,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('hazeGlareColor', rgb.r, rgb.g, rgb.b, 'haze'); } })); @@ -1572,15 +1572,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', + submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-skybox-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-skybox-color').attr('active', 'false'); }, - onSubmit: function(hsb, hex, rgb, el) { + onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); } })); From 4da7a1e65ae28a4927ceb5a20938625fc5ab5a18 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 23 Jan 2018 13:48:47 -0500 Subject: [PATCH 073/381] [Case 4315] ESLint pass on entityProperties.js. Re-ran after changes and issue count was 0. Changes Committed: modified: scripts/system/html/js/entityProperties.js --- scripts/system/html/js/entityProperties.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 3b6e207ccb..7008d0df66 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -653,16 +653,16 @@ function loaded() { var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); // Skybox - var elZoneSkyboxModeInherit = document.getElementById("property-zone-skybox-mode-inherit"); + var elZoneSkyboxModeInherit = document.getElementById("property-zone-skybox-mode-inherit"); var elZoneSkyboxModeDisabled = document.getElementById("property-zone-skybox-mode-disabled"); - var elZoneSkyboxModeEnabled = document.getElementById("property-zone-skybox-mode-enabled"); + var elZoneSkyboxModeEnabled = document.getElementById("property-zone-skybox-mode-enabled"); // Ambient light var elCopySkyboxURLToAmbientURL = document.getElementById("copy-skybox-url-to-ambient-url"); - var elZoneAmbientLightModeInherit = document.getElementById("property-zone-ambient-light-mode-inherit"); + var elZoneAmbientLightModeInherit = document.getElementById("property-zone-ambient-light-mode-inherit"); var elZoneAmbientLightModeDisabled = document.getElementById("property-zone-ambient-light-mode-disabled"); - var elZoneAmbientLightModeEnabled = document.getElementById("property-zone-ambient-light-mode-enabled"); + var elZoneAmbientLightModeEnabled = document.getElementById("property-zone-ambient-light-mode-enabled"); var elZoneAmbientLightIntensity = document.getElementById("property-zone-key-ambient-intensity"); var elZoneAmbientLightURL = document.getElementById("property-zone-key-ambient-url"); @@ -1013,9 +1013,9 @@ function loaded() { } else if (properties.type === "Zone") { // Key light - elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); + elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); elZoneKeyLightModeDisabled.checked = (properties.keyLightMode === 'disabled'); - elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); + elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; @@ -1027,14 +1027,14 @@ function loaded() { elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); // Skybox - elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); + elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); - elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); + elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); // Ambient light - elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); + elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); elZoneAmbientLightModeDisabled.checked = (properties.ambientLightMode === 'disabled'); - elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); + elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); elZoneAmbientLightIntensity.value = properties.ambientLight.ambientIntensity.toFixed(2); elZoneAmbientLightURL.value = properties.ambientLight.ambientURL; From 180be18178139099b9e42662d8c1e9282b438238 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 23 Jan 2018 14:02:31 -0500 Subject: [PATCH 074/381] [Case 4315] Fixes color picker issue in HMD mode (details below). Previously when in HMD mode, using the color picker to select a color via the gradient or hue areas would result in the page scrolling making it difficult to select the color or hue desired. This resolves the issue by turning off touch actions for the elements of the color picker that should have it when using the color picker. Tested in HMD & Desktop mode. Changes Committed: modified: scripts/system/html/css/colpick.css --- scripts/system/html/css/colpick.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/system/html/css/colpick.css b/scripts/system/html/css/colpick.css index 564f60cb3b..98417a5e9a 100644 --- a/scripts/system/html/css/colpick.css +++ b/scripts/system/html/css/colpick.css @@ -26,6 +26,7 @@ colpick Color Picker / colpick.com /*Color selection box with gradients*/ .colpick_color { position: absolute; + touch-action: none; left: 7px; top: 7px; width: 156px; @@ -84,6 +85,7 @@ colpick Color Picker / colpick.com /*Vertical hue bar*/ .colpick_hue { position: absolute; + touch-action: none; top: 6px; left: 175px; width: 19px; @@ -94,6 +96,7 @@ colpick Color Picker / colpick.com /*Hue bar sliding indicator*/ .colpick_hue_arrs { position: absolute; + touch-action: none; left: -8px; width: 35px; height: 7px; @@ -101,6 +104,7 @@ colpick Color Picker / colpick.com } .colpick_hue_larr { position:absolute; + touch-action: none; width: 0; height: 0; border-top: 6px solid transparent; @@ -109,6 +113,7 @@ colpick Color Picker / colpick.com } .colpick_hue_rarr { position:absolute; + touch-action: none; right:0; width: 0; height: 0; @@ -119,6 +124,7 @@ colpick Color Picker / colpick.com /*New color box*/ .colpick_new_color { position: absolute; + touch-action: none; left: 207px; top: 6px; width: 60px; @@ -129,6 +135,7 @@ colpick Color Picker / colpick.com /*Current color box*/ .colpick_current_color { position: absolute; + touch-action: none; left: 277px; top: 6px; width: 60px; @@ -139,6 +146,7 @@ colpick Color Picker / colpick.com /*Input field containers*/ .colpick_field, .colpick_hex_field { position: absolute; + touch-action: none; height: 20px; width: 60px; overflow:hidden; @@ -198,6 +206,7 @@ colpick Color Picker / colpick.com /*Text inputs*/ .colpick_field input, .colpick_hex_field input { position: absolute; + touch-action: none; right: 11px; margin: 0; padding: 0; @@ -217,6 +226,7 @@ colpick Color Picker / colpick.com /*Field up/down arrows*/ .colpick_field_arrs { position: absolute; + touch-action: none; top: 0; right: 0; width: 9px; @@ -225,6 +235,7 @@ colpick Color Picker / colpick.com } .colpick_field_uarr { position: absolute; + touch-action: none; top: 5px; width: 0; height: 0; @@ -234,6 +245,7 @@ colpick Color Picker / colpick.com } .colpick_field_darr { position: absolute; + touch-action: none; bottom:5px; width: 0; height: 0; @@ -244,6 +256,7 @@ colpick Color Picker / colpick.com /*Submit/Select button*/ .colpick_submit { position: absolute; + touch-action: none; left: 207px; top: 149px; width: 130px; From 79dbde6a6ff07189c351c8c25639887e3bdc7f13 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 23 Jan 2018 23:01:59 +0100 Subject: [PATCH 075/381] optional auto-complete build (requires NPM installed) --- cmake/macros/FindNPM.cmake | 14 ++++++++++++++ interface/CMakeLists.txt | 35 +++++++++++++++++++++++++---------- tools/CMakeLists.txt | 7 +++++-- tools/jsdoc/CMakeLists.txt | 4 +++- 4 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 cmake/macros/FindNPM.cmake diff --git a/cmake/macros/FindNPM.cmake b/cmake/macros/FindNPM.cmake new file mode 100644 index 0000000000..c66114f878 --- /dev/null +++ b/cmake/macros/FindNPM.cmake @@ -0,0 +1,14 @@ +# +# FindNPM.cmake +# cmake/macros +# +# Created by Thijs Wenker on 01/23/18. +# Copyright 2018 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html +# + +macro(find_npm) + find_program(NPM_EXECUTABLE "npm") +endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 130848fc08..b03a4637d0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -41,6 +41,8 @@ endif() file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") +find_npm() + # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) # Use .cpp and .h files as is. @@ -297,22 +299,38 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") + if (NPM_EXECUTABLE) + set(EXTRA_COPY_COMMANDS + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/../Resources/auto-complete" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/../Resources/auto-complete" + ) + endif() + # copy script files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/../Resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/../Resources/auto-complete" + + ${EXTRA_COPY_COMMANDS} ) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() else (APPLE) + if (NPM_EXECUTABLE) + set(EXTRA_COPY_COMMANDS + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/resources/auto-complete" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/resources/auto-complete" + ) + endif() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory @@ -321,11 +339,8 @@ else (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/resources/auto-complete" + + ${EXTRA_COPY_COMMANDS} ) # link target to external libraries diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index ac920d930d..53d7fc2836 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,8 +2,11 @@ add_subdirectory(scribe) set_target_properties(scribe PROPERTIES FOLDER "Tools") -add_subdirectory(jsdoc) -set_target_properties(jsdoc PROPERTIES FOLDER "Tools") +find_npm() +if (NPM_EXECUTABLE) + add_subdirectory(jsdoc) + set_target_properties(jsdoc PROPERTIES FOLDER "Tools") +endif() if (BUILD_TOOLS) add_subdirectory(udt-test) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 36c2af4265..b52d229130 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -2,6 +2,8 @@ set(TARGET_NAME jsdoc) add_custom_target(${TARGET_NAME}) +find_npm() + SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) @@ -9,7 +11,7 @@ file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (npm --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) + COMMAND (${NPM_EXECUTABLE} --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) From 6a035578017935304c77ac8821a5dc5d49f3d3cf Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 23 Jan 2018 14:44:10 -0800 Subject: [PATCH 076/381] don't override sensorToWorldMatrix --- interface/src/avatar/MyAvatar.cpp | 10 ++++++---- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e93b897013..2943cf7fd8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1790,7 +1790,7 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) { updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes } -void MyAvatar::initAnimGraph() { +void MyAvatar::initAnimGraph(bool updateBodySensorMat) { QUrl graphUrl; if (!_prefOverrideAnimGraphUrl.get().isEmpty()) { graphUrl = _prefOverrideAnimGraphUrl.get(); @@ -1803,8 +1803,10 @@ void MyAvatar::initAnimGraph() { _skeletonModel->getRig().initAnimGraph(graphUrl); _currentAnimGraphUrl.set(graphUrl); - _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. - updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + if (updateBodySensorMat) { + _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + } } void MyAvatar::destroyAnimGraph() { @@ -1819,7 +1821,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); - initAnimGraph(); + initAnimGraph(false); _isAnimatingScale = true; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 58b49b61ff..cdcd6f4607 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -730,7 +730,7 @@ private: void updatePosition(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void initHeadBones(); - void initAnimGraph(); + void initAnimGraph(bool updateBodySensorMat = true); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; From c46a1150006690ab8353bd888c0add5b55ec77cc Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 24 Jan 2018 01:45:53 +0100 Subject: [PATCH 077/381] attempt to fix ubuntu build --- tools/jsdoc/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index b52d229130..292523a813 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -4,14 +4,14 @@ add_custom_target(${TARGET_NAME}) find_npm() -SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) +set(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (${NPM_EXECUTABLE} --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) + COMMAND ${NPM_EXECUTABLE} --no-progress install && ${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR} WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) From 2cbcc28bd4c860a45e0e050ae959537c6b795a02 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 24 Jan 2018 10:44:04 -0800 Subject: [PATCH 078/381] only call init animGraph once --- interface/src/avatar/MyAvatar.cpp | 27 +++++++++++++++------------ interface/src/avatar/MyAvatar.h | 4 +++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1475860665..5f2cbf92b2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1785,12 +1785,10 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) { _currentAnimGraphUrl.set(url); _skeletonModel->getRig().initAnimGraph(url); - - _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. - updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded())); } -void MyAvatar::initAnimGraph(bool updateBodySensorMat) { +void MyAvatar::initAnimGraph() { QUrl graphUrl; if (!_prefOverrideAnimGraphUrl.get().isEmpty()) { graphUrl = _prefOverrideAnimGraphUrl.get(); @@ -1802,27 +1800,32 @@ void MyAvatar::initAnimGraph(bool updateBodySensorMat) { _skeletonModel->getRig().initAnimGraph(graphUrl); _currentAnimGraphUrl.set(graphUrl); - - if (updateBodySensorMat) { - _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. - updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes - } + connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded())); } void MyAvatar::destroyAnimGraph() { _skeletonModel->getRig().destroyAnimGraph(); } +void MyAvatar::animGraphLoaded() { + _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + _isAnimatingScale = true; + disconnect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded())); +} + void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { Avatar::postUpdate(deltaTime, scene); - if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) { + if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode() && _initHeadBones) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); - initAnimGraph(false); - _isAnimatingScale = true; + initAnimGraph(); + _initHeadBones = false; + } else if (!_skeletonModel->isLoaded()) { + _initHeadBones = true; } if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cdcd6f4607..6a9e0e6a38 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -566,6 +566,7 @@ public slots: void increaseSize(); void decreaseSize(); void resetSize(); + void animGraphLoaded(); void setGravity(float gravity); float getGravity(); @@ -730,7 +731,7 @@ private: void updatePosition(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void initHeadBones(); - void initAnimGraph(bool updateBodySensorMat = true); + void initAnimGraph(); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; @@ -808,6 +809,7 @@ private: bool _enableDebugDrawIKConstraints { false }; bool _enableDebugDrawIKChains { false }; bool _enableDebugDrawDetailedCollision { false }; + bool _initHeadBones { false }; AudioListenerMode _audioListenerMode; glm::vec3 _customListenPosition; From b7ba7862aa929a501a7fec542974d6ce3ebb4d5c Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 24 Jan 2018 11:18:42 -0800 Subject: [PATCH 079/381] give animGrapgh loader a high priority --- libraries/animation/src/AnimNodeLoader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 33f3d72756..8173845205 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -38,6 +38,8 @@ static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const Q static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f; + // called after children have been loaded // returns node on success, nullptr on failure. static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } @@ -653,6 +655,7 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) : { _resource = QSharedPointer::create(url); _resource->setSelf(_resource); + _resource->setLoadPriority(this, ANIM_GRAPH_LOAD_PRIORITY); connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone); connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError); _resource->ensureLoading(); From 6149630ccd52c4695de7a611f03e1731226a5e8f Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 24 Jan 2018 16:36:08 -0800 Subject: [PATCH 080/381] don't manually highlight tablet buttons from swipeview, since the tablet button highligh themselves --- .../resources/qml/hifi/tablet/TabletButton.qml | 1 - .../resources/qml/hifi/tablet/TabletHome.qml | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 4d443fb97c..5f49f9f10b 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -158,7 +158,6 @@ Item { gridView.currentIndex = buttonIndex tabletButton.isEntered = true; Tablet.playSound(TabletEnums.ButtonHover); - if (tabletButton.isActive) { tabletButton.state = "hover active state"; } else { diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 524bbf5f4d..88d8194c49 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -144,22 +144,7 @@ Item { bottomMargin: 0 } - function setButtonState(buttonIndex, buttonstate) { - if (buttonIndex < 0 || gridView.contentItem === undefined - || gridView.contentItem.children.length - 1 < buttonIndex) { - return; - } - var itemat = gridView.contentItem.children[buttonIndex].children[0]; - if (itemat.isActive) { - itemat.state = "active state"; - } else { - itemat.state = buttonstate; - } - } - onCurrentIndexChanged: { - setButtonState(previousGridIndex, "base state"); - setButtonState(currentIndex, "hover state"); previousGridIndex = currentIndex } From c73c0487a09938c925493b406340311e4096c0a8 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 24 Jan 2018 16:41:19 -0800 Subject: [PATCH 081/381] min diff --- interface/resources/qml/hifi/tablet/TabletButton.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 5f49f9f10b..4d443fb97c 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -158,6 +158,7 @@ Item { gridView.currentIndex = buttonIndex tabletButton.isEntered = true; Tablet.playSound(TabletEnums.ButtonHover); + if (tabletButton.isActive) { tabletButton.state = "hover active state"; } else { From ae6a95ec0d4f9f3aea4c3c54236e1220b14740b4 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 24 Jan 2018 18:07:35 -0800 Subject: [PATCH 082/381] fixing my bugs on vissibility --- interface/src/SecondaryCamera.cpp | 2 +- interface/src/ui/overlays/ModelOverlay.cpp | 2 +- libraries/render-utils/src/Model.h | 2 +- libraries/render/src/render/Item.h | 20 ++++++++++++++++---- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index cad4f7f1b5..9d5b6a44a5 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -205,7 +205,7 @@ public: void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { const auto cachedArg = task.addJob("SecondaryCamera"); - const auto items = task.addJob("FetchCullSort", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::VISIBLE_MASK_1, render::ItemKey::VISIBLE_MASK_1); assert(items.canCast()); if (!isDeferred) { task.addJob("Forward", items); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 367cb397d1..59d4521307 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -87,7 +87,7 @@ void ModelOverlay::update(float deltatime) { if (_visibleDirty) { _visibleDirty = false; // don't show overlays in mirrors - _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_0 * getVisible()); + _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_0); } if (_drawInFrontDirty) { _drawInFrontDirty = false; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5f58906e9f..d9845de9ed 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -389,7 +389,7 @@ protected: QUrl _url; bool _isVisible; - uint8_t _viewVisibilityMask { 0 }; + uint8_t _viewVisibilityMask { render::ItemKey::VISIBLE_MASK_ALL }; gpu::Buffers _blendedVertexBuffers; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 9ea8421f1a..18e626d005 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -96,6 +96,18 @@ public: Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } + Builder& withVisible(uint8_t maskIndex = NUM_VISIBLE_MASK_INDICES) { + if (maskIndex == NUM_VISIBLE_MASK_INDICES) { + _flags.reset(INVISIBLE0); + _flags.reset(INVISIBLE1); + _flags.reset(INVISIBLE2); + _flags.reset(INVISIBLE3); + } + else { + _flags.reset(INVISIBLE0 + maskIndex); + } + return (*this); + } Builder& withInvisible(uint8_t maskIndex = NUM_VISIBLE_MASK_INDICES) { if (maskIndex == NUM_VISIBLE_MASK_INDICES) { _flags.set(INVISIBLE0); @@ -109,16 +121,16 @@ public: } Builder& withViewVisibilityMask(uint8_t mask) { if (mask & render::ItemKey::VISIBLE_MASK_0) { - _flags.set(INVISIBLE0); + _flags.reset(INVISIBLE0); } if (mask & render::ItemKey::VISIBLE_MASK_1) { - _flags.set(INVISIBLE1); + _flags.reset(INVISIBLE1); } if (mask & render::ItemKey::VISIBLE_MASK_2) { - _flags.set(INVISIBLE2); + _flags.reset(INVISIBLE2); } if (mask & render::ItemKey::VISIBLE_MASK_3) { - _flags.set(INVISIBLE3); + _flags.reset(INVISIBLE3); } return (*this); } From 7ee560899222142202291afee62979926a9c3dd5 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Thu, 25 Jan 2018 12:02:26 -0800 Subject: [PATCH 083/381] initial first pass of exposing headless verification --- .../ui/overlays/ContextOverlayInterface.cpp | 192 ++++++++++-------- .../src/ui/overlays/ContextOverlayInterface.h | 30 +-- 2 files changed, 126 insertions(+), 96 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index d690880f99..3886d5ad10 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -168,7 +168,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL(PathUtils::resourcesPath() + "images/inspect-icon.png"); + _contextOverlay->setURL(PathUtils::resourcesUrl() + "images/inspect-icon.png"); _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } @@ -189,6 +189,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return false; } + bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); Setting::Handle _settingSwitch{ "commerce", true }; @@ -266,6 +267,112 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } +bool ContextOverlayInterface::getLastInspectedEntityWasValid() { + if (!_lastInspectedEntity.isNull() && !_lastInspectedValidEntity.isNull()) { + return _lastInspectedEntity == _lastInspectedValidEntity; + } + return false; +} + +void ContextOverlayInterface::requestEntityOwnershipVerification(const QUuid& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + _entityMarketplaceID = entityProperties.getMarketplaceID(); + if (!entityItemID.isNull() && _entityMarketplaceID.length() > 0) { + setLastInspectedEntity(entityItemID); + requestOwnershipVerification(); + } +} + +void ContextOverlayInterface::requestOwnershipVerification() { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); + + auto nodeList = DependencyManager::get(); + + if (entityProperties.getClientOnly()) { + if (entityProperties.verifyStaticCertificateProperties()) { + SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); + + if (entityServer) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); + QJsonObject request; + request["certificate_id"] = entityProperties.getCertificateID(); + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty"; + } + else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed'"; + } + else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { + qCDebug(entities) << "'transfer_status' is 'pending'"; + } + else { + QString ownerKey = jsonObject["transfer_recipient_key"].toString(); + + QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); + QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + + int certIDByteArraySize = certID.length(); + int textByteArraySize = text.length(); + int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(textByteArraySize); + challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(text); + challengeOwnershipPacket->write(nodeToChallengeByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); + + // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); + return; + } + else { + startChallengeOwnershipTimer(); + } + } + } + else { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << + "More info:" << networkReply->readAll(); + } + + networkReply->deleteLater(); + }); + } + else { + qCWarning(context_overlay) << "Couldn't get Entity Server!"; + } + } + else { + auto ledger = DependencyManager::get(); + _challengeOwnershipTimeoutTimer.stop(); + emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); + qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; + } + } +} + static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; void ContextOverlayInterface::openInspectionCertificate() { // lets open the tablet to the inspection certificate QML @@ -275,87 +382,7 @@ void ContextOverlayInterface::openInspectionCertificate() { _hmdScriptingInterface->openTablet(); setLastInspectedEntity(_currentEntityWithContextOverlay); - - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); - - auto nodeList = DependencyManager::get(); - - if (entityProperties.getClientOnly()) { - if (entityProperties.verifyStaticCertificateProperties()) { - SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); - - if (entityServer) { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); - QJsonObject request; - request["certificate_id"] = entityProperties.getCertificateID(); - networkRequest.setUrl(requestURL); - - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); - - if (networkReply->error() == QNetworkReply::NoError) { - if (!jsonObject["invalid_reason"].toString().isEmpty()) { - qCDebug(entities) << "invalid_reason not empty"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { - qCDebug(entities) << "'transfer_status' is 'failed'"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { - qCDebug(entities) << "'transfer_status' is 'pending'"; - } else { - QString ownerKey = jsonObject["transfer_recipient_key"].toString(); - - QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); - QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); - - int certIDByteArraySize = certID.length(); - int textByteArraySize = text.length(); - int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); - - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(textByteArraySize); - challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(text); - challengeOwnershipPacket->write(nodeToChallengeByteArray); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); - - // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); - return; - } else { - startChallengeOwnershipTimer(); - } - } - } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << - "More info:" << networkReply->readAll(); - } - - networkReply->deleteLater(); - }); - } else { - qCWarning(context_overlay) << "Couldn't get Entity Server!"; - } - } else { - auto ledger = DependencyManager::get(); - _challengeOwnershipTimeoutTimer.stop(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; - } - } + requestOwnershipVerification(); } } @@ -421,6 +448,7 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + _lastInspectedValidEntity = _lastInspectedEntity; } else { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 990a7fe599..389b377bd5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -33,24 +33,25 @@ /**jsdoc * @namespace ContextOverlay */ -class ContextOverlayInterface : public QObject, public Dependency { +class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT - Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) - Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) - Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) - QSharedPointer _entityScriptingInterface; + Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) + Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) + + QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; QSharedPointer _selectionScriptingInterface; - OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _contextOverlay { nullptr }; + OverlayID _contextOverlayID{ UNKNOWN_OVERLAY_ID }; + std::shared_ptr _contextOverlay{ nullptr }; public: - ContextOverlayInterface(); - Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } + Q_INVOKABLE bool getLastInspectedEntityWasValid(); + Q_INVOKABLE void requestEntityOwnershipVerification(const QUuid& entityID); void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } void setEnabled(bool enabled); @@ -61,7 +62,7 @@ public: signals: void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); -public slots: + public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); @@ -72,7 +73,7 @@ public slots: void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); bool contextOverlayFilterPassed(const EntityItemID& entityItemID); -private slots: + private slots: void handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: @@ -80,17 +81,18 @@ private: enum { MAX_SELECTION_COUNT = 16 }; - - bool _verboseLogging { true }; - bool _enabled { true }; + bool _verboseLogging{ true }; + bool _enabled{ true }; EntityItemID _currentEntityWithContextOverlay{}; EntityItemID _lastInspectedEntity{}; + EntityItemID _lastInspectedValidEntity{}; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; bool _isInMarketplaceInspectionMode { false }; void openInspectionCertificate(); + void requestOwnershipVerification(); void openMarketplace(); void enableEntityHighlight(const EntityItemID& entityItemID); void disableEntityHighlight(const EntityItemID& entityItemID); From c96e395a46df93b652a22d605f2b2ceb4bf68b64 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Jan 2018 12:13:32 -0800 Subject: [PATCH 084/381] fix warning --- libraries/entities/src/EntityEditFilters.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index a69a8ce7d1..f5bf699e02 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -271,7 +271,7 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { } } else if (wantsOriginalPropertiesValue.isArray()) { auto length = wantsOriginalPropertiesValue.property("length").toInteger(); - for (int i; i < length; i++) { + for (int i = 0; i < length; i++) { auto stringValue = wantsOriginalPropertiesValue.property(i).toString(); if (!stringValue.isEmpty()) { filterData.wantsOriginalProperties = true; @@ -305,7 +305,7 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { } } else if (wantsZonePropertiesValue.isArray()) { auto length = wantsZonePropertiesValue.property("length").toInteger(); - for (int i; i < length; i++) { + for (int i = 0; i < length; i++) { auto stringValue = wantsZonePropertiesValue.property(i).toString(); if (!stringValue.isEmpty()) { filterData.wantsZoneProperties = true; From 179c21acf4b3c35752dfd648de52c47700fea982 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 25 Jan 2018 22:46:30 +0100 Subject: [PATCH 085/381] jsdoc info on selected auto-complete entry --- interface/src/ui/JSConsole.cpp | 145 ++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 3100b47882..1ca1ac2842 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,21 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; const QString GUTTER_ERROR = "X"; +const QString JSDOC_LINE_SEPARATOR = "\r"; + +const QString JSDOC_STYLE = + ""; + const QString JSConsole::_consoleFileName { "about:console" }; const QString JSON_KEY = "entries"; @@ -50,7 +66,7 @@ QList _readLines(const QString& filename) { // TODO: check root["version"] return root[JSON_KEY].toVariant().toStringList(); } - + void _writeLines(const QString& filename, const QList& lines) { QFile file(filename); file.open(QFile::WriteOnly); @@ -62,6 +78,10 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } +QString _jsdocTypeToString(QJsonValue jsdocType) { + return jsdocType.toObject().value("names").toVariant().toStringList().join("/"); +} + void JSConsole::readAPI() { QFile file(PathUtils::resourcesPath() + "auto-complete/hifiJSDoc.json"); file.open(QFile::ReadOnly); @@ -102,7 +122,10 @@ QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { foreach(auto doc, _apiDocs) { auto object = doc.toObject(); auto scope = object.value("scope"); - if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty)) { + if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || + (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty && + object.value("kind").toString() != "typedef")) { + model->appendRow(getAutoCompleteItem(doc)); } } @@ -166,20 +189,119 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : } void JSConsole::insertCompletion(const QModelIndex& completion) { + auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); + auto kind = jsdocObject.value("kind").toString(); auto completionString = completion.data().toString(); - QTextCursor tc = _ui->promptTextEdit->textCursor(); + if (kind == "function") { + auto params = jsdocObject.value("params").toArray(); + // automatically add the parenthesis/parentheses for the functions + completionString += params.isEmpty() ? "()" : "("; + } + QTextCursor textCursor = _ui->promptTextEdit->textCursor(); int extra = completionString.length() - _completer->completionPrefix().length(); - tc.movePosition(QTextCursor::Left); - tc.movePosition(QTextCursor::EndOfWord); - tc.insertText(completionString.right(extra)); - _ui->promptTextEdit->setTextCursor(tc); + textCursor.movePosition(QTextCursor::Left); + textCursor.movePosition(QTextCursor::EndOfWord); + textCursor.insertText(completionString.right(extra)); + _ui->promptTextEdit->setTextCursor(textCursor); } void JSConsole::highlightedCompletion(const QModelIndex& completion) { - qDebug() << "Highlighted " << completion.data().toString(); auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); - - // qDebug() << "Highlighted data " << QJsonDocument(jsdocObject).toJson(QJsonDocument::Compact); + QString memberOf = ""; + if (!_completerModule.isEmpty()) { + memberOf = _completerModule + "."; + } + auto name = memberOf + "" + jsdocObject.value("name").toString() + ""; + auto description = jsdocObject.value("description").toString(); + auto examples = jsdocObject.value("examples").toArray(); + auto kind = jsdocObject.value("kind").toString(); + QString returnTypeText = ""; + + QString paramsTable = ""; + if (kind == "function") { + auto params = jsdocObject.value("params").toArray(); + auto returns = jsdocObject.value("returns"); + if (!returns.isUndefined()) { + returnTypeText = _jsdocTypeToString(jsdocObject.value("returns").toArray().at(0).toObject().value("type")) + " "; + } + name += "("; + if (!params.isEmpty()) { + bool hasDefaultParam = false; + bool hasOptionalParam = false; + bool firstItem = true; + foreach(auto param, params) { + auto paramObject = param.toObject(); + if (!hasOptionalParam && paramObject.value("optional").toBool(false)) { + hasOptionalParam = true; + name += "["; + } + if (!firstItem) { + name += ", "; + } else { + firstItem = false; + } + name += paramObject.value("name").toString(); + if (!hasDefaultParam && !paramObject.value("defaultvalue").isUndefined()) { + hasDefaultParam = true; + } + } + if (hasOptionalParam) { + name += "]"; + } + + paramsTable += ""; + if (hasDefaultParam) { + paramsTable += ""; + } + paramsTable += ""; + foreach(auto param, params) { + auto paramObject = param.toObject(); + paramsTable += ""; + } + + paramsTable += "
NameTypeDefaultDescription
" + paramObject.value("name").toString() + "" + + _jsdocTypeToString(paramObject.value("type")) + ""; + if (hasDefaultParam) { + paramsTable += paramObject.value("defaultvalue").toVariant().toString() + ""; + } + paramsTable += paramObject.value("description").toString() + "
"; + } + name += ")"; + } else if (!jsdocObject.value("type").isUndefined()){ + returnTypeText = _jsdocTypeToString(jsdocObject.value("type")) + " "; + } + auto popupText = JSDOC_STYLE + "" + returnTypeText + name + ""; + auto descriptionText = "

" + description.replace(JSDOC_LINE_SEPARATOR, "
") + "

"; + + popupText += descriptionText; + popupText += paramsTable; + auto returns = jsdocObject.value("returns"); + if (!returns.isUndefined()) { + foreach(auto returnEntry, returns.toArray()) { + auto returnsObject = returnEntry.toObject(); + auto returnsDescription = returnsObject.value("description").toString().replace(JSDOC_LINE_SEPARATOR, "
"); + popupText += "

Returns

" + returnsDescription + "

Type
" +
+                _jsdocTypeToString(returnsObject.value("type")) + "
"; + } + } + + if (!examples.isEmpty()) { + popupText += "

Examples

"; + foreach(auto example, examples) { + auto exampleText = example.toString(); + auto exampleLines = exampleText.split(JSDOC_LINE_SEPARATOR); + foreach(auto exampleLine, exampleLines) { + if (exampleLine.contains("")) { + popupText += exampleLine.replace("caption>", "h5>"); + } else { + popupText += "
" + exampleLine + "\n
"; + } + } + } + } + + QToolTip::showText(QPoint(_completer->popup()->pos().x() + _completer->popup()->width(), _completer->popup()->pos().y()), + popupText, _completer->popup()); } JSConsole::~JSConsole() { @@ -299,7 +421,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { case Qt::Key_Space: case Qt::Key_Enter: case Qt::Key_Return: - insertCompletion(_completer->currentIndex()); + insertCompletion(_completer->popup()->currentIndex()); _completer->popup()->hide(); return true; default: @@ -401,6 +523,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { cursorRect.setWidth(_completer->popup()->sizeHintForColumn(0) + _completer->popup()->verticalScrollBar()->sizeHint().width()); _completer->complete(cursorRect); + highlightedCompletion(_completer->popup()->currentIndex()); return false; } } From ce50380698285dde03ffccf639e795be51aeed9d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 25 Jan 2018 22:47:35 +0100 Subject: [PATCH 086/381] fix JSAPI typo --- libraries/entities/src/EntityScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 095a821482..9342f96e07 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -139,7 +139,7 @@ public slots: Q_INVOKABLE bool canRezTmpCertified(); /**jsdoc - * @function Entities.canWriteAsseets + * @function Entities.canWriteAssets * @return {bool} `true` if the DomainServer will allow this Node/Avatar to write to the asset server */ Q_INVOKABLE bool canWriteAssets(); From 2874a3d2cab9f46aa1dd9e4fbb1092f5029e7012 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 25 Jan 2018 23:39:32 +0100 Subject: [PATCH 087/381] fix android build (skip auto-complete when BUILD_TOOLS is FALSE) --- interface/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 96d04c784d..d0d89919d5 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -276,8 +276,10 @@ endif() # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) -# require JSDoc to be build before interface is deployed (Console Auto-complete) -add_dependencies(${TARGET_NAME} jsdoc) +if (BUILD_TOOLS) + # require JSDoc to be build before interface is deployed (Console Auto-complete) + add_dependencies(${TARGET_NAME} jsdoc) +endif() if (APPLE) # link in required OS X frameworks and include the right GL headers @@ -294,7 +296,7 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") - if (NPM_EXECUTABLE) + if (BUILD_TOOLS AND NPM_EXECUTABLE) set(EXTRA_COPY_COMMANDS COMMAND "${CMAKE_COMMAND}" -E make_directory "$/../Resources/auto-complete" @@ -317,7 +319,7 @@ if (APPLE) fixup_interface() else() - if (NPM_EXECUTABLE) + if (BUILD_TOOLS AND NPM_EXECUTABLE) set(EXTRA_COPY_COMMANDS COMMAND "${CMAKE_COMMAND}" -E make_directory "$/resources/auto-complete" From aea16fe071d1731f783d6f93482c1161e2d513c2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Jan 2018 15:01:29 -0800 Subject: [PATCH 088/381] add delete filter support --- libraries/entities/src/EntityEditFilters.cpp | 1 + libraries/entities/src/EntityTree.cpp | 18 ++++++++++++++++++ libraries/entities/src/EntityTree.h | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index f5bf699e02..4e48c671cb 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -245,6 +245,7 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { entitiesObject.setProperty("ADD_FILTER_TYPE", EntityTree::FilterType::Add); entitiesObject.setProperty("EDIT_FILTER_TYPE", EntityTree::FilterType::Edit); entitiesObject.setProperty("PHYSICS_FILTER_TYPE", EntityTree::FilterType::Physics); + entitiesObject.setProperty("ERASE_FILTER_TYPE", EntityTree::FilterType::Erase); global.setProperty("Entities", entitiesObject); filterData.filterFn = global.property("filter"); if (!filterData.filterFn.isFunction()) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9ab9f63245..629281749e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1928,6 +1928,24 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons #endif EntityItemID entityItemID(entityID); + + EntityItemPointer existingEntity; + + auto startLookup = usecTimestampNow(); + existingEntity = findEntityByEntityItemID(entityItemID); + auto endLookup = usecTimestampNow(); + _totalLookupTime += endLookup - startLookup; + + auto startFilter = usecTimestampNow(); + FilterType filterType = FilterType::Erase; + EntityItemProperties dummyProperties; + bool wasChanged = false; + + bool allowed = (sourceNode->isAllowedEditor()) || filterProperties(existingEntity, dummyProperties, dummyProperties, wasChanged, filterType); + auto endFilter = usecTimestampNow(); + + _totalFilterTime += endFilter - startFilter; + entityItemIDsToDelete << entityItemID; if (wantEditLogging() || wantTerseEditLogging()) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 11a747d624..ba786fbe98 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -57,7 +57,8 @@ public: enum FilterType { Add, Edit, - Physics + Physics, + Erase }; EntityTree(bool shouldReaverage = false); virtual ~EntityTree(); From 6192625942a34242688cb5e087b3fed99ed7e954 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Jan 2018 15:01:50 -0800 Subject: [PATCH 089/381] add delete filter support --- .../prevent-erase-in-zone-example.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js diff --git a/scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js b/scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js new file mode 100644 index 0000000000..356cc55079 --- /dev/null +++ b/scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js @@ -0,0 +1,24 @@ +// +// prevent-erase-in-zone-example.js +// +// +// Created by Brad Hefta-Gaub to use Entities on Jan. 25, 2018 +// Copyright 2018 High Fidelity, Inc. +// +// This sample entity edit filter script will keep prevent any entity inside the zone from being erased. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function filter(properties, type, originalProperties, zoneProperties) { + + if (type == Entities.ERASE_FILTER_TYPE) { + return false; + } + return properties; +} + +filter.wantsOriginalProperties = true; +filter.wantsZoneProperties = true; +filter; \ No newline at end of file From 7e5684c439abc1399963ce1e9866d57af8d8a309 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Thu, 25 Jan 2018 15:04:12 -0800 Subject: [PATCH 090/381] use signals --- .../ui/overlays/ContextOverlayInterface.cpp | 12 ++++------- .../src/ui/overlays/ContextOverlayInterface.h | 20 +++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3886d5ad10..069446494f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -267,13 +267,6 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } -bool ContextOverlayInterface::getLastInspectedEntityWasValid() { - if (!_lastInspectedEntity.isNull() && !_lastInspectedValidEntity.isNull()) { - return _lastInspectedEntity == _lastInspectedValidEntity; - } - return false; -} - void ContextOverlayInterface::requestEntityOwnershipVerification(const QUuid& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -368,6 +361,7 @@ void ContextOverlayInterface::requestOwnershipVerification() { auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); + emit ownershipVerificationFailed(_lastInspectedEntity); qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; } } @@ -424,6 +418,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() { connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); + emit ownershipVerificationFailed(_lastInspectedEntity); }); _challengeOwnershipTimeoutTimer.start(5000); @@ -448,8 +443,9 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); - _lastInspectedValidEntity = _lastInspectedEntity; + emit ownershipVerificationSuccess(_lastInspectedEntity); } else { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); + emit ownershipVerificationFailed(_lastInspectedEntity); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 389b377bd5..c96c5989a6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -36,21 +36,20 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT - Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) - Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) - Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) + Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) + Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) - QSharedPointer _entityScriptingInterface; + QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; QSharedPointer _selectionScriptingInterface; - OverlayID _contextOverlayID{ UNKNOWN_OVERLAY_ID }; - std::shared_ptr _contextOverlay{ nullptr }; + OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _contextOverlay { nullptr }; public: ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } - Q_INVOKABLE bool getLastInspectedEntityWasValid(); Q_INVOKABLE void requestEntityOwnershipVerification(const QUuid& entityID); void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } @@ -61,8 +60,10 @@ public: signals: void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); + void ownershipVerificationSuccess(const QUuid& entityID); + void ownershipVerificationFailed(const QUuid& entityID); - public slots: +public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); @@ -73,7 +74,7 @@ signals: void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); bool contextOverlayFilterPassed(const EntityItemID& entityItemID); - private slots: +private slots: void handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: @@ -85,7 +86,6 @@ private: bool _enabled{ true }; EntityItemID _currentEntityWithContextOverlay{}; EntityItemID _lastInspectedEntity{}; - EntityItemID _lastInspectedValidEntity{}; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; From f3e20e72cb51f032ef024c9dbbbf339b2f72d761 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 25 Jan 2018 15:09:03 -0800 Subject: [PATCH 091/381] really fix multi-highlighing --- interface/resources/qml/hifi/tablet/TabletHome.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 88d8194c49..f56d4f73be 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -148,6 +148,18 @@ Item { previousGridIndex = currentIndex } + onMovementStarted: { + if (currentIndex < 0 || gridView.currentItem === undefined || gridView.contentItem.children.length - 1 < currentIndex) { + return; + } + var button = gridView.contentItem.children[currentIndex].children[0]; + if (button.isActive) { + button.state = "active state"; + } else { + button.state = "base state"; + } + } + cellWidth: width/3 cellHeight: cellWidth flow: GridView.LeftToRight From 3a6f184278946aff8bdbb914918f95728cadbdc9 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Thu, 25 Jan 2018 15:13:14 -0800 Subject: [PATCH 092/381] make clear function is for avatar entities only, add error handling in non-client entity cases --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 7 ++++++- interface/src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 069446494f..d86d441e3b 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -267,10 +267,15 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } -void ContextOverlayInterface::requestEntityOwnershipVerification(const QUuid& entityItemID) { +void ContextOverlayInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); _entityMarketplaceID = entityProperties.getMarketplaceID(); if (!entityItemID.isNull() && _entityMarketplaceID.length() > 0) { + if (!entityProperties.getClientOnly()) { + qCDebug(entities) << "Failed to prove ownership of:" << entityItemID << "is not an avatar entity"; + emit ownershipVerificationFailed(entityItemID); + return; + } setLastInspectedEntity(entityItemID); requestOwnershipVerification(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c96c5989a6..04416299d6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -50,7 +50,7 @@ class ContextOverlayInterface : public QObject, public Dependency { public: ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } - Q_INVOKABLE void requestEntityOwnershipVerification(const QUuid& entityID); + Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } void setEnabled(bool enabled); From faf8350369e26b627a1c6b574cbac2578061bef8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 25 Jan 2018 15:57:48 -0800 Subject: [PATCH 093/381] Bug fix for avatar neck cauterization The basic dual-quaternion skinning algorithm does not handle non-rigid transformations like scale well. Because we only use scaling for head cauterization, we special case this by passing in a cauterization factor, as well as a cauterization position to the vertex shader. If a vertex is flagged as cauterized, we slam it to equal the cauterization position. Although, not as smooth as the previous method, it seems to work well enough on the avatar's I've tested. --- .../render-utils/src/CauterizedModel.cpp | 2 + libraries/render-utils/src/Model.h | 10 +++- libraries/render-utils/src/Skinning.slh | 50 +++++++++++++++---- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index e3f26a43d8..0b8c636678 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -115,6 +115,7 @@ void CauterizedModel::updateClusterMatrices() { Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterTransforms[j].setCauterizationParameters(0.0f, jointPose.trans()); #else auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); @@ -151,6 +152,7 @@ void CauterizedModel::updateClusterMatrices() { Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterTransforms[j].setCauterizationParameters(1.0f, cauterizePose.trans()); #else glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 560aa82f0c..17ac251459 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -263,26 +263,34 @@ public: _scale.x = p.scale().x; _scale.y = p.scale().y; _scale.z = p.scale().z; + _scale.w = 0.0f; _dq = DualQuaternion(p.rot(), p.trans()); } TransformDualQuaternion(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) { _scale.x = scale.x; _scale.y = scale.y; _scale.z = scale.z; + _scale.w = 0.0f; _dq = DualQuaternion(rot, trans); } TransformDualQuaternion(const Transform& transform) { _scale = glm::vec4(transform.getScale(), 0.0f); + _scale.w = 0.0f; _dq = DualQuaternion(transform.getRotation(), transform.getTranslation()); } glm::vec3 getScale() const { return glm::vec3(_scale); } glm::quat getRotation() const { return _dq.getRotation(); } glm::vec3 getTranslation() const { return _dq.getTranslation(); } glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRotation(), getTranslation()); }; + + void setCauterizationParameters(float cauterizationAmount, const glm::vec3& cauterizedPosition) { + _scale.w = cauterizationAmount; + _cauterizedPosition = glm::vec4(cauterizedPosition, 1.0f); + } protected: glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; DualQuaternion _dq; - glm::vec4 _padding; + glm::vec4 _cauterizedPosition { 0.0f, 0.0f, 0.0f, 1.0f }; }; #endif diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index a7edfb14a6..702813366f 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -58,17 +58,19 @@ mat4 dualQuatToMat4(vec4 real, vec4 dual) { void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { // linearly blend scale and dual quaternion components - vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0]); + vec4 scale = clusterMatrix[0]; vec4 real = clusterMatrix[1]; vec4 dual = clusterMatrix[2]; + vec4 cauterizedPos = clusterMatrix[3]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -79,6 +81,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; dAccum += dual * dqClusterWeight; + cAccum += cauterizedPos * clusterWeight; } // normalize dual quaternion @@ -88,25 +91,34 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + + // an sAccum.w of > 0 indicates that this joint is cauterized. + if (sAccum.w > 0.1) { + skinnedPosition = cAccum; + } else { + sAccum.w = 1.0; + skinnedPosition = m * (sAccum * inPosition); + } } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { // linearly blend scale and dual quaternion components - vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0]); + vec4 scale = clusterMatrix[0]; vec4 real = clusterMatrix[1]; vec4 dual = clusterMatrix[2]; + vec4 cauterizedPos = clusterMatrix[3]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -117,6 +129,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; dAccum += dual * dqClusterWeight; + cAccum += cauterizedPos * clusterWeight; } // normalize dual quaternion @@ -126,7 +139,15 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + + // an sAccum.w of > 0 indicates that this joint is cauterized. + if (sAccum.w > 0.1) { + skinnedPosition = cAccum; + } else { + sAccum.w = 1.0; + skinnedPosition = m * (sAccum * inPosition); + } + skinnedNormal = vec3(m * vec4(inNormal, 0)); } @@ -134,18 +155,20 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { // linearly blend scale and dual quaternion components - vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0]); + vec4 scale = clusterMatrix[0]; vec4 real = clusterMatrix[1]; vec4 dual = clusterMatrix[2]; + vec4 cauterizedPos = clusterMatrix[3]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -156,6 +179,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; dAccum += dual * dqClusterWeight; + cAccum += cauterizedPos * clusterWeight; } // normalize dual quaternion @@ -165,7 +189,15 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + + // an sAccum.w of > 0 indicates that this vertex is cauterized. + if (sAccum.w > 0.1) { + skinnedPosition = cAccum; + } else { + sAccum.w = 1.0; + skinnedPosition = m * (sAccum * inPosition); + } + skinnedNormal = vec3(m * vec4(inNormal, 0)); skinnedTangent = vec3(m * vec4(inTangent, 0)); } From 78d72a1703155554af3ff21f073a2b5dbe782cf6 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 25 Jan 2018 16:03:05 -0800 Subject: [PATCH 094/381] remove unnecessary invokeMethod from ResourceCache::getResource --- libraries/networking/src/ResourceCache.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index a3ac995bcf..7ba7cca96d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -340,14 +340,6 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& return resource; } - if (QThread::currentThread() != thread()) { - qCDebug(networking) << "Fetching asynchronously:" << url; - QMetaObject::invokeMethod(this, "getResource", - Q_ARG(QUrl, url), Q_ARG(QUrl, fallback)); - // Cannot use extra parameter as it might be freed before the invocation - return QSharedPointer(); - } - if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { return getResource(fallback, QUrl()); } @@ -358,6 +350,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& extra); resource->setSelf(resource); resource->setCache(this); + resource->moveToThread(qApp->thread()); connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); { QWriteLocker locker(&_resourcesLock); From 2f4e61888fb4636d140b0ece98f46c1e016b581c Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 25 Jan 2018 16:57:35 -0800 Subject: [PATCH 095/381] Making it work maybe --- interface/src/ui/overlays/ModelOverlay.h | 2 +- interface/src/ui/overlays/OverlaysPayload.cpp | 4 ++-- .../src/RenderableModelEntityItem.cpp | 4 ++-- .../render-utils/src/CauterizedModel.cpp | 2 +- .../render-utils/src/MeshPartPayload.cpp | 2 +- libraries/render/src/render/Item.h | 20 ++++++++----------- 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index a39f762210..a012969621 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -106,7 +106,7 @@ private: bool _jointMappingCompleted { false }; QVector _jointMapping; // domain is index into model-joints, range is index into animation-joints - bool _visibleDirty { false }; + bool _visibleDirty { true }; bool _drawInFrontDirty { false }; bool _drawInHUDDirty { false }; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index c071c44d42..937de1a236 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -46,9 +46,9 @@ namespace render { } if (overlay->getVisible()) { - builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_0); // don't draw overlays in mirror + builder.withVisibilityMask(render::ItemKey::VISIBLE_MASK_0); // don't draw overlays in mirror } else { - builder.withViewVisibilityMask(render::ItemKey::VISIBLE_MASK_NONE); + builder.withVisibilityMask(render::ItemKey::VISIBLE_MASK_NONE); } return builder.build(); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 0ae7efb652..ee3557346d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1334,8 +1334,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->stopModelOverrideIfNoParent(); uint32_t viewVisiblityMask = _cauterized ? - render::ItemKey::VISIBLE_MASK_0 : // draw in every view except the main one (view zero) - render::ItemKey::VISIBLE_MASK_NONE; // draw in all views + render::ItemKey::VISIBLE_MASK_1 : // draw in every view except the main one (view zero) + render::ItemKey::VISIBLE_MASK_ALL; // draw in all views if (model->isVisible() != _visible || model->getViewVisibilityMask() != viewVisiblityMask) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index f67f5ef358..809be09436 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -247,7 +247,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_NONE); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index da636eed06..451b168465 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -396,7 +396,7 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi if (!isVisible) { builder.withInvisible(); } else { - builder.withViewVisibilityMask(viewVisiblityMask); + builder.withVisibilityMask(viewVisiblityMask); } if (isLayered) { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 18e626d005..97eaefcb53 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -119,18 +119,14 @@ public: } return (*this); } - Builder& withViewVisibilityMask(uint8_t mask) { - if (mask & render::ItemKey::VISIBLE_MASK_0) { - _flags.reset(INVISIBLE0); - } - if (mask & render::ItemKey::VISIBLE_MASK_1) { - _flags.reset(INVISIBLE1); - } - if (mask & render::ItemKey::VISIBLE_MASK_2) { - _flags.reset(INVISIBLE2); - } - if (mask & render::ItemKey::VISIBLE_MASK_3) { - _flags.reset(INVISIBLE3); + Builder& withVisibilityMask(uint8_t mask) { + for (int i = 0; i < ItemKey::NUM_VISIBLE_MASK_INDICES; i++) { + if ((1 << i) & mask) { + withVisible(i); + } + else { + withInvisible(i); + } } return (*this); } From 51dac0437422f119d4457ca24c93b9db9e07fb55 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 26 Jan 2018 02:59:43 -0800 Subject: [PATCH 096/381] Introducing the tag in lieu of the vsisiblity mask --- interface/src/Application.cpp | 2 +- interface/src/SecondaryCamera.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 8 +- interface/src/ui/overlays/ModelOverlay.cpp | 2 +- interface/src/ui/overlays/OverlaysPayload.cpp | 8 +- .../src/RenderableModelEntityItem.cpp | 4 +- .../render-utils/src/CauterizedModel.cpp | 2 +- .../render-utils/src/MeshPartPayload.cpp | 4 +- libraries/render-utils/src/Model.h | 2 +- .../render-utils/src/RenderShadowTask.cpp | 2 +- libraries/render/src/render/Item.cpp | 20 ++- libraries/render/src/render/Item.h | 138 ++++++++---------- .../src/render/RenderFetchCullSortTask.cpp | 18 +-- .../src/render/RenderFetchCullSortTask.h | 2 +- 14 files changed, 102 insertions(+), 114 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3284ed902e..bdb97bcdd8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2274,7 +2274,7 @@ void Application::initializeGL() { #ifndef Q_OS_ANDROID _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); #endif - _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::VISIBLE_MASK_0, render::ItemKey::VISIBLE_MASK_0); + _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 9d5b6a44a5..1ae1cc3559 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -20,7 +20,7 @@ using RenderArgsPointer = std::shared_ptr; void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { task.addJob("RenderShadowTask", cullFunctor); - const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::VISIBLE_MASK_1, render::ItemKey::VISIBLE_MASK_1); + const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); assert(items.canCast()); if (!isDeferred) { task.addJob("Forward", items); @@ -205,7 +205,7 @@ public: void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { const auto cachedArg = task.addJob("SecondaryCamera"); - const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::VISIBLE_MASK_1, render::ItemKey::VISIBLE_MASK_1); + const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); assert(items.canCast()); if (!isDeferred) { task.addJob("Forward", items); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index dd51de2079..a63af4435b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1078,7 +1078,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_NONE); + _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1428,7 +1428,7 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); - _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::VISIBLE_MASK_NONE); + _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE); _headBoneSet.clear(); emit skeletonChanged(); @@ -1742,7 +1742,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) { if (model->isActive() && model->isRenderable()) { - model->setVisibleInScene(visible, scene, render::ItemKey::VISIBLE_MASK_NONE); + model->setVisibleInScene(visible, scene, render::ItemKey::TAG_BITS_NONE); } } @@ -1938,7 +1938,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(), - render::ItemKey::VISIBLE_MASK_NONE); + render::ItemKey::TAG_BITS_NONE); } } } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 59d4521307..015a1a4a3b 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -87,7 +87,7 @@ void ModelOverlay::update(float deltatime) { if (_visibleDirty) { _visibleDirty = false; // don't show overlays in mirrors - _model->setVisibleInScene(getVisible(), scene, render::ItemKey::VISIBLE_MASK_0); + _model->setVisibleInScene(getVisible(), scene, render::ItemKey::TAG_BITS_0); } if (_drawInFrontDirty) { _drawInFrontDirty = false; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 937de1a236..f99ced0021 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -45,12 +45,12 @@ namespace render { builder.withViewSpace(); } - if (overlay->getVisible()) { - builder.withVisibilityMask(render::ItemKey::VISIBLE_MASK_0); // don't draw overlays in mirror - } else { - builder.withVisibilityMask(render::ItemKey::VISIBLE_MASK_NONE); + if (!overlay->getVisible()) { + builder.withInvisible(); } + builder.withTagBits(render::ItemKey::TAG_BITS_0); // Only draw overlays in main view + return builder.build(); } template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ee3557346d..190864cecf 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1334,8 +1334,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->stopModelOverrideIfNoParent(); uint32_t viewVisiblityMask = _cauterized ? - render::ItemKey::VISIBLE_MASK_1 : // draw in every view except the main one (view zero) - render::ItemKey::VISIBLE_MASK_ALL; // draw in all views + render::ItemKey::TAG_BITS_1 : // draw in every view except the main one (view zero) + render::ItemKey::TAG_BITS_ALL; // draw in all views if (model->isVisible() != _visible || model->getViewVisibilityMask() != viewVisiblityMask) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 809be09436..574d3d741c 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -247,7 +247,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::VISIBLE_MASK_ALL); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::TAG_BITS_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 451b168465..aaec28e210 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -395,10 +395,10 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi if (!isVisible) { builder.withInvisible(); - } else { - builder.withVisibilityMask(viewVisiblityMask); } + builder.withTagBits(render::ItemKey::TAG_BITS_ALL); // Draw models in all tags + if (isLayered) { builder.withLayered(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index d9845de9ed..1fa14f92d6 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -389,7 +389,7 @@ protected: QUrl _url; bool _isVisible; - uint8_t _viewVisibilityMask { render::ItemKey::VISIBLE_MASK_ALL }; + uint8_t _viewVisibilityMask { render::ItemKey::TAG_BITS_ALL }; gpu::Buffers _blendedVertexBuffers; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7a7714e1e0..d83dfd73a5 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -264,7 +264,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit1() = ItemFilter::Builder::visibleWorldItems(0x00, 0x00).withTypeShape().withOpaque().withoutLayered(); + output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 0e4502a47c..1bf20ab510 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -34,13 +34,19 @@ const int Item::LAYER_3D = 1; const int Item::LAYER_3D_FRONT = 2; const int Item::LAYER_3D_HUD = 3; -const uint8_t ItemKey::NUM_VISIBLE_MASK_INDICES { 4 }; -const uint8_t ItemKey::VISIBLE_MASK_ALL { 0x0F }; -const uint8_t ItemKey::VISIBLE_MASK_NONE { 0x00 }; -const uint8_t ItemKey::VISIBLE_MASK_0 { 0x01 }; -const uint8_t ItemKey::VISIBLE_MASK_1 { 0x02 }; -const uint8_t ItemKey::VISIBLE_MASK_2 { 0x04 }; -const uint8_t ItemKey::VISIBLE_MASK_3 { 0x08 }; +const uint8_t ItemKey::TAG_BITS_ALL{ 0xFF }; +const uint8_t ItemKey::TAG_BITS_NONE{ 0x00 }; +const uint8_t ItemKey::TAG_BITS_0{ 0x01 }; +const uint8_t ItemKey::TAG_BITS_1{ 0x02 }; +const uint8_t ItemKey::TAG_BITS_2{ 0x04 }; +const uint8_t ItemKey::TAG_BITS_3{ 0x08 }; +const uint8_t ItemKey::TAG_BITS_4 { 0x10 }; +const uint8_t ItemKey::TAG_BITS_5{ 0x20 }; +const uint8_t ItemKey::TAG_BITS_6{ 0x40 }; +const uint8_t ItemKey::TAG_BITS_7 { 0x80 }; + +const uint32_t ItemKey::KEY_TAG_BITS_MASK = ((uint32_t) ItemKey::TAG_BITS_ALL) << FIRST_TAG_BIT; + void Item::Status::Value::setScale(float scale) { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 97eaefcb53..4833bd2f42 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -38,38 +38,61 @@ class Context; // Key is the KEY to filter Items and create specialized lists class ItemKey { public: - enum FlagBit { + // 8 tags are available to organize the items and filter them against as fields of the ItemKey. + // TAG & TAG_BITS are defined from several bits in the Key. + // An Item can be tagged and filtering can rely on the tags to keep or exclude items + // ItemKey are not taged by default + enum Tag : uint8_t { + TAG_0 = 0, // 8 Tags + TAG_1, + TAG_2, + TAG_3, + TAG_4, + TAG_5, + TAG_6, + TAG_7, + + NUM_TAGS, + }; + // Tag bits are derived from the Tag enum + const static uint8_t TAG_BITS_ALL; + const static uint8_t TAG_BITS_NONE; + const static uint8_t TAG_BITS_0; + const static uint8_t TAG_BITS_1; + const static uint8_t TAG_BITS_2; + const static uint8_t TAG_BITS_3; + const static uint8_t TAG_BITS_4; + const static uint8_t TAG_BITS_5; + const static uint8_t TAG_BITS_6; + const static uint8_t TAG_BITS_7; + + enum FlagBit : uint32_t { TYPE_SHAPE = 0, // Item is a Shape TYPE_LIGHT, // Item is a Light TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items + TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item DEFORMED, // Deformed within bound, not solid - INVISIBLE0, // Visible or not in this mask index? - INVISIBLE1, // Visible or not in this mask index? - INVISIBLE2, // Visible or not in this mask index? - INVISIBLE3, // Visible or not in this mask index? + INVISIBLE, // Visible or not in the scene? SHADOW_CASTER, // Item cast shadows - PICKABLE, // Item can be picked/selected LAYERED, // Item belongs to one of the layers different from the default layer - SMALLER, + FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against + LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, + + __SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells) NUM_FLAGS, // Not a valid flag }; typedef std::bitset Flags; - // VISIBLE MASK is defined from several bits in the Key. - // An Item can be visible in some mask bits and not other allowing for per view rendering - // Beware that the visibility mask is the oposite of what stored in the key vals. - const static uint8_t NUM_VISIBLE_MASK_INDICES; - const static uint8_t VISIBLE_MASK_ALL; - const static uint8_t VISIBLE_MASK_NONE; - const static uint8_t VISIBLE_MASK_0; - const static uint8_t VISIBLE_MASK_1; - const static uint8_t VISIBLE_MASK_2; - const static uint8_t VISIBLE_MASK_3; + // All the bits touching tag bits sets to true + const static uint32_t KEY_TAG_BITS_MASK; + static uint32_t evalTagBitsWithKeyBits(uint8_t tagBits, const uint32_t keyBits) { + return (keyBits & ~KEY_TAG_BITS_MASK) | (((uint32_t)tagBits) << FIRST_TAG_BIT); + } // The key is the Flags Flags _flags; @@ -96,44 +119,14 @@ public: Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } - Builder& withVisible(uint8_t maskIndex = NUM_VISIBLE_MASK_INDICES) { - if (maskIndex == NUM_VISIBLE_MASK_INDICES) { - _flags.reset(INVISIBLE0); - _flags.reset(INVISIBLE1); - _flags.reset(INVISIBLE2); - _flags.reset(INVISIBLE3); - } - else { - _flags.reset(INVISIBLE0 + maskIndex); - } - return (*this); - } - Builder& withInvisible(uint8_t maskIndex = NUM_VISIBLE_MASK_INDICES) { - if (maskIndex == NUM_VISIBLE_MASK_INDICES) { - _flags.set(INVISIBLE0); - _flags.set(INVISIBLE1); - _flags.set(INVISIBLE2); - _flags.set(INVISIBLE3); - } else { - _flags.set(INVISIBLE0 + maskIndex); - } - return (*this); - } - Builder& withVisibilityMask(uint8_t mask) { - for (int i = 0; i < ItemKey::NUM_VISIBLE_MASK_INDICES; i++) { - if ((1 << i) & mask) { - withVisible(i); - } - else { - withInvisible(i); - } - } - return (*this); - } + Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } - Builder& withPickable() { _flags.set(PICKABLE); return (*this); } Builder& withLayered() { _flags.set(LAYERED); return (*this); } + Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); } + // Set ALL the tags in one call using the Tag bits + Builder& withTagBits(uint8_t tagBits) { _flags = evalTagBitsWithKeyBits(tagBits, _flags.to_ulong()); return (*this); } + // Convenient standard keys that we will keep on using all over the place static Builder opaqueShape() { return Builder().withTypeShape(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } @@ -158,21 +151,20 @@ public: bool isRigid() const { return !_flags[DEFORMED]; } bool isDeformed() const { return _flags[DEFORMED]; } - bool isVisible(uint8_t maskIndex) const { return !_flags[INVISIBLE0 + maskIndex]; } - bool isInvisible(uint8_t maskIndex) const { return _flags[INVISIBLE0 + maskIndex]; } - uint8_t getVisibleMask() const { return (~(_flags.to_ulong() >> INVISIBLE0) & VISIBLE_MASK_ALL);} - bool isVisibleMask(uint8_t mask) const { return getVisibleMask() & mask; } + bool isVisible() const { return !_flags[INVISIBLE]; } + bool isInvisible() const { return _flags[INVISIBLE]; } bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } - bool isPickable() const { return _flags[PICKABLE]; } - bool isLayered() const { return _flags[LAYERED]; } bool isSpatial() const { return !isLayered(); } + bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; } + uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } + // Probably not public, flags used by the scene - bool isSmall() const { return _flags[SMALLER]; } - void setSmaller(bool smaller) { (smaller ? _flags.set(SMALLER) : _flags.reset(SMALLER)); } + bool isSmall() const { return _flags[__SMALLER]; } + void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); } bool operator==(const ItemKey& key) { return (_flags == key._flags); } bool operator!=(const ItemKey& key) { return (_flags != key._flags); } @@ -220,34 +212,24 @@ public: Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } - Builder& withVisible(uint8_t maskIndex) { _value.reset(ItemKey::INVISIBLE0 + maskIndex); _mask.set(ItemKey::INVISIBLE0 + maskIndex); return (*this); } - Builder& withInvisible(uint8_t maskIndex) { _value.set(ItemKey::INVISIBLE0 + maskIndex); _mask.set(ItemKey::INVISIBLE0 + maskIndex); return (*this); } - Builder& withVisibilityMask(uint8_t mask, uint8_t touchBits) { - for (int i = 0; i < ItemKey::NUM_VISIBLE_MASK_INDICES; i++) { - if ((1 << i) & touchBits) { - if ((1 << i) & mask) { - withVisible(i); - } - else { - withInvisible(i); - } - } - } - return (*this); - } + Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } + Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } - Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } - Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } + Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } + // Set ALL the tags in one call using the Tag bits and the Tag bits touched + Builder& withTagBits(uint8_t tagBits, uint8_t tagMask) { _value = ItemKey::evalTagBitsWithKeyBits(tagBits, _value.to_ulong()); _mask = ItemKey::evalTagBitsWithKeyBits(tagMask, _mask.to_ulong()); return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place - static Builder visibleWorldItems(uint8_t visibilityMask, uint8_t visibilityMaskTouched) { return Builder().withVisibilityMask(visibilityMask, visibilityMaskTouched).withWorldSpace(); } + static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index b4e1a618dc..a402c3bc9d 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -17,12 +17,12 @@ using namespace render; -void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor, uint8_t visibilityMask, uint8_t visibilityMaskTouched) { +void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // CPU jobs: // Fetch and cull the items from the scene - const ItemFilter filter = ItemFilter::Builder::visibleWorldItems(visibilityMask, visibilityMaskTouched).withoutLayered(); + const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered().withTagBits(tagBits, tagMask); const auto spatialFilter = render::Varying(filter); const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); @@ -40,15 +40,15 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const int META_BUCKET = 3; const int BACKGROUND_BUCKET = 2; MultiFilterItems::ItemFilterArray spatialFilters = { { - ItemFilter::Builder::opaqueShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), - ItemFilter::Builder::transparentShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), - ItemFilter::Builder::light().withVisibilityMask(visibilityMask, visibilityMaskTouched), - ItemFilter::Builder::meta().withVisibilityMask(visibilityMask, visibilityMaskTouched) + ItemFilter::Builder::opaqueShape().withVisible().withTagBits(tagBits, tagMask), + ItemFilter::Builder::transparentShape().withVisible().withTagBits(tagBits, tagMask), + ItemFilter::Builder::light().withVisible().withTagBits(tagBits, tagMask), + ItemFilter::Builder::meta().withVisible().withTagBits(tagBits, tagMask) } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { - ItemFilter::Builder::opaqueShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), - ItemFilter::Builder::transparentShape().withVisibilityMask(visibilityMask, visibilityMaskTouched), - ItemFilter::Builder::background().withVisibilityMask(visibilityMask, visibilityMaskTouched) + ItemFilter::Builder::opaqueShape().withVisible().withTagBits(tagBits, tagMask), + ItemFilter::Builder::transparentShape().withVisible().withTagBits(tagBits, tagMask), + ItemFilter::Builder::background().withVisible().withTagBits(tagBits, tagMask) } }; const auto filteredSpatialBuckets = task.addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 9aa235b3fb..8c9f2e7304 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -36,7 +36,7 @@ public: RenderFetchCullSortTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t visibilityMask = 0xFF, uint8_t visibilityMaskTouched = 0x00); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; #endif // hifi_RenderFetchCullSortTask_h From 6b0b17ff633f72bb16171da4f34ad9f672ecefec Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 26 Jan 2018 17:57:20 +0100 Subject: [PATCH 097/381] Working on better adaptive bias algorithm for shadow --- libraries/render-utils/src/LightStage.cpp | 8 ++--- libraries/render-utils/src/LightStage.h | 2 +- .../render-utils/src/RenderShadowTask.cpp | 10 ++++-- libraries/render-utils/src/RenderShadowTask.h | 16 ++++++++- libraries/render-utils/src/Shadow.slh | 17 +++++---- .../src/directional_ambient_light_shadow.slf | 4 ++- .../src/directional_skybox_light_shadow.slf | 4 ++- scripts/developer/utilities/render/shadow.qml | 35 +++++++++++++++++++ 8 files changed, 77 insertions(+), 19 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index f146cd6e0a..eac9dd7657 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -220,7 +220,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, } void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float nearDepth, float farDepth) { + float nearDepth, float farDepth, float baseBias) { assert(nearDepth < farDepth); assert(cascadeIndex < _cascades.size()); @@ -270,11 +270,7 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co // Update the buffer auto& schema = _schemaBuffer.edit(); schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); - // Adapt shadow bias to shadow resolution with a totally empirical formula - const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 7.5f; - const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; - schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); + schema.cascades[cascadeIndex].bias = baseBias; } void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index d1a8680706..c2c2df8c89 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -80,7 +80,7 @@ public: void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth = 1.0f, float farDepth = 1000.0f); void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float nearDepth = 1.0f, float farDepth = 1000.0f); + float nearDepth = 1.0f, float farDepth = 1000.0f, float baseBias = 0.005f); void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d83dfd73a5..f41860b6de 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -216,7 +216,9 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende task.addJob("ShadowSetup"); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { - const auto setupOutput = task.addJob("ShadowCascadeSetup", i); + char jobName[64]; + sprintf(jobName, "ShadowCascadeSetup%d", i); + const auto setupOutput = task.addJob(jobName, i); const auto shadowFilter = setupOutput.getN(1); // CPU jobs: @@ -253,6 +255,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { } } +void RenderShadowCascadeSetup::configure(const Config& configuration) { + _baseBias = configuration.bias * configuration.bias * 0.01f; +} + void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); @@ -266,7 +272,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, _baseBias); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index d8d4c624e7..42c279c02a 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -62,17 +62,31 @@ public: }; +class RenderShadowCascadeSetupConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float bias MEMBER bias NOTIFY dirty) +public: + + float bias{ 0.5f }; + +signals: + void dirty(); +}; + class RenderShadowCascadeSetup { public: using Outputs = render::VaryingSet3; - using JobModel = render::Job::ModelO; + using Config = RenderShadowCascadeSetupConfig; + using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + void configure(const Config& configuration); void run(const render::RenderContextPointer& renderContext, Outputs& output); private: unsigned int _cascadeIndex; + float _baseBias{ 0.1f }; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index a12dd0f4a4..50ae8864f4 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -83,27 +83,30 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { +float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) { if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } - // Multiply bias if we are at a grazing angle with light - float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal)); - float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor); + // Add fixed bias + bias += getShadowBias(cascadeIndex); return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } -float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) { +float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[2]; ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); + // Adjust bias if we are at a grazing angle with light + float ndotl = dot(worldLightDir, worldNormal); + float bias = 0.5*(1.0/(ndotl*ndotl)-1.0); + bias *= getShadowScale(); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias); } float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); // Falloff to max distance diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index f7ea8c5966..eead392fd8 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -28,7 +28,9 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); + Light shadowLight = getKeyLight(); + vec3 worldLightDirection = getLightDirection(shadowLight); + float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal); if (frag.mode == FRAG_MODE_UNLIT) { discard; diff --git a/libraries/render-utils/src/directional_skybox_light_shadow.slf b/libraries/render-utils/src/directional_skybox_light_shadow.slf index 37c9ae7fba..2a2f8f65a3 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -28,7 +28,9 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); + Light shadowLight = getKeyLight(); + vec3 worldLightDirection = getLightDirection(shadowLight); + float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index a8fcf1aec7..32405a5260 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -10,6 +10,9 @@ // import QtQuick 2.5 import QtQuick.Controls 1.4 +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls +import "configSlider" Column { id: root @@ -68,4 +71,36 @@ Column { font.italic: true } } + ConfigSlider { + label: qsTr("Cascade 0 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup0") + property: "bias" + max: 1.0 + min: 0.01 + } + ConfigSlider { + label: qsTr("Cascade 1 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup1") + property: "bias" + max: 1.0 + min: 0.01 + } + ConfigSlider { + label: qsTr("Cascade 2 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup2") + property: "bias" + max: 1.0 + min: 0.01 + } + ConfigSlider { + label: qsTr("Cascade 3 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup3") + property: "bias" + max: 1.0 + min: 0.01 + } } From be42f0cfb1c4d66aba476ee040000104a1850b95 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 26 Jan 2018 13:24:44 -0800 Subject: [PATCH 098/381] finally fix the multihighlighting --- interface/resources/qml/hifi/tablet/TabletHome.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index f56d4f73be..cfff3a3273 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -127,7 +127,7 @@ Item { GridView { id: gridView - + flickableDirection: Flickable.AutoFlickIfNeeded keyNavigationEnabled: false highlightFollowsCurrentItem: false From 43eaa02ef069f560b8fb1d5477b2c132722026da Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 26 Jan 2018 14:01:33 -0800 Subject: [PATCH 099/381] updated Skinning.slh comment and constant. --- libraries/render-utils/src/Skinning.slh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 702813366f..6048ba4ade 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -92,8 +92,11 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - // an sAccum.w of > 0 indicates that this joint is cauterized. - if (sAccum.w > 0.1) { + // sAccum.w indicates the amount of cauterization for this vertex. + // 0 indicates no cauterization and 1 indicates full cauterization. + // TODO: make this cauterization smoother or implement full dual-quaternion scale support. + const float CAUTERIZATION_THRESHOLD = 0.1; + if (sAccum.w > CAUTERIZATION_THRESHOLD) { skinnedPosition = cAccum; } else { sAccum.w = 1.0; @@ -140,8 +143,11 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - // an sAccum.w of > 0 indicates that this joint is cauterized. - if (sAccum.w > 0.1) { + // sAccum.w indicates the amount of cauterization for this vertex. + // 0 indicates no cauterization and 1 indicates full cauterization. + // TODO: make this cauterization smoother or implement full dual-quaternion scale support. + const float CAUTERIZATION_THRESHOLD = 0.1; + if (sAccum.w > CAUTERIZATION_THRESHOLD) { skinnedPosition = cAccum; } else { sAccum.w = 1.0; @@ -190,8 +196,11 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - // an sAccum.w of > 0 indicates that this vertex is cauterized. - if (sAccum.w > 0.1) { + // sAccum.w indicates the amount of cauterization for this vertex. + // 0 indicates no cauterization and 1 indicates full cauterization. + // TODO: make this cauterization smoother or implement full dual-quaternion scale support. + const float CAUTERIZATION_THRESHOLD = 0.1; + if (sAccum.w > CAUTERIZATION_THRESHOLD) { skinnedPosition = cAccum; } else { sAccum.w = 1.0; From aa82ad885561411daf1d97778193f22cd18b612b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jan 2018 14:57:36 -0800 Subject: [PATCH 100/381] adjust client to only delete entities on server echo --- libraries/entities/src/EntityScriptingInterface.cpp | 5 ++++- libraries/entities/src/EntityTree.cpp | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 206065beeb..877225de77 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -585,7 +585,10 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { if (entity->getLocked()) { shouldDelete = false; } else { - _entityTree->deleteEntity(entityID); + // only delete local entities, server entities will round trip through the server filters + if (entity->getClientOnly()) { + _entityTree->deleteEntity(entityID); + } } } }); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 629281749e..8071a8f9b7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1946,12 +1946,16 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons _totalFilterTime += endFilter - startFilter; - entityItemIDsToDelete << entityItemID; - - if (wantEditLogging() || wantTerseEditLogging()) { - qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; + if (allowed) { + entityItemIDsToDelete << entityItemID; + if (wantEditLogging() || wantTerseEditLogging()) { + qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; + } + } else if (wantEditLogging() || wantTerseEditLogging()) { + qCDebug(entities) << "User [" << sourceNode->getUUID() << "] attempted to deleteentity. ID:" << entityItemID << " Filter rejected erase."; } + } deleteEntities(entityItemIDsToDelete, true, true); } From 20635acf27d5501e431288576ac5cad98dac571c Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Fri, 26 Jan 2018 17:13:39 -0800 Subject: [PATCH 101/381] moving logic to wallet, crashes currently --- .../scripting/WalletScriptingInterface.cpp | 22 +++++++++++++++++++ .../src/scripting/WalletScriptingInterface.h | 5 +++++ .../ui/overlays/ContextOverlayInterface.cpp | 19 ++++------------ .../src/ui/overlays/ContextOverlayInterface.h | 3 +-- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 71a7076bdf..8d6dc7bbcd 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -16,6 +16,16 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q } WalletScriptingInterface::WalletScriptingInterface() { + _contextOverlayInterface = DependencyManager::get(); + + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + _entityPropertyFlags += PROP_MARKETPLACE_ID; + _entityPropertyFlags += PROP_DIMENSIONS; + _entityPropertyFlags += PROP_REGISTRATION_POINT; + _entityPropertyFlags += PROP_CERTIFICATE_ID; + _entityPropertyFlags += PROP_CLIENT_ONLY; + _entityPropertyFlags += PROP_OWNING_AVATAR_ID; } void WalletScriptingInterface::refreshWalletStatus() { @@ -26,4 +36,16 @@ void WalletScriptingInterface::refreshWalletStatus() { void WalletScriptingInterface::setWalletStatus(const uint& status) { _walletStatus = status; emit DependencyManager::get()->walletStatusResult(status); +} + +void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityID) { + EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, _entityPropertyFlags); + if (!entityID.isNull() && entityProperties.getMarketplaceID().length() > 0) { + if (!entityProperties.getClientOnly()) { + qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; + emit _contextOverlayInterface->ownershipVerificationFailed(entityID); + return; + } + _contextOverlayInterface->requestOwnershipVerification(entityID); + } } \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 5469e732c7..c725c34685 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -21,6 +21,7 @@ #include #include "Application.h" #include "commerce/Wallet.h" +#include "ui/overlays/ContextOverlayInterface.h" class CheckoutProxy : public QmlWrapper { Q_OBJECT @@ -33,12 +34,15 @@ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) + QSharedPointer _contextOverlayInterface; + public: WalletScriptingInterface(); Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } + Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); // setWalletStatus() should never be made Q_INVOKABLE. If it were, // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); @@ -49,6 +53,7 @@ signals: private: uint _walletStatus; + EntityPropertyFlags _entityPropertyFlags; }; #endif // hifi_WalletScriptingInterface_h diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index d86d441e3b..4a5aa3d0f4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -267,21 +267,10 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } -void ContextOverlayInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityItemID) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - _entityMarketplaceID = entityProperties.getMarketplaceID(); - if (!entityItemID.isNull() && _entityMarketplaceID.length() > 0) { - if (!entityProperties.getClientOnly()) { - qCDebug(entities) << "Failed to prove ownership of:" << entityItemID << "is not an avatar entity"; - emit ownershipVerificationFailed(entityItemID); - return; - } - setLastInspectedEntity(entityItemID); - requestOwnershipVerification(); - } -} +void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID) { + + setLastInspectedEntity(entityID); -void ContextOverlayInterface::requestOwnershipVerification() { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); auto nodeList = DependencyManager::get(); @@ -381,7 +370,7 @@ void ContextOverlayInterface::openInspectionCertificate() { _hmdScriptingInterface->openTablet(); setLastInspectedEntity(_currentEntityWithContextOverlay); - requestOwnershipVerification(); + requestOwnershipVerification(_lastInspectedEntity); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 04416299d6..4063b28785 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -50,13 +50,13 @@ class ContextOverlayInterface : public QObject, public Dependency { public: ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } - Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } void setEnabled(bool enabled); bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } + void requestOwnershipVerification(const QUuid& entityID); signals: void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); @@ -92,7 +92,6 @@ private: bool _isInMarketplaceInspectionMode { false }; void openInspectionCertificate(); - void requestOwnershipVerification(); void openMarketplace(); void enableEntityHighlight(const EntityItemID& entityItemID); void disableEntityHighlight(const EntityItemID& entityItemID); From 8dfa3aace3c25fe0d966c9931a856309281518e8 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 26 Jan 2018 17:34:37 -0800 Subject: [PATCH 102/381] cleaning en route --- interface/src/Application_render.cpp | 2 +- interface/src/SecondaryCamera.cpp | 1 + .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/RenderableEntityItem.cpp | 4 +- .../src/RenderableModelEntityItem.cpp | 11 ++--- .../RenderableParticleEffectEntityItem.cpp | 4 +- .../src/RenderablePolyLineEntityItem.cpp | 2 +- .../src/RenderableZoneEntityItem.cpp | 2 +- libraries/render-utils/src/AnimDebugDraw.cpp | 2 +- .../render-utils/src/CauterizedModel.cpp | 2 +- libraries/render-utils/src/LightPayload.cpp | 9 ++++- .../render-utils/src/MeshPartPayload.cpp | 26 ++++++++---- libraries/render-utils/src/MeshPartPayload.h | 9 +++-- libraries/render-utils/src/Model.cpp | 40 +++++++++---------- libraries/render-utils/src/Model.h | 6 +-- .../render-utils/src/RenderShadowTask.cpp | 4 +- libraries/render-utils/src/RenderShadowTask.h | 2 +- libraries/render-utils/src/RenderViewTask.cpp | 4 +- libraries/render-utils/src/RenderViewTask.h | 2 +- .../src/render/RenderFetchCullSortTask.cpp | 14 +++---- 20 files changed, 85 insertions(+), 63 deletions(-) diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 1231e5834b..e1f198eed2 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -178,7 +178,7 @@ public: render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; namespace render { - template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 1ae1cc3559..748f1595db 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -19,6 +19,7 @@ using RenderArgsPointer = std::shared_ptr; void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { + task.addJob("RenderShadowTask", cullFunctor); const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); assert(items.canCast()); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 500a24763d..86635cd3bf 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -50,7 +50,7 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { - return ItemKey::Builder::opaqueShape().withTypeMeta(); + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) { return static_pointer_cast(avatar)->getBounds(); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 0988c696dd..aca2f4d35b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -159,10 +159,10 @@ Item::Bound EntityRenderer::getBound() { ItemKey EntityRenderer::getKey() { if (isTransparent()) { - return ItemKey::Builder::transparentShape().withTypeMeta(); + return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } - return ItemKey::Builder::opaqueShape().withTypeMeta(); + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 190864cecf..87767db4f4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1014,9 +1014,9 @@ ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Pare void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) { if (didVisualGeometryRequestSucceed) { - _itemKey = ItemKey::Builder().withTypeMeta(); + _itemKey = ItemKey::Builder().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } else { - _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape(); + _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } } @@ -1333,15 +1333,16 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); entity->stopModelOverrideIfNoParent(); - uint32_t viewVisiblityMask = _cauterized ? + // Default behavior for model is to not be visible in main view if cauterized (aka parented to the avatar's neck joint) + uint32_t viewTaskBits = _cauterized ? render::ItemKey::TAG_BITS_1 : // draw in every view except the main one (view zero) render::ItemKey::TAG_BITS_ALL; // draw in all views - if (model->isVisible() != _visible || model->getViewVisibilityMask() != viewVisiblityMask) { + if (model->isVisible() != _visible || model->getViewTagBits() != viewTaskBits) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(_visible, scene, viewVisiblityMask); + model->setVisibleInScene(_visible, scene, viewTaskBits); } // TODO? early exit here when not visible? diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 2059487426..af95878213 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -147,9 +147,9 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn ItemKey ParticleEffectEntityRenderer::getKey() { if (_visible) { - return ItemKey::Builder::transparentShape(); + return ItemKey::Builder::transparentShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } else { - return ItemKey::Builder().withInvisible().build(); + return ItemKey::Builder().withInvisible().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).build(); } } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 4d223669b4..42110170a0 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -112,7 +112,7 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) } ItemKey PolyLineEntityRenderer::getKey() { - return ItemKey::Builder::transparentShape().withTypeMeta(); + return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } ShapeKey PolyLineEntityRenderer::getShapeKey() { diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index b43944f26a..04f07c5bd3 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -269,7 +269,7 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe ItemKey ZoneEntityRenderer::getKey() { - return ItemKey::Builder().withTypeMeta().build(); + return ItemKey::Builder().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).build(); } bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index c22e99cbbc..4b5b06ab0b 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -67,7 +67,7 @@ public: typedef render::Payload AnimDebugDrawPayload; namespace render { - template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return (data->_isVisible ? ItemKey::Builder::opaqueShape() : ItemKey::Builder::opaqueShape().withInvisible()); } + template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return (data->_isVisible ? ItemKey::Builder::opaqueShape() : ItemKey::Builder::opaqueShape().withInvisible()).withTagBits(ItemKey::TAG_BITS_ALL); } template <> const Item::Bound payloadGetBound(const AnimDebugDrawData::Pointer& data) { return data->_bound; } template <> void payloadRender(const AnimDebugDrawData::Pointer& data, RenderArgs* args) { data->render(args); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 574d3d741c..e30c72e9d4 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -247,7 +247,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::TAG_BITS_ALL); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::TAG_BITS_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/LightPayload.cpp b/libraries/render-utils/src/LightPayload.cpp index 09334faa13..f1eef19498 100644 --- a/libraries/render-utils/src/LightPayload.cpp +++ b/libraries/render-utils/src/LightPayload.cpp @@ -18,9 +18,13 @@ namespace render { template <> const ItemKey payloadGetKey(const LightPayload::Pointer& payload) { ItemKey::Builder builder; builder.withTypeLight(); - if (!payload || !payload->isVisible()) { - builder.withInvisible(); + builder.withTagBits(ItemKey::TAG_BITS_ALL); + if (!payload) { + if (!payload->isVisible()) { + builder.withInvisible(); + } } + return builder.build(); } @@ -87,6 +91,7 @@ namespace render { template <> const ItemKey payloadGetKey(const KeyLightPayload::Pointer& payload) { ItemKey::Builder builder; builder.withTypeLight(); + builder.withTagBits(ItemKey::TAG_BITS_ALL); if (!payload || !payload->isVisible()) { builder.withInvisible(); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index aaec28e210..9655b60a78 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -71,10 +71,20 @@ void MeshPartPayload::updateMaterial(graphics::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; } -ItemKey MeshPartPayload::getKey() const { +void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) { ItemKey::Builder builder; builder.withTypeShape(); + if (!isVisible) { + builder.withInvisible(); + } + + builder.withTagBits(tagBits); + + if (isLayered) { + builder.withLayered(); + } + if (_drawMaterial) { auto matKey = _drawMaterial->getKey(); if (matKey.isTranslucent()) { @@ -82,7 +92,11 @@ ItemKey MeshPartPayload::getKey() const { } } - return builder.build(); + _itemKey = builder.build(); +} + +ItemKey MeshPartPayload::getKey() const { + return _itemKey; } Item::Bound MeshPartPayload::getBound() const { @@ -389,7 +403,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render _worldBound.transform(boundTransform); } -void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVisiblityMask) { +void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) { ItemKey::Builder builder; builder.withTypeShape(); @@ -397,7 +411,7 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi builder.withInvisible(); } - builder.withTagBits(render::ItemKey::TAG_BITS_ALL); // Draw models in all tags + builder.withTagBits(tagBits); if (isLayered) { builder.withLayered(); @@ -417,10 +431,6 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered, uint8_t viewVi _itemKey = builder.build(); } -ItemKey ModelMeshPartPayload::getKey() const { - return _itemKey; -} - void ModelMeshPartPayload::setLayer(bool isLayeredInFront, bool isLayeredInHUD) { if (isLayeredInFront) { _layer = Item::LAYER_3D_FRONT; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index cf64879b3c..21f9dc2e68 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -33,6 +33,8 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; + virtual void updateKey(bool isVisible, bool isLayered, uint8_t tagBits); + virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); virtual void notifyLocationChanged() {} @@ -70,6 +72,9 @@ public: size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } int getMaterialTextureCount() { return _drawMaterial ? _drawMaterial->getTextureCount() : 0; } bool hasTextureInfo() const { return _drawMaterial ? _drawMaterial->hasTextureInfo() : false; } + +protected: + render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; }; namespace render { @@ -94,16 +99,15 @@ public: using TransformType = glm::mat4; #endif + void updateKey(bool isVisible, bool isLayered, uint8_t tagBits) override; void updateClusterBuffer(const std::vector& clusterTransforms); void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface - render::ItemKey getKey() const override; int getLayer() const; render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) override; - void setKey(bool isVisible, bool isLayered, uint8_t viewVisiblityMask); void setLayer(bool isLayeredInFront, bool isLayeredInHUD); void setShapeKey(bool invalidateShapeKey, bool isWireframe); @@ -126,7 +130,6 @@ private: void initCache(const ModelPointer& model); gpu::BufferPointer _blendedVertexBuffer; - render::ItemKey _itemKey { render::ItemKey::Builder::opaqueShape().build() }; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; int _layer { render::Item::LAYER_3D }; }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 4103dffefc..56bed15376 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -268,7 +268,7 @@ void Model::updateRenderItems() { bool isWireframe = self->isWireframe(); bool isVisible = self->isVisible(); - uint8_t viewVisibilityMask = self->getViewVisibilityMask(); + uint8_t viewTagBits = self->getViewTagBits(); bool isLayeredInFront = self->isLayeredInFront(); bool isLayeredInHUD = self->isLayeredInHUD(); @@ -283,7 +283,7 @@ void Model::updateRenderItems() { transaction.updateItem(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey, isWireframe, isVisible, - viewVisibilityMask, isLayeredInFront, + viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { data.updateClusterBuffer(clusterTransforms); @@ -300,7 +300,7 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); @@ -662,25 +662,25 @@ void Model::calculateTriangleSets() { } } -void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewVisibilityMask) { - if (_isVisible != isVisible || _viewVisibilityMask != viewVisibilityMask) { +void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits) { + if (_isVisible != isVisible || _viewTagBits != viewTagBits) { _isVisible = isVisible; - _viewVisibilityMask = viewVisibilityMask; + _viewTagBits = viewTagBits; bool isLayeredInFront = _isLayeredInFront; bool isLayeredInHUD = _isLayeredInHUD; render::Transaction transaction; foreach (auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); }); } scene->enqueueTransaction(transaction); @@ -693,21 +693,21 @@ void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& _isLayeredInFront = isLayeredInFront; bool isVisible = _isVisible; - uint8_t viewVisibilityMask = _viewVisibilityMask; + uint8_t viewTagBits = _viewTagBits; bool isLayeredInHUD = _isLayeredInHUD; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } @@ -720,21 +720,21 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce _isLayeredInHUD = isLayeredInHUD; bool isVisible = _isVisible; - uint8_t viewVisibilityMask = _viewVisibilityMask; + uint8_t viewTagBits = _viewTagBits; bool isLayeredInFront = _isLayeredInFront; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewVisibilityMask, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD, viewVisibilityMask); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1fa14f92d6..72527e1fde 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,7 +86,7 @@ public: const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support - void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewVisiblityMask); + void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits); void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene); void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene); bool needsFixupInScene() const; @@ -104,7 +104,7 @@ public: bool isRenderable() const; bool isVisible() const { return _isVisible; } - uint8_t getViewVisibilityMask() const { return _viewVisibilityMask; } + uint8_t getViewTagBits() const { return _viewTagBits; } bool isLayeredInFront() const { return _isLayeredInFront; } bool isLayeredInHUD() const { return _isLayeredInHUD; } @@ -389,7 +389,7 @@ protected: QUrl _url; bool _isVisible; - uint8_t _viewVisibilityMask { render::ItemKey::TAG_BITS_ALL }; + uint8_t _viewTagBits{ render::ItemKey::TAG_BITS_ALL }; gpu::Buffers _blendedVertexBuffers; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d83dfd73a5..20af1278ac 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -200,7 +200,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con }); } -void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) { +void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; }; // Prepare the ShapePipeline @@ -259,6 +259,8 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Cache old render args RenderArgs* args = renderContext->args; + // const auto& filterMask = inputs; + output.edit0() = args->_renderMode; output.edit2() = args->_sizeScale; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index d8d4c624e7..2bc325cc81 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -48,7 +48,7 @@ public: using JobModel = render::Task::Model; RenderShadowTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); void configure(const Config& configuration); }; diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 83868e1443..42d3044b1b 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -14,7 +14,7 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" -void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask, uint8_t visibilityMaskTouched) { +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits, uint8_t tagMask) { // auto items = input.get(); // Shadows use an orthographic projection because they are linked to sunlights @@ -30,7 +30,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: return true; }); - const auto items = task.addJob("FetchCullSort", cullFunctor, visibilityMask, visibilityMaskTouched); + const auto items = task.addJob("FetchCullSort", cullFunctor, tagBits, tagMask); assert(items.canCast()); if (isDeferred) { diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index faace7349e..5da3d18474 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -23,7 +23,7 @@ public: RenderViewTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t visibilityMask = 0xFF, uint8_t visibilityMaskTouched = 0x00); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index a402c3bc9d..bb6bd462f6 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -40,15 +40,15 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const int META_BUCKET = 3; const int BACKGROUND_BUCKET = 2; MultiFilterItems::ItemFilterArray spatialFilters = { { - ItemFilter::Builder::opaqueShape().withVisible().withTagBits(tagBits, tagMask), - ItemFilter::Builder::transparentShape().withVisible().withTagBits(tagBits, tagMask), - ItemFilter::Builder::light().withVisible().withTagBits(tagBits, tagMask), - ItemFilter::Builder::meta().withVisible().withTagBits(tagBits, tagMask) + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::light(), + ItemFilter::Builder::meta() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { - ItemFilter::Builder::opaqueShape().withVisible().withTagBits(tagBits, tagMask), - ItemFilter::Builder::transparentShape().withVisible().withTagBits(tagBits, tagMask), - ItemFilter::Builder::background().withVisible().withTagBits(tagBits, tagMask) + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::background() } }; const auto filteredSpatialBuckets = task.addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) From a3d86a02423eeaa22fe373b29531d0fc1f77a52b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jan 2018 17:37:24 -0800 Subject: [PATCH 103/381] cleanup --- libraries/entities/src/EntityEditFilters.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 62 ++++++++++--------- libraries/entities/src/EntityTree.h | 4 +- ...e.js => prevent-delete-in-zone-example.js} | 6 +- 4 files changed, 41 insertions(+), 33 deletions(-) rename scripts/tutorials/entity_edit_filters/{prevent-erase-in-zone-example.js => prevent-delete-in-zone-example.js} (81%) diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 4e48c671cb..4b66b61d93 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -245,7 +245,7 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { entitiesObject.setProperty("ADD_FILTER_TYPE", EntityTree::FilterType::Add); entitiesObject.setProperty("EDIT_FILTER_TYPE", EntityTree::FilterType::Edit); entitiesObject.setProperty("PHYSICS_FILTER_TYPE", EntityTree::FilterType::Physics); - entitiesObject.setProperty("ERASE_FILTER_TYPE", EntityTree::FilterType::Erase); + entitiesObject.setProperty("DELETE_FILTER_TYPE", EntityTree::FilterType::Delete); global.setProperty("Entities", entitiesObject); filterData.filterFn = global.property("filter"); if (!filterData.filterFn.isFunction()) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8071a8f9b7..57540092da 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1850,6 +1850,37 @@ void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { } +bool EntityTree::shouldEraseEntity(EntityItemID entityID, const SharedNodePointer& sourceNode) { + EntityItemPointer existingEntity; + + auto startLookup = usecTimestampNow(); + existingEntity = findEntityByEntityItemID(entityID); + auto endLookup = usecTimestampNow(); + _totalLookupTime += endLookup - startLookup; + + auto startFilter = usecTimestampNow(); + FilterType filterType = FilterType::Delete; + EntityItemProperties dummyProperties; + bool wasChanged = false; + + bool allowed = (sourceNode->isAllowedEditor()) || filterProperties(existingEntity, dummyProperties, dummyProperties, wasChanged, filterType); + auto endFilter = usecTimestampNow(); + + _totalFilterTime += endFilter - startFilter; + + if (allowed) { + if (wantEditLogging() || wantTerseEditLogging()) { + qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityID; + } + } + else if (wantEditLogging() || wantTerseEditLogging()) { + qCDebug(entities) << "User [" << sourceNode->getUUID() << "] attempted to deleteentity. ID:" << entityID << " Filter rejected erase."; + } + + return allowed; +} + + // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) { #ifdef EXTRA_ERASE_DEBUGGING @@ -1877,12 +1908,10 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo #endif EntityItemID entityItemID(entityID); - entityItemIDsToDelete << entityItemID; - if (wantEditLogging() || wantTerseEditLogging()) { - qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; + if (shouldEraseEntity(entityID, sourceNode)) { + entityItemIDsToDelete << entityItemID; } - } deleteEntities(entityItemIDsToDelete, true, true); } @@ -1929,33 +1958,10 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons EntityItemID entityItemID(entityID); - EntityItemPointer existingEntity; - - auto startLookup = usecTimestampNow(); - existingEntity = findEntityByEntityItemID(entityItemID); - auto endLookup = usecTimestampNow(); - _totalLookupTime += endLookup - startLookup; - - auto startFilter = usecTimestampNow(); - FilterType filterType = FilterType::Erase; - EntityItemProperties dummyProperties; - bool wasChanged = false; - - bool allowed = (sourceNode->isAllowedEditor()) || filterProperties(existingEntity, dummyProperties, dummyProperties, wasChanged, filterType); - auto endFilter = usecTimestampNow(); - - _totalFilterTime += endFilter - startFilter; - - if (allowed) { + if (shouldEraseEntity(entityID, sourceNode)) { entityItemIDsToDelete << entityItemID; - if (wantEditLogging() || wantTerseEditLogging()) { - qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; - } - } else if (wantEditLogging() || wantTerseEditLogging()) { - qCDebug(entities) << "User [" << sourceNode->getUUID() << "] attempted to deleteentity. ID:" << entityItemID << " Filter rejected erase."; } - } deleteEntities(entityItemIDsToDelete, true, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index ba786fbe98..4c5fcdb0f7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -58,7 +58,7 @@ public: Add, Edit, Physics, - Erase + Delete }; EntityTree(bool shouldReaverage = false); virtual ~EntityTree(); @@ -194,6 +194,8 @@ public: int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); int processEraseMessageDetails(const QByteArray& buffer, const SharedNodePointer& sourceNode); + bool shouldEraseEntity(EntityItemID entityID, const SharedNodePointer& sourceNode); + EntityTreeElementPointer getContainingElement(const EntityItemID& entityItemID) /*const*/; void addEntityMapEntry(EntityItemPointer entity); diff --git a/scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js b/scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js similarity index 81% rename from scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js rename to scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js index 356cc55079..94ee58738d 100644 --- a/scripts/tutorials/entity_edit_filters/prevent-erase-in-zone-example.js +++ b/scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js @@ -1,11 +1,11 @@ // -// prevent-erase-in-zone-example.js +// prevent-delete-in-zone-example.js // // // Created by Brad Hefta-Gaub to use Entities on Jan. 25, 2018 // Copyright 2018 High Fidelity, Inc. // -// This sample entity edit filter script will keep prevent any entity inside the zone from being erased. +// This sample entity edit filter script will keep prevent any entity inside the zone from being deleted. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -13,7 +13,7 @@ function filter(properties, type, originalProperties, zoneProperties) { - if (type == Entities.ERASE_FILTER_TYPE) { + if (type == Entities.DELETE_FILTER_TYPE) { return false; } return properties; From b883d006c83f6c333d74be14a4b22a90950a8cd1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jan 2018 18:26:48 -0800 Subject: [PATCH 104/381] add flags to support asking for specific messages, update examples --- libraries/entities/src/EntityEditFilters.cpp | 26 +++++++++++++++++++ libraries/entities/src/EntityEditFilters.h | 6 +++++ .../prevent-all-deletes.js | 22 ++++++++++++++++ .../prevent-delete-in-zone-example.js | 8 +++--- 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 scripts/tutorials/entity_edit_filters/prevent-all-deletes.js diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 4b66b61d93..cd1982bdb4 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -62,6 +62,16 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper return false; } + // check to see if this filter wants to filter this message type + if ((!filterData.wantsToFilterEdit && filterType == EntityTree::FilterType::Edit) || + (!filterData.wantsToFilterPhysics && filterType == EntityTree::FilterType::Physics) || + (!filterData.wantsToFilterDelete && filterType == EntityTree::FilterType::Delete) || + (!filterData.wantsToFilterAdd && filterType == EntityTree::FilterType::Add)) { + + wasChanged = false; + return true; // accept the message + } + auto oldProperties = propertiesIn.getDesiredProperties(); auto specifiedProperties = propertiesIn.getChangedProperties(); propertiesIn.setDesiredProperties(specifiedProperties); @@ -254,6 +264,22 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { filterData.rejectAll=true; } + // if the wantsToFilterEdit is a boolean evaluate as a boolean, otherwise assume true + QScriptValue wantsToFilterAddValue = filterData.filterFn.property("wantsToFilterAdd"); + filterData.wantsToFilterAdd = wantsToFilterAddValue.isBool() ? wantsToFilterAddValue.toBool() : true; + + // if the wantsToFilterEdit is a boolean evaluate as a boolean, otherwise assume true + QScriptValue wantsToFilterEditValue = filterData.filterFn.property("wantsToFilterEdit"); + filterData.wantsToFilterEdit = wantsToFilterEditValue.isBool() ? wantsToFilterEditValue.toBool() : true; + + // if the wantsToFilterPhysics is a boolean evaluate as a boolean, otherwise assume true + QScriptValue wantsToFilterPhysicsValue = filterData.filterFn.property("wantsToFilterPhysics"); + filterData.wantsToFilterPhysics = wantsToFilterPhysicsValue.isBool() ? wantsToFilterPhysicsValue.toBool() : true; + + // if the wantsToFilterDelete is a boolean evaluate as a boolean, otherwise assume false + QScriptValue wantsToFilterDeleteValue = filterData.filterFn.property("wantsToFilterDelete"); + filterData.wantsToFilterDelete = wantsToFilterDeleteValue.isBool() ? wantsToFilterDeleteValue.toBool() : false; + // check to see if the filterFn has properties asking for Original props QScriptValue wantsOriginalPropertiesValue = filterData.filterFn.property("wantsOriginalProperties"); // if the wantsOriginalProperties is a boolean, or a string, or list of strings, then evaluate as follows: diff --git a/libraries/entities/src/EntityEditFilters.h b/libraries/entities/src/EntityEditFilters.h index be3df50d3f..cb99c97762 100644 --- a/libraries/entities/src/EntityEditFilters.h +++ b/libraries/entities/src/EntityEditFilters.h @@ -30,6 +30,12 @@ public: QScriptValue filterFn; bool wantsOriginalProperties { false }; bool wantsZoneProperties { false }; + + bool wantsToFilterAdd { true }; + bool wantsToFilterEdit { true }; + bool wantsToFilterPhysics { true }; + bool wantsToFilterDelete { true }; + EntityPropertyFlags includedOriginalProperties; EntityPropertyFlags includedZoneProperties; bool wantsZoneBoundingBox { false }; diff --git a/scripts/tutorials/entity_edit_filters/prevent-all-deletes.js b/scripts/tutorials/entity_edit_filters/prevent-all-deletes.js new file mode 100644 index 0000000000..0e2c54a04a --- /dev/null +++ b/scripts/tutorials/entity_edit_filters/prevent-all-deletes.js @@ -0,0 +1,22 @@ +// +// prevent-all-deletes.js +// +// +// Created by Brad Hefta-Gaub to use Entities on Jan. 25, 2018 +// Copyright 2018 High Fidelity, Inc. +// +// This sample entity edit filter script will prevent deletes of any entities. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function filter() { + return false; // all deletes are blocked +} + +filter.wantsToFilterAdd = false; // don't run on adds +filter.wantsToFilterEdit = false; // don't run on edits +filter.wantsToFilterPhysics = false; // don't run on physics +filter.wantsToFilterDelete = true; // do run on deletes +filter; \ No newline at end of file diff --git a/scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js b/scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js index 94ee58738d..521bb94d00 100644 --- a/scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js +++ b/scripts/tutorials/entity_edit_filters/prevent-delete-in-zone-example.js @@ -5,13 +5,14 @@ // Created by Brad Hefta-Gaub to use Entities on Jan. 25, 2018 // Copyright 2018 High Fidelity, Inc. // -// This sample entity edit filter script will keep prevent any entity inside the zone from being deleted. +// This sample entity edit filter script will get all edits, adds, physcis, and deletes, but will only block +// deletes, and will pass through all others. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -function filter(properties, type, originalProperties, zoneProperties) { +function filter(properties, type) { if (type == Entities.DELETE_FILTER_TYPE) { return false; @@ -19,6 +20,5 @@ function filter(properties, type, originalProperties, zoneProperties) { return properties; } -filter.wantsOriginalProperties = true; -filter.wantsZoneProperties = true; +filter.wantsToFilterDelete = true; // do run on deletes filter; \ No newline at end of file From efc63a41e9551583c3eeb62c41e52e37789594c9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jan 2018 18:49:55 -0800 Subject: [PATCH 105/381] make sure true results for delete actually work, add more examples --- libraries/entities/src/EntityEditFilters.cpp | 13 +++++++++- ...event-add-of-entities-named-bob-example.js | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 scripts/tutorials/entity_edit_filters/prevent-add-of-entities-named-bob-example.js diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index cd1982bdb4..12bf1ac231 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -125,7 +125,7 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper return false; } - if (result.isObject()){ + if (result.isObject()) { // make propertiesIn reflect the changes, for next filter... propertiesIn.copyFromScriptValue(result, false); @@ -134,6 +134,17 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper // Javascript objects are == only if they are the same object. To compare arbitrary values, we need to use JSON. auto out = QJsonValue::fromVariant(result.toVariant()); wasChanged |= (in != out); + } else if (result.isBool()) { + + // if the filter returned false, then it's authoritative + if (!result.toBool()) { + return false; + } + + // otherwise, assume it wants to pass all properties + propertiesOut = propertiesIn; + wasChanged = false; + } else { return false; } diff --git a/scripts/tutorials/entity_edit_filters/prevent-add-of-entities-named-bob-example.js b/scripts/tutorials/entity_edit_filters/prevent-add-of-entities-named-bob-example.js new file mode 100644 index 0000000000..03fbe97430 --- /dev/null +++ b/scripts/tutorials/entity_edit_filters/prevent-add-of-entities-named-bob-example.js @@ -0,0 +1,26 @@ +// +// prevent-add-of-entities-named-bob-example.js +// +// +// Created by Brad Hefta-Gaub to use Entities on Jan. 25, 2018 +// Copyright 2018 High Fidelity, Inc. +// +// This sample entity edit filter script will get all edits, adds, physcis, and deletes, but will only block +// deletes, and will pass through all others. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function filter(properties, type) { + if (properties.name == "bob") { + return false; + } + return properties; +} + +filter.wantsToFilterAdd = true; // do run on add +filter.wantsToFilterEdit = false; // do not run on edit +filter.wantsToFilterPhysics = false; // do not run on physics +filter.wantsToFilterDelete = false; // do not run on delete +filter; \ No newline at end of file From 221475d7d9097b8c097656e57f4f5d1b567c568b Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Sat, 27 Jan 2018 10:37:32 -0800 Subject: [PATCH 106/381] FIxing the dark ambient lighting on scattering surface --- libraries/render-utils/src/LightAmbient.slh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index eb565d60e4..acd30d527d 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -99,8 +99,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie // Diffuse from ambient diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz; - diffuse /= 3.1415926; - specular = vec3(0.0); + + // Scattering ambient specular is the same as non scattering for now + // TODO: we should use the same specular answer as for direct lighting } <@endif@> From 898c99e5b83a7b5315f626e152bcb82933daecb9 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 27 Jan 2018 19:50:52 +0100 Subject: [PATCH 107/381] append file jsdoc json file to resources.rcc resource file --- cmake/macros/GenerateQrc.cmake | 9 ++++++- interface/CMakeLists.txt | 49 +++++++++++++--------------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/cmake/macros/GenerateQrc.cmake b/cmake/macros/GenerateQrc.cmake index 9bf530b2a2..5e2be71c82 100644 --- a/cmake/macros/GenerateQrc.cmake +++ b/cmake/macros/GenerateQrc.cmake @@ -1,7 +1,7 @@ function(GENERATE_QRC) set(oneValueArgs OUTPUT PREFIX PATH) - set(multiValueArgs GLOBS) + set(multiValueArgs CUSTOM_PATHS GLOBS) cmake_parse_arguments(GENERATE_QRC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) if ("${GENERATE_QRC_PREFIX}" STREQUAL "") set(QRC_PREFIX_PATH /) @@ -20,6 +20,13 @@ function(GENERATE_QRC) endforeach() endforeach() + foreach(CUSTOM_PATH ${GENERATE_QRC_CUSTOM_PATHS}) + string(REPLACE "=" ";" CUSTOM_PATH ${CUSTOM_PATH}) + list(GET CUSTOM_PATH 0 IMPORT_PATH) + list(GET CUSTOM_PATH 1 LOCAL_PATH) + set(QRC_CONTENTS "${QRC_CONTENTS}${IMPORT_PATH}\n") + endforeach() + set(GENERATE_QRC_DEPENDS ${ALL_FILES} PARENT_SCOPE) configure_file("${HF_CMAKE_DIR}/templates/resources.qrc.in" ${GENERATE_QRC_OUTPUT}) endfunction() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d0d89919d5..fc3bc4a0d1 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -11,9 +11,22 @@ function(JOIN VALUES GLUE OUTPUT) set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) endfunction() +set(CUSTOM_INTERFACE_QRC_PATHS "") + +function(ADD_CUSTOM_QRC_PATH CUSTOM_PATHS_VAR IMPORT_PATH LOCAL_PATH) + list(APPEND ${CUSTOM_PATHS_VAR} "${IMPORT_PATH}=${LOCAL_PATH}") + set(${CUSTOM_PATHS_VAR} ${${CUSTOM_PATHS_VAR}} PARENT_SCOPE) +endfunction() + +find_npm() + +if (BUILD_TOOLS AND NPM_EXECUTABLE) + add_custom_qrc_path(CUSTOM_INTERFACE_QRC_PATHS "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "auto-complete/hifiJSDoc.json") +endif () + set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc) -generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *) +generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources CUSTOM_PATHS ${CUSTOM_INTERFACE_QRC_PATHS} GLOBS *) add_custom_command( OUTPUT ${RESOURCES_RCC} @@ -53,8 +66,6 @@ file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") list(APPEND INTERFACE_SRCS ${RESOURCES_RCC}) -find_npm() - # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) # Use .cpp and .h files as is. @@ -165,6 +176,11 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () +if (BUILD_TOOLS AND NPM_EXECUTABLE) + # require JSDoc to be build before interface is deployed (Console Auto-complete) + add_dependencies(resources jsdoc) +endif() + add_dependencies(${TARGET_NAME} resources) if (WIN32) @@ -276,11 +292,6 @@ endif() # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) -if (BUILD_TOOLS) - # require JSDoc to be build before interface is deployed (Console Auto-complete) - add_dependencies(${TARGET_NAME} jsdoc) -endif() - if (APPLE) # link in required OS X frameworks and include the right GL headers find_library(OpenGL OpenGL) @@ -296,38 +307,18 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") - if (BUILD_TOOLS AND NPM_EXECUTABLE) - set(EXTRA_COPY_COMMANDS - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/../Resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/../Resources/auto-complete" - ) - endif() # copy script files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" - - ${EXTRA_COPY_COMMANDS} ) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() else() - if (BUILD_TOOLS AND NPM_EXECUTABLE) - set(EXTRA_COPY_COMMANDS - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/resources/auto-complete" - ) - endif() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_if_different @@ -344,8 +335,6 @@ else() COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" - - ${EXTRA_COPY_COMMANDS} ) # link target to external libraries From 8fdbac521fc6b010248921dfb0d63bc2e6bee0a5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 27 Jan 2018 19:58:15 +0100 Subject: [PATCH 108/381] move ADD_CUSTOM_QRC_PATH function to its own file --- cmake/macros/AddCustomQrcPath.cmake | 7 +++++++ interface/CMakeLists.txt | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 cmake/macros/AddCustomQrcPath.cmake diff --git a/cmake/macros/AddCustomQrcPath.cmake b/cmake/macros/AddCustomQrcPath.cmake new file mode 100644 index 0000000000..6bd54baa4d --- /dev/null +++ b/cmake/macros/AddCustomQrcPath.cmake @@ -0,0 +1,7 @@ +# adds a custom path and local path to the inserted CUSTOM_PATHS_VAR list which +# can be given to the GENERATE_QRC command + +function(ADD_CUSTOM_QRC_PATH CUSTOM_PATHS_VAR IMPORT_PATH LOCAL_PATH) + list(APPEND ${CUSTOM_PATHS_VAR} "${IMPORT_PATH}=${LOCAL_PATH}") + set(${CUSTOM_PATHS_VAR} ${${CUSTOM_PATHS_VAR}} PARENT_SCOPE) +endfunction() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fc3bc4a0d1..a49019e775 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -13,11 +13,6 @@ endfunction() set(CUSTOM_INTERFACE_QRC_PATHS "") -function(ADD_CUSTOM_QRC_PATH CUSTOM_PATHS_VAR IMPORT_PATH LOCAL_PATH) - list(APPEND ${CUSTOM_PATHS_VAR} "${IMPORT_PATH}=${LOCAL_PATH}") - set(${CUSTOM_PATHS_VAR} ${${CUSTOM_PATHS_VAR}} PARENT_SCOPE) -endfunction() - find_npm() if (BUILD_TOOLS AND NPM_EXECUTABLE) From 0781d8eaf03a21a22c3050f633720a36aa874855 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 17 Jan 2018 22:19:21 -0800 Subject: [PATCH 109/381] Remove some unneeded Qt dependencies --- assignment-client/CMakeLists.txt | 2 +- gvr-interface/CMakeLists.txt | 4 ++-- interface/CMakeLists.txt | 4 ++-- libraries/avatars-renderer/CMakeLists.txt | 2 +- libraries/display-plugins/CMakeLists.txt | 2 +- libraries/entities-renderer/CMakeLists.txt | 2 +- libraries/gl/CMakeLists.txt | 2 +- libraries/gpu-gles/CMakeLists.txt | 2 +- libraries/networking/src/AddressManager.cpp | 6 +++--- libraries/plugins/CMakeLists.txt | 2 +- libraries/render-utils/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 1 + libraries/shared/CMakeLists.txt | 2 +- libraries/trackers/src/trackers/EyeTracker.cpp | 1 - libraries/ui-plugins/CMakeLists.txt | 2 +- libraries/ui/CMakeLists.txt | 2 +- plugins/hifiCodec/src/HiFiCodec.cpp | 3 +-- plugins/hifiKinect/CMakeLists.txt | 2 +- plugins/hifiLeapMotion/CMakeLists.txt | 2 +- plugins/hifiNeuron/CMakeLists.txt | 2 +- plugins/hifiSdl2/CMakeLists.txt | 2 +- plugins/hifiSdl2/src/SDL2Manager.cpp | 5 +++-- plugins/hifiSixense/CMakeLists.txt | 2 +- plugins/hifiSpacemouse/CMakeLists.txt | 2 +- plugins/openvr/CMakeLists.txt | 2 +- plugins/pcmCodec/src/PCMCodecManager.cpp | 2 +- tests/gpu-test/CMakeLists.txt | 2 +- tests/render-perf/CMakeLists.txt | 2 +- tests/render-texture-load/CMakeLists.txt | 2 +- tests/render-utils/CMakeLists.txt | 2 +- tests/shaders/CMakeLists.txt | 2 +- tools/ac-client/CMakeLists.txt | 2 +- tools/ac-client/src/ACClientApp.h | 2 +- tools/atp-client/CMakeLists.txt | 2 +- tools/atp-client/src/ATPClientApp.h | 2 +- tools/ice-client/CMakeLists.txt | 2 +- tools/ice-client/src/ICEClientApp.h | 2 +- tools/skeleton-dump/CMakeLists.txt | 2 +- tools/skeleton-dump/src/SkeletonDumpApp.h | 2 +- tools/vhacd-util/CMakeLists.txt | 2 +- tools/vhacd-util/src/VHACDUtilApp.h | 2 +- 41 files changed, 46 insertions(+), 46 deletions(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index acdc1cbc53..c73e8e1d34 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME assignment-client) -setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) +setup_hifi_project(Core Gui Network Script Quick WebSockets) # Fix up the rpath so macdeployqt works if (APPLE) diff --git a/gvr-interface/CMakeLists.txt b/gvr-interface/CMakeLists.txt index 7d6655e875..72f1096881 100644 --- a/gvr-interface/CMakeLists.txt +++ b/gvr-interface/CMakeLists.txt @@ -17,9 +17,9 @@ if (ANDROID) set(BUILD_SHARED_LIBS ON) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}") - setup_hifi_library(Gui Widgets AndroidExtras) + setup_hifi_library(Gui AndroidExtras) else () - setup_hifi_project(Gui Widgets) + setup_hifi_project(Gui) endif () include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 383c50ed44..0c4319a9b7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -70,7 +70,7 @@ endif () find_package( Qt5 COMPONENTS - Gui Multimedia Network OpenGL Qml Quick Script Svg + Gui Widgets Multimedia Network Qml Quick Script Svg ${PLATFORM_QT_COMPONENTS} WebChannel WebSockets ) @@ -255,7 +255,7 @@ endif () target_link_libraries( ${TARGET_NAME} - Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL + Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg Qt5::WebChannel ${PLATFORM_QT_LIBRARIES} diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index 53edc692f2..1f740700c5 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME avatars-renderer) AUTOSCRIBE_SHADER_LIB(gpu graphics render render-utils) -setup_hifi_library(Widgets Network Script) +setup_hifi_library(Network Script) link_hifi_libraries(shared gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer) include_hifi_library_headers(avatars) include_hifi_library_headers(networking) diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 1d9d42d579..7d34258c96 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME display-plugins) AUTOSCRIBE_SHADER_LIB(gpu display-plugins) -setup_hifi_library(OpenGL) +setup_hifi_library(Gui) link_hifi_libraries(shared plugins ui-plugins gl ui render-utils ${PLATFORM_GL_BACKEND}) include_hifi_library_headers(gpu) include_hifi_library_headers(model-networking) diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index ea75367e1e..4b94757dec 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils) -setup_hifi_library(Widgets Network Script) +setup_hifi_library(Network Script) link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image ui pointers) include_hifi_library_headers(networking) include_hifi_library_headers(gl) diff --git a/libraries/gl/CMakeLists.txt b/libraries/gl/CMakeLists.txt index 9fc7a0c10f..925cf9b288 100644 --- a/libraries/gl/CMakeLists.txt +++ b/libraries/gl/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gl) -setup_hifi_library(OpenGL Qml Quick) +setup_hifi_library(Gui Widgets Qml Quick) link_hifi_libraries(shared) target_opengl() diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index 16575a7018..ea69919f6d 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gpu-gles) -setup_hifi_library(Concurrent) +setup_hifi_library(Gui Concurrent) link_hifi_libraries(shared gl gpu) GroupSources("src") target_opengl() diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index fa5507fcf7..d58d41efe9 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include #include #include @@ -764,7 +764,7 @@ void AddressManager::copyAddress() { } // assume that the address is being copied because the user wants a shareable address - QApplication::clipboard()->setText(currentShareableAddress().toString()); + QGuiApplication::clipboard()->setText(currentShareableAddress().toString()); } void AddressManager::copyPath() { @@ -773,7 +773,7 @@ void AddressManager::copyPath() { return; } - QApplication::clipboard()->setText(currentPath()); + QGuiApplication::clipboard()->setText(currentPath()); } QString AddressManager::getDomainId() const { diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 6067c0fbed..1c0d74c45a 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME plugins) -setup_hifi_library(OpenGL) +setup_hifi_library(Gui) link_hifi_libraries(shared networking) include_hifi_library_headers(gpu) diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 6be3057c93..57e3572012 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME render-utils) AUTOSCRIBE_SHADER_LIB(gpu graphics render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") -setup_hifi_library(Widgets OpenGL Network Qml Quick Script) +setup_hifi_library(Gui Network Qml Quick Script) link_hifi_libraries(shared ktx gpu graphics model-networking render animation fbx image procedural) include_hifi_library_headers(networking) include_hifi_library_headers(octree) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 478e4c4765..31436bbf8b 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,4 +1,5 @@ set(TARGET_NAME script-engine) +# FIXME Move undo scripting interface to application and remove Widgets setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets) target_zlib() diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 3d61765988..92cebac869 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME shared) # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) -setup_hifi_library(Gui Network Script Widgets) +setup_hifi_library(Gui Network Script) if (WIN32) target_link_libraries(${TARGET_NAME} Wbemuuid.lib) diff --git a/libraries/trackers/src/trackers/EyeTracker.cpp b/libraries/trackers/src/trackers/EyeTracker.cpp index e641abc630..a64b945c55 100644 --- a/libraries/trackers/src/trackers/EyeTracker.cpp +++ b/libraries/trackers/src/trackers/EyeTracker.cpp @@ -9,7 +9,6 @@ #include "EyeTracker.h" #include -#include #include #include diff --git a/libraries/ui-plugins/CMakeLists.txt b/libraries/ui-plugins/CMakeLists.txt index 8da0815082..8f4123c1d4 100644 --- a/libraries/ui-plugins/CMakeLists.txt +++ b/libraries/ui-plugins/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME ui-plugins) -setup_hifi_library(OpenGL) +setup_hifi_library(Gui) link_hifi_libraries(shared plugins ui) include_hifi_library_headers(gpu) \ No newline at end of file diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 2dd23f5134..d16377587c 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME ui) -setup_hifi_library(OpenGL Multimedia Network Qml Quick Script WebChannel WebSockets XmlPatterns ${PLATFORM_QT_COMPONENTS}) +setup_hifi_library(Widgets Multimedia Network Qml Quick Script WebChannel WebSockets XmlPatterns ${PLATFORM_QT_COMPONENTS}) link_hifi_libraries(shared networking gl audio audio-client plugins pointers) include_hifi_library_headers(controllers) diff --git a/plugins/hifiCodec/src/HiFiCodec.cpp b/plugins/hifiCodec/src/HiFiCodec.cpp index f78bbae2c1..99bb411539 100644 --- a/plugins/hifiCodec/src/HiFiCodec.cpp +++ b/plugins/hifiCodec/src/HiFiCodec.cpp @@ -9,12 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "HiFiCodec.h" #include #include -#include "HiFiCodec.h" const char* HiFiCodec::NAME { "hifiAC" }; diff --git a/plugins/hifiKinect/CMakeLists.txt b/plugins/hifiKinect/CMakeLists.txt index a7088eb3b4..97f78b0c11 100644 --- a/plugins/hifiKinect/CMakeLists.txt +++ b/plugins/hifiKinect/CMakeLists.txt @@ -10,7 +10,7 @@ if (WIN32) find_package(KINECT) if (KINECT_FOUND) set(TARGET_NAME hifiKinect) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins) # need to setup appropriate externals... diff --git a/plugins/hifiLeapMotion/CMakeLists.txt b/plugins/hifiLeapMotion/CMakeLists.txt index 14f9bbaa17..3075107bb3 100644 --- a/plugins/hifiLeapMotion/CMakeLists.txt +++ b/plugins/hifiLeapMotion/CMakeLists.txt @@ -9,7 +9,7 @@ find_package(LEAPMOTION) if (LEAPMOTION_FOUND) set(TARGET_NAME hifiLeapMotion) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins) target_leapmotion() endif() diff --git a/plugins/hifiNeuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt index a9ed8cca6e..dd78b52240 100644 --- a/plugins/hifiNeuron/CMakeLists.txt +++ b/plugins/hifiNeuron/CMakeLists.txt @@ -9,7 +9,7 @@ if (APPLE OR WIN32) set(TARGET_NAME hifiNeuron) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins) target_neuron() diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt index 7e499e314a..8b2bb114a0 100644 --- a/plugins/hifiSdl2/CMakeLists.txt +++ b/plugins/hifiSdl2/CMakeLists.txt @@ -7,6 +7,6 @@ # set(TARGET_NAME hifiSdl2) -setup_hifi_plugin(Script Qml Widgets) +setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins script-engine) target_sdl2() diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index a5376af24e..664e53d115 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -9,14 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "SDL2Manager.h" + +#include #include #include #include #include -#include "SDL2Manager.h" static_assert( (int)controller::A == (int)SDL_CONTROLLER_BUTTON_A && diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt index 6e642fce29..e84a4587c4 100644 --- a/plugins/hifiSixense/CMakeLists.txt +++ b/plugins/hifiSixense/CMakeLists.txt @@ -12,7 +12,7 @@ # the msvcr100 runtime support, the plugin will not load. if (NOT ANDROID) set(TARGET_NAME hifiSixense) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) target_sixense() if (WIN32) diff --git a/plugins/hifiSpacemouse/CMakeLists.txt b/plugins/hifiSpacemouse/CMakeLists.txt index bcfb309a69..174d0822ec 100644 --- a/plugins/hifiSpacemouse/CMakeLists.txt +++ b/plugins/hifiSpacemouse/CMakeLists.txt @@ -10,7 +10,7 @@ if(WIN32) set(TARGET_NAME hifiSpacemouse) find_package(3DCONNEXIONCLIENT) if (3DCONNEXIONCLIENT_FOUND) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared networking controllers ui plugins input-plugins) target_include_directories(${TARGET_NAME} PUBLIC ${3DCONNEXIONCLIENT_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${3DCONNEXIONCLIENT_LIBRARIES}) diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 3581009284..487a005068 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -10,7 +10,7 @@ if (WIN32 AND (NOT USE_GLES)) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) set(TARGET_NAME openvr) - setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia) + setup_hifi_plugin(Gui Qml Multimedia) link_hifi_libraries(shared gl networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine audio-client render-utils graphics gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND}) diff --git a/plugins/pcmCodec/src/PCMCodecManager.cpp b/plugins/pcmCodec/src/PCMCodecManager.cpp index 7278edaf92..051f3973a8 100644 --- a/plugins/pcmCodec/src/PCMCodecManager.cpp +++ b/plugins/pcmCodec/src/PCMCodecManager.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 2092279159..7bc1349091 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME gpu-test) AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL Script Widgets) +setup_hifi_project(Quick Gui Script) setup_memory_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") link_hifi_libraries( diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 5314f7a45b..d377b7616d 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -8,7 +8,7 @@ endif() setup_memory_debugger() # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries diff --git a/tests/render-texture-load/CMakeLists.txt b/tests/render-texture-load/CMakeLists.txt index dbfb7d33db..2ed905a3ef 100644 --- a/tests/render-texture-load/CMakeLists.txt +++ b/tests/render-texture-load/CMakeLists.txt @@ -8,7 +8,7 @@ endif() setup_memory_debugger() # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index e3b1523a96..be75c53f2e 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME render-utils-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index 76d2aa100c..44394db6a0 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME shaders-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() diff --git a/tools/ac-client/CMakeLists.txt b/tools/ac-client/CMakeLists.txt index 24eeadba9c..ad16187dcb 100644 --- a/tools/ac-client/CMakeLists.txt +++ b/tools/ac-client/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME ac-client) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared networking) diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h index e295b17654..d43e78eaeb 100644 --- a/tools/ac-client/src/ACClientApp.h +++ b/tools/ac-client/src/ACClientApp.h @@ -13,7 +13,7 @@ #ifndef hifi_ACClientApp_h #define hifi_ACClientApp_h -#include +#include #include #include #include diff --git a/tools/atp-client/CMakeLists.txt b/tools/atp-client/CMakeLists.txt index 4cee30bcc3..19c70597f7 100644 --- a/tools/atp-client/CMakeLists.txt +++ b/tools/atp-client/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME atp-client) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared networking) diff --git a/tools/atp-client/src/ATPClientApp.h b/tools/atp-client/src/ATPClientApp.h index a3904d6e50..eaf1043153 100644 --- a/tools/atp-client/src/ATPClientApp.h +++ b/tools/atp-client/src/ATPClientApp.h @@ -13,7 +13,7 @@ #ifndef hifi_ATPClientApp_h #define hifi_ATPClientApp_h -#include +#include #include #include #include diff --git a/tools/ice-client/CMakeLists.txt b/tools/ice-client/CMakeLists.txt index ae42d79f7e..64ec6b131d 100644 --- a/tools/ice-client/CMakeLists.txt +++ b/tools/ice-client/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME ice-client) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared networking) diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index de6b6abb14..642b9bc36b 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -13,7 +13,7 @@ #ifndef hifi_ICEClientApp_h #define hifi_ICEClientApp_h -#include +#include #include #include #include diff --git a/tools/skeleton-dump/CMakeLists.txt b/tools/skeleton-dump/CMakeLists.txt index 1c30e322d6..d0a6475c59 100644 --- a/tools/skeleton-dump/CMakeLists.txt +++ b/tools/skeleton-dump/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME skeleton-dump) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared fbx graphics gpu gl animation) diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.h b/tools/skeleton-dump/src/SkeletonDumpApp.h index 40df98eb65..272f7563fa 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.h +++ b/tools/skeleton-dump/src/SkeletonDumpApp.h @@ -12,7 +12,7 @@ #ifndef hifi_SkeletonDumpApp_h #define hifi_SkeletonDumpApp_h -#include +#include class SkeletonDumpApp : public QCoreApplication { Q_OBJECT diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index 599561bd2d..5ba8b7d971 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME vhacd-util) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) link_hifi_libraries(shared fbx graphics gpu gl) add_dependency_external_projects(vhacd) diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index ebb8164634..0d75275802 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -13,7 +13,7 @@ #ifndef hifi_VHACDUtilApp_h #define hifi_VHACDUtilApp_h -#include +#include #include From 4cbfef55efc3c0861612eebbc70e444069b659a7 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 29 Jan 2018 10:33:16 +0100 Subject: [PATCH 110/381] Merged with master. Had to add back shaders and code --- .../render-utils/src/DeferredGlobalLight.slh | 7 ++ .../render-utils/src/RenderPipelines.cpp | 22 ++-- .../render-utils/src/model_translucent.slf | 2 +- .../src/model_translucent_fade.slf | 2 +- .../src/model_translucent_normal_map.slf | 91 ++++++++++++++++ .../src/model_translucent_normal_map.slv | 46 ++++++++ .../src/model_translucent_normal_map_fade.slf | 101 ++++++++++++++++++ .../src/overlay3D_model_translucent.slf | 2 +- .../src/simple_transparent_textured.slf | 2 +- .../src/simple_transparent_textured_fade.slf | 2 +- 10 files changed, 264 insertions(+), 13 deletions(-) create mode 100644 libraries/render-utils/src/model_translucent_normal_map.slf create mode 100644 libraries/render-utils/src/model_translucent_normal_map.slv create mode 100644 libraries/render-utils/src/model_translucent_normal_map_fade.slf diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index fae645fdbc..faf8641926 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -204,6 +204,13 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl return color; } +<@endfunc@> + +<@func declareEvalGlobalLightingAlphaBlendedWithHaze()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> + vec3 evalGlobalLightingAlphaBlendedWithHaze( mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 1c828c3d06..900eacec12 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -31,6 +31,7 @@ #include "model_lightmap_normal_map_fade_vert.h" #include "model_translucent_vert.h" #include "model_translucent_fade_vert.h" +#include "model_translucent_normal_map_vert.h" #include "skin_model_fade_vert.h" #include "skin_model_normal_map_fade_vert.h" @@ -73,12 +74,14 @@ #include "model_lightmap_specular_map_frag.h" #include "model_translucent_frag.h" #include "model_translucent_unlit_frag.h" +#include "model_translucent_normal_map_frag.h" #include "model_lightmap_fade_frag.h" #include "model_lightmap_normal_map_fade_frag.h" #include "model_lightmap_normal_specular_map_fade_frag.h" #include "model_lightmap_specular_map_fade_frag.h" #include "model_translucent_fade_frag.h" +#include "model_translucent_normal_map_fade_frag.h" #include "model_translucent_unlit_fade_frag.h" #include "overlay3D_vert.h" @@ -191,6 +194,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); auto modelTranslucentVertex = gpu::Shader::createVertex(std::string(model_translucent_vert)); + auto modelTranslucentNormalMapVertex = gpu::Shader::createVertex(std::string(model_translucent_normal_map_vert)); auto modelTranslucentFadeVertex = gpu::Shader::createVertex(std::string(model_translucent_fade_vert)); auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); @@ -220,6 +224,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); + auto modelTranslucentNormalMapPixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_frag)); auto modelTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_frag)); auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); @@ -238,6 +243,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_fade_frag)); auto modelShadowFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); auto modelTranslucentFadePixel = gpu::Shader::createPixel(std::string(model_translucent_fade_frag)); + auto modelTranslucentNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_fade_frag)); auto modelTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_fade_frag)); auto simpleFadePixel = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); auto simpleUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); @@ -307,13 +313,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleVertex, simpleTranslucentUnlitPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents(), - modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular(), modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular(), - modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap(), @@ -333,13 +339,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleFadeVertex, simpleTranslucentUnlitFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withFade(), - modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular().withFade(), modelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular().withFade(), - modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap().withFade(), @@ -405,26 +411,26 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents(), - skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular(), skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular(), - skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withFade(), skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withFade(), - skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular().withFade(), skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular().withFade(), - skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); // Depth-only addPipeline( diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 761e702595..67a5651ab8 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index a2d271653c..316dae7aad 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> diff --git a/libraries/render-utils/src/model_translucent_normal_map.slf b/libraries/render-utils/src/model_translucent_normal_map.slf new file mode 100644 index 0000000000..759007d93e --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map.slf @@ -0,0 +1,91 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_translucent_normal_map.frag +// fragment shader +// +// Created by Olivier Prat on 23/01/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include graphics/Material.slh@> + +<@include DeferredGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> + +<@include LightLocal.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec4 _worldPosition; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; +in float _alpha; + +out vec4 _fragColor; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + vec3 fragNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } + + _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + albedo, + fresnel, + metallic, + emissive, + surface, opacity, localLighting.rgb), + opacity); +} diff --git a/libraries/render-utils/src/model_translucent_normal_map.slv b/libraries/render-utils/src/model_translucent_normal_map.slv new file mode 100644 index 0000000000..db824a3709 --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map.slv @@ -0,0 +1,46 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model_translucent_normal_map.slv +// vertex shader +// +// Created by Olivier Prat on 23/01/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out float _alpha; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec4 _position; +out vec4 _worldPosition; +out vec3 _normal; +out vec3 _tangent; +out vec3 _color; + +void main(void) { + _color = colorToLinearRGB(inColor.xyz); + _alpha = inColor.w; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> + <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> + <$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$> +} diff --git a/libraries/render-utils/src/model_translucent_normal_map_fade.slf b/libraries/render-utils/src/model_translucent_normal_map_fade.slf new file mode 100644 index 0000000000..204b5ac56b --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map_fade.slf @@ -0,0 +1,101 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_translucent_normal_map_fade.frag +// fragment shader +// +// Created by Olivier Prat on 23/01/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include graphics/Material.slh@> + +<@include DeferredGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> + +<@include LightLocal.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +<@include Fade.slh@> +<$declareFadeFragment()$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; +in float _alpha; +in vec4 _worldPosition; + +out vec4 _fragColor; + +void main(void) { + vec3 fadeEmissive; + FadeObjectParams fadeParams; + + <$fetchFadeObjectParams(fadeParams)$> + applyFade(fadeParams, _worldPosition.xyz, fadeEmissive); + + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + // Lighting is done in world space + vec3 fragNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } + + _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + albedo, + fresnel, + metallic, + emissive+fadeEmissive, + surface, opacity, localLighting.rgb), + opacity); +} diff --git a/libraries/render-utils/src/overlay3D_model_translucent.slf b/libraries/render-utils/src/overlay3D_model_translucent.slf index 8dd3a81443..b66c114f4a 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent.slf @@ -11,7 +11,7 @@ // <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include graphics/Material.slh@> diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index b16b19c8b4..30c420233f 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -16,7 +16,7 @@ <@include DeferredBufferWrite.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> diff --git a/libraries/render-utils/src/simple_transparent_textured_fade.slf b/libraries/render-utils/src/simple_transparent_textured_fade.slf index ad260210a7..a8a5875a4b 100644 --- a/libraries/render-utils/src/simple_transparent_textured_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_fade.slf @@ -16,7 +16,7 @@ <@include DeferredBufferWrite.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> From 0324f41565c77acdda2d326560aeba2a2f260498 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 29 Jan 2018 17:23:35 +0100 Subject: [PATCH 111/381] Trying to improve adaptive shadow bias --- libraries/render-utils/src/LightStage.cpp | 35 ++++++++++++++----- libraries/render-utils/src/LightStage.h | 3 ++ .../render-utils/src/RenderShadowTask.cpp | 4 ++- libraries/render-utils/src/RenderShadowTask.h | 2 +- libraries/render-utils/src/Shadow.slh | 15 +++++--- libraries/render-utils/src/ShadowCore.slh | 14 ++++++-- libraries/render-utils/src/Shadows_shared.slh | 8 ++--- 7 files changed, 58 insertions(+), 23 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index eac9dd7657..e06d24f1b1 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,8 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.5, 0.5, 1.0 }; const int LightStage::Shadow::MAP_SIZE = 1024; -static const auto MAX_BIAS = 0.006f; - const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -63,7 +61,7 @@ LightStage::LightStage() { LightStage::Shadow::Schema::Schema() { ShadowTransform defaultTransform; - defaultTransform.bias = MAX_BIAS; + defaultTransform.fixedBias = 0.005f; std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; cascadeCount = 1; @@ -72,6 +70,18 @@ LightStage::Shadow::Schema::Schema() { maxDistance = 20.0f; } +void LightStage::Shadow::Schema::updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum) { + auto& cascade = cascades[cascadeIndex]; + cascade.frustumPosition = shadowFrustum.getPosition(); + // The adaptative bias is computing how much depth offset we have to add to + // push back a coarsely sampled surface perpendicularly to the shadow direction, + // to not have shadow acnee. The final computation is done in the shader, based on the + // surface normal. + auto maxWorldFrustumSize = glm::max(shadowFrustum.getWidth(), shadowFrustum.getHeight()); + cascade.adaptiveBiasUnitScale = maxWorldFrustumSize * 0.5f * invMapSize; + cascade.adaptiveBiasTransformScale = shadowFrustum.getNearClip() * shadowFrustum.getFarClip() / (shadowFrustum.getFarClip() - shadowFrustum.getNearClip()); +} + LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() }, _minDistance{ 0.0f }, @@ -210,13 +220,15 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, // Position the keylight frustum auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection; + // Update the buffer + auto& schema = _schemaBuffer.edit(); + auto cascadeIndex = 0; for (auto& cascade : _cascades) { cascade._frustum->setOrientation(orientation); cascade._frustum->setPosition(position); + schema.cascades[cascadeIndex].frustumPosition = position; + cascadeIndex++; } - // Update the buffer - auto& schema = _schemaBuffer.edit(); - schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f); } void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, @@ -269,8 +281,10 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co // Update the buffer auto& schema = _schemaBuffer.edit(); - schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); - schema.cascades[cascadeIndex].bias = baseBias; + auto& schemaCascade = schema.cascades[cascadeIndex]; + schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); + schemaCascade.fixedBias = baseBias; + schema.updateCascade(cascadeIndex, *cascade._frustum); } void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { @@ -281,7 +295,10 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View *cascade._frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); + auto& schema = _schemaBuffer.edit(); + auto& schemaCascade = schema.cascades[cascadeIndex]; + schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); + schema.updateCascade(cascadeIndex, shadowFrustum); } LightStage::Index LightStage::findLight(const LightPointer& light) const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index c2c2df8c89..922ec5eb4a 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -110,6 +110,8 @@ public: Schema(); + void updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum); + }; UniformBufferView _schemaBuffer = nullptr; }; @@ -213,6 +215,7 @@ protected: Index _sunOffLightId; Index _defaultLightId; + }; using LightStagePointer = std::shared_ptr; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index f41860b6de..2172dda3e3 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -256,7 +256,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { } void RenderShadowCascadeSetup::configure(const Config& configuration) { - _baseBias = configuration.bias * configuration.bias * 0.01f; + // I'm not very proud of this empirical adjustment + auto cascadeBias = configuration.bias * powf(1.1f, _cascadeIndex); + _baseBias = cascadeBias * cascadeBias * 0.01f; } void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 42c279c02a..c4f0c65bfc 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -67,7 +67,7 @@ class RenderShadowCascadeSetupConfig : public render::Job::Config { Q_PROPERTY(float bias MEMBER bias NOTIFY dirty) public: - float bias{ 0.5f }; + float bias{ 0.25f }; signals: void dirty(); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 50ae8864f4..c11f5fa6a7 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -83,11 +83,17 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) { +float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias, + vec3 worldPosition, vec3 worldLightDir) { if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } + // After this, bias is in view units + bias *= getShadowAdaptiveBiasUnitScale(cascadeIndex); + // Transform to texcoord space (between 0 and 1) + float shadowDepth = abs(dot(worldPosition-getShadowFrustumPosition(cascadeIndex), worldLightDir)); + bias = transformShadowAdaptiveBias(cascadeIndex, shadowDepth, bias); // Add fixed bias bias += getShadowBias(cascadeIndex); return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); @@ -101,12 +107,11 @@ float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDe // Adjust bias if we are at a grazing angle with light float ndotl = dot(worldLightDir, worldNormal); - float bias = 0.5*(1.0/(ndotl*ndotl)-1.0); - bias *= getShadowScale(); + float bias = 1.0/(ndotl*ndotl)-1.0; vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias, worldPosition.xyz, worldLightDir); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias, worldPosition.xyz, worldLightDir); } float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); // Falloff to max distance diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 9b3b086598..2d48e16ef4 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -38,11 +38,19 @@ float getShadowScale() { } float getShadowBias(int cascadeIndex) { - return shadow.cascades[cascadeIndex].bias; + return shadow.cascades[cascadeIndex].fixedBias; } -vec3 getShadowDirInViewSpace() { - return shadow.lightDirInViewSpace; +vec3 getShadowFrustumPosition(int cascadeIndex) { + return shadow.cascades[cascadeIndex].frustumPosition; +} + +float getShadowAdaptiveBiasUnitScale(int cascadeIndex) { + return shadow.cascades[cascadeIndex].adaptiveBiasUnitScale; +} + +float transformShadowAdaptiveBias(int cascadeIndex, float shadowDepth, float depthBias) { + return shadow.cascades[cascadeIndex].adaptiveBiasTransformScale * depthBias / (shadowDepth*shadowDepth); } // Compute the texture coordinates from world coordinates diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index abb226421c..def6b1b4d4 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -11,16 +11,16 @@ struct ShadowTransform { MAT4 reprojection; - - float bias; + VEC3 frustumPosition; + float fixedBias; + float adaptiveBiasUnitScale; + float adaptiveBiasTransformScale; float _padding1; float _padding2; - float _padding3; }; struct ShadowParameters { ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; - VEC3 lightDirInViewSpace; int cascadeCount; float invMapSize; float invCascadeBlendWidth; From 0e3a01f1e8a1b1671076731f274d27b612bb65bd Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 29 Jan 2018 10:20:16 -0800 Subject: [PATCH 112/381] it's working! removed my null pointer and just get it from the dependency manager when required --- interface/src/scripting/WalletScriptingInterface.cpp | 5 ++--- interface/src/scripting/WalletScriptingInterface.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 8d6dc7bbcd..87c5c89ab3 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -16,7 +16,6 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q } WalletScriptingInterface::WalletScriptingInterface() { - _contextOverlayInterface = DependencyManager::get(); _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; @@ -43,9 +42,9 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui if (!entityID.isNull() && entityProperties.getMarketplaceID().length() > 0) { if (!entityProperties.getClientOnly()) { qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; - emit _contextOverlayInterface->ownershipVerificationFailed(entityID); + emit DependencyManager::get()->ownershipVerificationFailed(entityID); return; } - _contextOverlayInterface->requestOwnershipVerification(entityID); + DependencyManager::get()->requestOwnershipVerification(entityID); } } \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index c725c34685..8e2dcd47a8 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -34,8 +34,6 @@ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) - QSharedPointer _contextOverlayInterface; - public: WalletScriptingInterface(); From 68fd9eafa8c3fd7409d410750be1cae162ec89b1 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 29 Jan 2018 10:23:24 -0800 Subject: [PATCH 113/381] fix entity parenting crash --- libraries/entities/src/EntityTree.cpp | 15 +++++++++++++-- libraries/entities/src/EntityTree.h | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 15fc3256bd..91694c86aa 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1678,14 +1678,23 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } } +QVector EntityTree::getEntitiesParentFixup() const { + QReadLocker locker(&_needsParentFixupLock); + return _needsParentFixup; +} + +void EntityTree::setNeedsParentFixup(QVector entitiesFixup) { + QWriteLocker locker(&_needsParentFixupLock); + _needsParentFixup = entitiesFixup; +} void EntityTree::fixupNeedsParentFixups() { PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; - QWriteLocker locker(&_needsParentFixupLock); + QVector entitiesParentFixup = getEntitiesParentFixup(); - QMutableVectorIterator iter(_needsParentFixup); + QMutableVectorIterator iter(entitiesParentFixup); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); @@ -1748,6 +1757,8 @@ void EntityTree::fixupNeedsParentFixups() { PerformanceTimer perfTimer("recurseTreeWithOperator"); recurseTreeWithOperator(&moveOperator); } + + setNeedsParentFixup(entitiesParentFixup); } void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 11a747d624..fa44fd37b8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -385,6 +385,8 @@ private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); + QVector getEntitiesParentFixup() const; + void setNeedsParentFixup(QVector entitiesFixup); std::shared_ptr _myAvatar{ nullptr }; }; From 6a971e136296f962695f142a22570661465d7287 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 29 Jan 2018 11:27:30 -0800 Subject: [PATCH 114/381] remove restriction to be avatar only --- interface/src/scripting/WalletScriptingInterface.cpp | 9 +++------ interface/src/scripting/WalletScriptingInterface.h | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 87c5c89ab3..3e125caeeb 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -37,14 +37,11 @@ void WalletScriptingInterface::setWalletStatus(const uint& status) { emit DependencyManager::get()->walletStatusResult(status); } -void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityID) { +void WalletScriptingInterface::proveEntityOwnershipVerification(const QUuid& entityID) { EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, _entityPropertyFlags); if (!entityID.isNull() && entityProperties.getMarketplaceID().length() > 0) { - if (!entityProperties.getClientOnly()) { - qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; - emit DependencyManager::get()->ownershipVerificationFailed(entityID); - return; - } DependencyManager::get()->requestOwnershipVerification(entityID); + } else { + qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is null or not a marketplace item"; } } \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 8e2dcd47a8..960c38d6fb 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -40,7 +40,7 @@ public: Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } - Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); + Q_INVOKABLE void proveEntityOwnershipVerification(const QUuid& entityID); // setWalletStatus() should never be made Q_INVOKABLE. If it were, // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); From 1fd4c5c1a45199581e89e63a1b1ffb89cdbdfd65 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 29 Jan 2018 12:20:51 -0800 Subject: [PATCH 115/381] Integrating the tag flags to the render item key and adding configration of the render pipelien with the Tag information --- interface/src/SecondaryCamera.cpp | 2 +- libraries/render-utils/src/RenderShadowTask.cpp | 6 ++---- libraries/render-utils/src/RenderShadowTask.h | 4 +++- libraries/render-utils/src/RenderViewTask.cpp | 2 +- libraries/render/src/render/CullTask.cpp | 6 ++++-- libraries/render/src/render/CullTask.h | 4 ++-- libraries/render/src/render/RenderFetchCullSortTask.cpp | 4 +++- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 748f1595db..6b8e370689 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -20,7 +20,7 @@ using RenderArgsPointer = std::shared_ptr; void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { - task.addJob("RenderShadowTask", cullFunctor); + task.addJob("RenderShadowTask", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); assert(items.canCast()); if (!isDeferred) { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 20af1278ac..19a3f4ed73 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -216,7 +216,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende task.addJob("ShadowSetup"); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { - const auto setupOutput = task.addJob("ShadowCascadeSetup", i); + const auto setupOutput = task.addJob("ShadowCascadeSetup", i, tagBits, tagMask); const auto shadowFilter = setupOutput.getN(1); // CPU jobs: @@ -259,14 +259,12 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Cache old render args RenderArgs* args = renderContext->args; - // const auto& filterMask = inputs; - output.edit0() = args->_renderMode; output.edit2() = args->_sizeScale; const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask); globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 2bc325cc81..33e0ad4daa 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -67,12 +67,14 @@ public: using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelO; - RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + RenderShadowCascadeSetup(unsigned int cascadeIndex, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : _cascadeIndex{ cascadeIndex }, _tagBits(tagBits), _tagMask(tagMask) {} void run(const render::RenderContextPointer& renderContext, Outputs& output); private: unsigned int _cascadeIndex; + uint8_t _tagBits{ 0x00 }; + uint8_t _tagMask{ 0x00 }; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 42d3044b1b..19924b4ddc 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -28,7 +28,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: const auto threshold = 1e-3f; return relativeBoundRadius > threshold; return true; - }); + }, tagBits, tagMask); const auto items = task.addJob("FetchCullSort", cullFunctor, tagBits, tagMask); assert(items.canCast()); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 70331cdb47..8d99c3e8f2 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -61,7 +61,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc details._rendered += (int)outItems.size(); } -void FetchNonspatialItems::run(const RenderContextPointer& renderContext, ItemBounds& outItems) { +void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto& scene = renderContext->_scene; @@ -72,7 +72,9 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, ItemBo outItems.reserve(items.size()); for (auto& id : items) { auto& item = scene->getItem(id); - outItems.emplace_back(ItemBound(id, item.getBound())); + if (filter.test(item.getKey())) { + outItems.emplace_back(ItemBound(id, item.getBound())); + } } } diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 486c4f4cdf..4461537109 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -24,8 +24,8 @@ namespace render { class FetchNonspatialItems { public: - using JobModel = Job::ModelO; - void run(const RenderContextPointer& renderContext, ItemBounds& outItems); + using JobModel = Job::ModelIO; + void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemBounds& outItems); }; class FetchSpatialTreeConfig : public Job::Config { diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index bb6bd462f6..7f60d5bb52 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -29,7 +29,9 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); // Overlays are not culled - const auto nonspatialSelection = task.addJob("FetchOverlaySelection"); + const ItemFilter overlayfilter = ItemFilter::Builder().withVisible().withTagBits(tagBits, tagMask); + const auto nonspatialFilter = render::Varying(overlayfilter); + const auto nonspatialSelection = task.addJob("FetchOverlaySelection", nonspatialFilter); // Multi filter visible items into different buckets const int NUM_SPATIAL_FILTERS = 4; From f8f76d6757ae6c089ae1b562b0d5efd815e71f6f Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 29 Jan 2018 12:46:00 -0800 Subject: [PATCH 116/381] changing back to avatar only and making some changes to avoid multiple calls to dependency manager --- .../scripting/WalletScriptingInterface.cpp | 24 +++++++++---------- .../src/scripting/WalletScriptingInterface.h | 3 +-- .../ui/overlays/ContextOverlayInterface.cpp | 1 - .../src/ui/overlays/ContextOverlayInterface.h | 1 + 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 3e125caeeb..e2158b9fd7 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -17,14 +17,6 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q WalletScriptingInterface::WalletScriptingInterface() { - _entityPropertyFlags += PROP_POSITION; - _entityPropertyFlags += PROP_ROTATION; - _entityPropertyFlags += PROP_MARKETPLACE_ID; - _entityPropertyFlags += PROP_DIMENSIONS; - _entityPropertyFlags += PROP_REGISTRATION_POINT; - _entityPropertyFlags += PROP_CERTIFICATE_ID; - _entityPropertyFlags += PROP_CLIENT_ONLY; - _entityPropertyFlags += PROP_OWNING_AVATAR_ID; } void WalletScriptingInterface::refreshWalletStatus() { @@ -37,11 +29,17 @@ void WalletScriptingInterface::setWalletStatus(const uint& status) { emit DependencyManager::get()->walletStatusResult(status); } -void WalletScriptingInterface::proveEntityOwnershipVerification(const QUuid& entityID) { - EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, _entityPropertyFlags); - if (!entityID.isNull() && entityProperties.getMarketplaceID().length() > 0) { - DependencyManager::get()->requestOwnershipVerification(entityID); +void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityID) { + QSharedPointer contextOverlayInterface = DependencyManager::get(); + EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, + contextOverlayInterface->getEntityPropertyFlags()); + if (entityProperties.getClientOnly()) { + if (!entityID.isNull() && entityProperties.getCertificateID().length() > 0) { + contextOverlayInterface->requestOwnershipVerification(entityID); + } else { + qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is null or not a certified item"; + } } else { - qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is null or not a marketplace item"; + qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; } } \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 960c38d6fb..8f5c65e335 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -40,7 +40,7 @@ public: Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } - Q_INVOKABLE void proveEntityOwnershipVerification(const QUuid& entityID); + Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); // setWalletStatus() should never be made Q_INVOKABLE. If it were, // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); @@ -51,7 +51,6 @@ signals: private: uint _walletStatus; - EntityPropertyFlags _entityPropertyFlags; }; #endif // hifi_WalletScriptingInterface_h diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 4a5aa3d0f4..a273304eaa 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -189,7 +189,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return false; } - bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); Setting::Handle _settingSwitch{ "commerce", true }; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 4063b28785..5bb0b522a3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -57,6 +57,7 @@ public: bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } void requestOwnershipVerification(const QUuid& entityID); + EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; } signals: void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); From 634bc83ed5e6ef325a206a9bbde9213a92af7156 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 29 Jan 2018 12:54:52 -0800 Subject: [PATCH 117/381] fix elses --- .../ui/overlays/ContextOverlayInterface.cpp | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index a273304eaa..a7f8861882 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -299,14 +299,11 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID if (networkReply->error() == QNetworkReply::NoError) { if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty"; - } - else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { + } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { qCDebug(entities) << "'transfer_status' is 'failed'"; - } - else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { + } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { qCDebug(entities) << "'transfer_status' is 'pending'"; - } - else { + } else { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); QByteArray certID = entityProperties.getCertificateID().toUtf8(); @@ -332,25 +329,21 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); return; - } - else { + } else { startChallengeOwnershipTimer(); } } - } - else { + } else { qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "More info:" << networkReply->readAll(); } networkReply->deleteLater(); }); - } - else { + } else { qCWarning(context_overlay) << "Couldn't get Entity Server!"; } - } - else { + } else { auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); From b60a45d3582a4afaf6312207b37b7a35e56a09e2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 29 Jan 2018 15:14:29 -0800 Subject: [PATCH 118/381] Inspection Certificate Redesign --- .../InspectionCertificate.qml | 324 +++++++++++------- .../images/cert-bg-gold-split.png | Bin 0 -> 189708 bytes .../inspectionCertificate/images/cert-bg.jpg | Bin 64011 -> 0 bytes .../images/nocert-bg-split.png | Bin 0 -> 17201 bytes 4 files changed, 191 insertions(+), 133 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg-gold-split.png delete mode 100644 interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg create mode 100644 interface/resources/qml/hifi/commerce/inspectionCertificate/images/nocert-bg-split.png diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index c331532f5e..b42e2eb4d9 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -19,8 +19,6 @@ import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet -// references XXX from root context - Rectangle { HifiConstants { id: hifi; } @@ -31,6 +29,7 @@ Rectangle { property string itemOwner: "--"; property string itemEdition: "--"; property string dateOfPurchase: "--"; + property string itemCost: "--"; property bool isLightbox: false; property bool isMyCert: false; property bool isCertificateInvalid: false; @@ -49,8 +48,9 @@ Rectangle { "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"); root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" : (result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run)); - root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000); + root.dateOfPurchase = (root.isCertificateInvalid || !root.isMyCert) ? "Undisclosed" : getFormattedDate(result.data.transfer_created_at * 1000); root.itemName = result.data.marketplace_item_name; + root.itemCost = result.data.cost && root.isMyCert ? result.data.cost : "Undisclosed"; if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") { titleBarText.text = "Invalid Certificate"; @@ -81,11 +81,11 @@ Rectangle { titleBarText.color = hifi.colors.redHighlight; popText.text = ""; - root.itemOwner = ""; - dateOfPurchaseHeader.text = ""; - root.dateOfPurchase = ""; + root.itemOwner = "--"; + root.dateOfPurchase = "--"; + root.itemCost = "--"; root.itemEdition = "Uncertified Copy"; - + showInMarketplaceButton.visible = false; errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; errorText.color = hifi.colors.baseGray; } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED @@ -94,9 +94,6 @@ Rectangle { titleBarText.color = hifi.colors.redHighlight; popText.text = ""; - root.itemOwner = ""; - dateOfPurchaseHeader.text = ""; - root.dateOfPurchase = ""; root.itemEdition = "Uncertified Copy"; errorText.text = "The avatar who rezzed this item doesn't own it."; @@ -123,7 +120,7 @@ Rectangle { Image { anchors.fill: parent; - source: "images/cert-bg.jpg"; + source: root.isCertificateInvalid ? "images/nocert-bg-split.png" : "images/cert-bg-gold-split.png"; } // Title text @@ -145,7 +142,7 @@ Rectangle { // Title text RalewayRegular { id: popText; - text: "Proof of Provenance"; + text: "PROOF OF PROVENANCE"; // Text size size: 16; // Anchors @@ -157,6 +154,35 @@ Rectangle { // Style color: hifi.colors.darkGray; } + + // "Close" button + HiFiGlyphs { + id: closeGlyphButton; + text: hifi.glyphs.close; + color: hifi.colors.white; + size: 26; + anchors.top: parent.top; + anchors.topMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + if (root.isLightbox) { + root.visible = false; + } else { + sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases}); + } + } + } + } // // "CERTIFICATE" START @@ -165,9 +191,11 @@ Rectangle { id: certificateContainer; anchors.top: popText.bottom; anchors.topMargin: 30; - anchors.bottom: buttonsContainer.top; + anchors.bottom: infoContainer.top; anchors.left: parent.left; + anchors.leftMargin: 45; anchors.right: parent.right; + anchors.rightMargin: 24; RalewayRegular { id: itemNameHeader; @@ -177,9 +205,7 @@ Rectangle { // Anchors anchors.top: parent.top; anchors.left: parent.left; - anchors.leftMargin: 45; anchors.right: parent.right; - anchors.rightMargin: 16; height: paintedHeight; // Style color: hifi.colors.darkGray; @@ -196,7 +222,7 @@ Rectangle { anchors.right: itemNameHeader.right; height: paintedHeight; // Style - color: hifi.colors.white; + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.white; elide: Text.ElideRight; MouseArea { anchors.fill: parent; @@ -205,70 +231,20 @@ Rectangle { sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); } onEntered: itemName.color = hifi.colors.blueHighlight; - onExited: itemName.color = hifi.colors.white; + onExited: itemName.color = root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.white; } } - RalewayRegular { - id: ownedByHeader; - text: "OWNER"; - // Text size - size: 16; - // Anchors - anchors.top: itemName.bottom; - anchors.topMargin: 28; - anchors.left: parent.left; - anchors.leftMargin: 45; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: paintedHeight; - // Style - color: hifi.colors.darkGray; - } - RalewayRegular { - id: ownedBy; - text: root.itemOwner; - // Text size - size: 22; - // Anchors - anchors.top: ownedByHeader.bottom; - anchors.topMargin: 8; - anchors.left: ownedByHeader.left; - height: paintedHeight; - // Style - color: hifi.colors.white; - elide: Text.ElideRight; - } - AnonymousProRegular { - id: isMyCertText; - visible: root.isMyCert && !root.isCertificateInvalid; - text: "(Private)"; - size: 18; - // Anchors - anchors.top: ownedBy.top; - anchors.topMargin: 4; - anchors.bottom: ownedBy.bottom; - anchors.left: ownedBy.right; - anchors.leftMargin: 6; - anchors.right: ownedByHeader.right; - // Style - color: hifi.colors.white; - elide: Text.ElideRight; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { id: editionHeader; text: "EDITION"; // Text size size: 16; // Anchors - anchors.top: ownedBy.bottom; + anchors.top: itemName.bottom; anchors.topMargin: 28; anchors.left: parent.left; - anchors.leftMargin: 45; anchors.right: parent.right; - anchors.rightMargin: 16; height: paintedHeight; // Style color: hifi.colors.darkGray; @@ -285,21 +261,117 @@ Rectangle { anchors.right: editionHeader.right; height: paintedHeight; // Style - color: hifi.colors.white; + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.white; + } + + // "Show In Marketplace" button + HifiControlsUit.Button { + id: showInMarketplaceButton; + enabled: root.marketplaceUrl; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.light; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 48; + anchors.right: parent.right; + width: 200; + height: 40; + text: "View In Market" + onClicked: { + sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); + } + } + } + // + // "CERTIFICATE" END + // + + // + // "INFO CONTAINER" START + // + Item { + id: infoContainer; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.leftMargin: 45; + anchors.right: parent.right; + anchors.rightMargin: 24; + height: root.isCertificateInvalid ? 372 : 220; + + RalewayRegular { + id: errorText; + visible: text !== ""; + // Text size + size: 20; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 36; + anchors.left: parent.left; + anchors.right: parent.right; + height: 120; + // Style + wrapMode: Text.WordWrap; + color: hifi.colors.darkGray; + verticalAlignment: Text.AlignTop; + } + + RalewayRegular { + id: ownedByHeader; + text: "OWNER"; + // Text size + size: 16; + // Anchors + anchors.top: errorText.visible ? errorText.bottom : parent.top; + anchors.topMargin: 28; + anchors.left: parent.left; + anchors.right: parent.right; + height: paintedHeight; + // Style + color: hifi.colors.darkGray; + } + + RalewayRegular { + id: ownedBy; + text: root.itemOwner; + // Text size + size: 22; + // Anchors + anchors.top: ownedByHeader.bottom; + anchors.topMargin: 8; + anchors.left: ownedByHeader.left; + height: paintedHeight; + // Style + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.blueAccent; + elide: Text.ElideRight; + } + AnonymousProRegular { + id: isMyCertText; + visible: root.isMyCert; + text: "(Private)"; + size: 18; + // Anchors + anchors.top: ownedBy.top; + anchors.topMargin: 4; + anchors.bottom: ownedBy.bottom; + anchors.left: ownedBy.right; + anchors.leftMargin: 6; + anchors.right: ownedByHeader.right; + // Style + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.blueAccent; + elide: Text.ElideRight; + verticalAlignment: Text.AlignVCenter; } RalewayRegular { id: dateOfPurchaseHeader; - text: "DATE OF PURCHASE"; + text: "PURCHASE DATE"; // Text size size: 16; // Anchors - anchors.top: edition.bottom; + anchors.top: ownedBy.bottom; anchors.topMargin: 28; anchors.left: parent.left; - anchors.leftMargin: 45; - anchors.right: parent.right; - anchors.rightMargin: 16; + anchors.right: parent.horizontalCenter; + anchors.rightMargin: 8; height: paintedHeight; // Style color: hifi.colors.darkGray; @@ -316,73 +388,58 @@ Rectangle { anchors.right: dateOfPurchaseHeader.right; height: paintedHeight; // Style - color: hifi.colors.white; + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.blueAccent; } RalewayRegular { - id: errorText; + id: priceHeader; + text: "PURCHASE PRICE"; // Text size - size: 20; + size: 16; // Anchors - anchors.top: dateOfPurchase.bottom; - anchors.topMargin: 36; - anchors.left: dateOfPurchase.left; - anchors.right: dateOfPurchase.right; - anchors.bottom: parent.bottom; - // Style - wrapMode: Text.WordWrap; - color: hifi.colors.redHighlight; - verticalAlignment: Text.AlignTop; - } - } - // - // "CERTIFICATE" END - // - - Item { - id: buttonsContainer; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 30; - anchors.left: parent.left; - anchors.right: parent.right; - height: 50; - - // "Cancel" button - HifiControlsUit.Button { - color: hifi.buttons.noneBorderlessWhite; - colorScheme: hifi.colorSchemes.light; - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 30; - width: parent.width/2 - 50; - height: 50; - text: "close"; - onClicked: { - if (root.isLightbox) { - root.visible = false; - } else { - sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases}); - } - } - } - - // "Show In Marketplace" button - HifiControlsUit.Button { - id: showInMarketplaceButton; - enabled: root.marketplaceUrl; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.light; - anchors.top: parent.top; + anchors.top: ownedBy.bottom; + anchors.topMargin: 28; + anchors.left: parent.horizontalCenter; anchors.right: parent.right; - anchors.rightMargin: 30; - width: parent.width/2 - 50; - height: 50; - text: "View In Market" - onClicked: { - sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); - } + height: paintedHeight; + // Style + color: hifi.colors.darkGray; + } + HiFiGlyphs { + id: hfcGlyph; + visible: priceText.text !== "Undisclosed"; + text: hifi.glyphs.hfc; + // Size + size: 24; + // Anchors + anchors.top: priceHeader.bottom; + anchors.topMargin: 8; + anchors.left: priceHeader.left; + width: visible ? paintedWidth + 6 : 0; + height: 40; + // Style + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.blueAccent; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignLeft; + } + AnonymousProRegular { + id: priceText; + text: root.itemCost; + // Text size + size: 18; + // Anchors + anchors.top: priceHeader.bottom; + anchors.topMargin: 8; + anchors.left: hfcGlyph.right; + anchors.right: priceHeader.right; + height: paintedHeight; + // Style + color: root.isCertificateInvalid ? hifi.colors.redHighlight : hifi.colors.blueAccent; } } + // + // "INFO CONTAINER" END + // // // FUNCTION DEFINITIONS START @@ -414,6 +471,7 @@ Rectangle { root.itemEdition = "--"; root.dateOfPurchase = "--"; root.marketplaceUrl = ""; + root.itemCost = "--"; root.isMyCert = false; errorText.text = ""; break; diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg-gold-split.png b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg-gold-split.png new file mode 100644 index 0000000000000000000000000000000000000000..14a17df0b16f35320b67561440d6d9cf11cb2f29 GIT binary patch literal 189708 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 k+*$f8F` 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

0~BjTbon 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 literal 0 HcmV?d00001 diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg deleted file mode 100644 index b39a55e4e83f3290d7befdfeb1055cf9b73f4bfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64011 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 diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/nocert-bg-split.png b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/nocert-bg-split.png new file mode 100644 index 0000000000000000000000000000000000000000..b2f5a492653b84bf6b739ae270180b1bb7b0d48d GIT binary patch literal 17201 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 F{|C0gl@I^` literal 0 HcmV?d00001 From ce8159340b31c3372018409b70c5f968e4557b28 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 29 Jan 2018 15:51:09 -0800 Subject: [PATCH 119/381] fix vs autoformatting --- interface/src/ui/overlays/ContextOverlayInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 5bb0b522a3..a260c9cc39 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -84,7 +84,7 @@ private: MAX_SELECTION_COUNT = 16 }; bool _verboseLogging{ true }; - bool _enabled{ true }; + bool _enabled { true }; EntityItemID _currentEntityWithContextOverlay{}; EntityItemID _lastInspectedEntity{}; QString _entityMarketplaceID; From 8b7c9895081d957aba5242488dde56465295cda5 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 29 Jan 2018 16:34:44 -0800 Subject: [PATCH 120/381] move emit signals to wallet interface --- interface/src/scripting/WalletScriptingInterface.h | 2 ++ interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 ++++---- interface/src/ui/overlays/ContextOverlayInterface.h | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 8f5c65e335..9e40aad087 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -48,6 +48,8 @@ public: signals: void walletStatusChanged(); void walletNotSetup(); + void ownershipVerificationSuccess(const QUuid& entityID); + void ownershipVerificationFailed(const QUuid& entityID); private: uint _walletStatus; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index a7f8861882..ed7b811fb0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -347,7 +347,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - emit ownershipVerificationFailed(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; } } @@ -404,7 +404,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() { connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); - emit ownershipVerificationFailed(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); }); _challengeOwnershipTimeoutTimer.start(5000); @@ -429,9 +429,9 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); - emit ownershipVerificationSuccess(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationSuccess(_lastInspectedEntity); } else { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); - emit ownershipVerificationFailed(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index a260c9cc39..6aad2a773b 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -26,6 +26,7 @@ #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" +#include "scripting/WalletScriptingInterface.h" #include "EntityTree.h" #include "ContextOverlayLogging.h" @@ -61,8 +62,6 @@ public: signals: void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); - void ownershipVerificationSuccess(const QUuid& entityID); - void ownershipVerificationFailed(const QUuid& entityID); public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); From 9ee715364185b913bf928864867278a11ac35af0 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 29 Jan 2018 16:53:02 -0800 Subject: [PATCH 121/381] fix scale speed, fix stretch panels, fix scale cube highlight --- .../system/libraries/entitySelectionTool.js | 96 +++++++++++-------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 253c67bfca..3da7d1f083 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -246,6 +246,7 @@ SelectionDisplay = (function() { var SCALE_MINIMUM_DIMENSION = 0.02; var STRETCH_MINIMUM_DIMENSION = 0.001; + var STRETCH_DIRECTION_ALL_FACTOR = 15; // These are multipliers for sizing the rotation degrees display while rotating an entity var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.0; @@ -266,12 +267,6 @@ SelectionDisplay = (function() { ALL : 3 } - var ROTATE_DIRECTION = { - PITCH : 0, - YAW : 1, - ROLL : 2 - } - var SCALE_DIRECTION = { LBN : 0, RBN : 1, @@ -283,6 +278,12 @@ SelectionDisplay = (function() { RTF : 7 } + var ROTATE_DIRECTION = { + PITCH : 0, + YAW : 1, + ROLL : 2 + } + var spaceMode = SPACE_LOCAL; var overlayNames = []; var lastCameraPosition = Camera.getPosition(); @@ -706,7 +707,7 @@ SelectionDisplay = (function() { }; }; - function makeStretchTool(stretchMode, directionEnum, directionVec, pivot, offset) { + function makeStretchTool(stretchMode, directionEnum, directionVec, pivot, offset, stretchPanel, scaleGrabber) { var directionFor3DStretch = directionVec; var distanceFor3DStretch = 0; var DISTANCE_INFLUENCE_THRESHOLD = 1.2; @@ -892,16 +893,29 @@ SelectionDisplay = (function() { that.setGrabberTranslateVisible(false); that.setGrabberRotateVisible(false); - that.setGrabberScaleVisible(directionEnum === STRETCH_DIRECTION.ALL); + that.setGrabberScaleVisible(true); that.setGrabberStretchXVisible(directionEnum === STRETCH_DIRECTION.X); that.setGrabberStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); that.setGrabberStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); that.setGrabberClonerVisible(false); + + if (stretchPanel != null) { + Overlays.editOverlay(stretchPanel, { visible: true }); + } + if (scaleGrabber != null) { + Overlays.editOverlay(scaleGrabber, { color: GRABBER_SCALE_CUBE_SELECTED_COLOR }); + } SelectionManager.saveProperties(); }; var onEnd = function(event, reason) { + if (stretchPanel != null) { + Overlays.editOverlay(stretchPanel, { visible: false }); + } + if (scaleGrabber != null) { + Overlays.editOverlay(scaleGrabber, { color: GRABBER_SCALE_CUBE_IDLE_COLOR }); + } pushCommandForSelections(); }; @@ -959,6 +973,11 @@ SelectionDisplay = (function() { vector = grid.snapToSpacing(vector); var changeInDimensions = Vec3.multiply(-1, vec3Mult(localSigns, vector)); + + if (directionEnum === STRETCH_DIRECTION.ALL) { + changeInDimensions = Vec3.multiply(changeInDimensions, STRETCH_DIRECTION_ALL_FACTOR); + } + var newDimensions; if (proportional) { var absX = Math.abs(changeInDimensions.x); @@ -1024,60 +1043,53 @@ SelectionDisplay = (function() { } function addGrabberStretchTool(overlay, mode, directionEnum) { - var directionVec, pivot, offset; + var directionVec, pivot, offset, stretchPanel; if (directionEnum === STRETCH_DIRECTION.X) { + stretchPanel = grabberStretchXPanel; directionVec = { x:-1, y:0, z:0 }; - pivot = { x:-1, y:0, z:0 }; - offset = { x:1, y:0, z:0 }; } else if (directionEnum === STRETCH_DIRECTION.Y) { + stretchPanel = grabberStretchYPanel; directionVec = { x:0, y:-1, z:0 }; - pivot = { x:0, y:-1, z:0 }; - offset = { x:0, y:1, z:0 }; } else if (directionEnum === STRETCH_DIRECTION.Z) { + stretchPanel = grabberStretchZPanel directionVec = { x:0, y:0, z:-1 }; - pivot = { x:0, y:0, z:-1 }; - offset = { x:0, y:0, z:1 }; } - var tool = makeStretchTool(mode, directionEnum, directionVec, pivot, offset); + pivot = directionVec; + offset = Vec3.multiply(directionVec, -1); + var tool = makeStretchTool(mode, directionEnum, directionVec, pivot, offset, stretchPanel, null); return addGrabberTool(overlay, tool); } function addGrabberScaleTool(overlay, mode, directionEnum) { - var directionVec, pivot, offset; + var directionVec, pivot, offset, selectedGrabber; if (directionEnum === SCALE_DIRECTION.LBN) { directionVec = { x:1, y:1, z:1 }; - pivot = { x:1, y:1, z:1 }; - offset = { x:-1, y:-1, z:-1 }; + selectedGrabber = grabberScaleLBNCube; } else if (directionEnum === SCALE_DIRECTION.RBN) { directionVec = { x:1, y:1, z:-1 }; - pivot = { x:1, y:1, z:-1 }; - offset = { x:-1, y:-1, z:1 }; + selectedGrabber = grabberScaleRBNCube; } else if (directionEnum === SCALE_DIRECTION.LBF) { directionVec = { x:-1, y:1, z:1 }; - pivot = { x:-1, y:1, z:1 }; - offset = { x:1, y:-1, z:-1 }; + selectedGrabber = grabberScaleLBFCube; } else if (directionEnum === SCALE_DIRECTION.RBF) { directionVec = { x:-1, y:1, z:-1 }; - pivot = { x:-1, y:1, z:-1 }; - offset = { x:1, y:-1, z:1 }; + selectedGrabber = grabberScaleRBFCube; } else if (directionEnum === SCALE_DIRECTION.LTN) { directionVec = { x:1, y:-1, z:1 }; - pivot = { x:1, y:-1, z:1 }; - offset = { x:-1, y:1, z:-1 }; + selectedGrabber = grabberScaleLTNCube; } else if (directionEnum === SCALE_DIRECTION.RTN) { directionVec = { x:1, y:-1, z:-1 }; - pivot = { x:1, y:-1, z:-1 }; - offset = { x:-1, y:1, z:1 }; + selectedGrabber = grabberScaleRTNCube; } else if (directionEnum === SCALE_DIRECTION.LTF) { directionVec = { x:-1, y:-1, z:1 }; - pivot = { x:-1, y:-1, z:1 }; - offset = { x:1, y:1, z:-1 }; + selectedGrabber = grabberScaleLTFCube; } else if (directionEnum === SCALE_DIRECTION.RTF) { directionVec = { x:-1, y:-1, z:-1 }; - pivot = { x:-1, y:-1, z:-1 }; - offset = { x:1, y:1, z:1 }; + selectedGrabber = grabberScaleRTFCube; } - var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVec, pivot, offset); + pivot = directionVec; + offset = Vec3.multiply(directionVec, -1); + var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVec, pivot, offset, null, selectedGrabber); return addGrabberTool(overlay, tool); } @@ -1346,6 +1358,7 @@ SelectionDisplay = (function() { if (SelectionManager.hasSelection()) { var worldPosition = SelectionManager.worldPosition; var worldRotation = SelectionManager.worldRotation; + var worldRotationInverse = Quat.inverse(worldRotation); var worldDimensions = SelectionManager.worldDimensions; var worldDimensionsX = worldDimensions.x; @@ -1478,7 +1491,9 @@ SelectionDisplay = (function() { position: stretchXPos, dimensions: stretchSphereDimensions }); - var stretchPanelXDimensions = Vec3.subtract(grabberScaleLTFCubePos, grabberScaleRBFCubePos); + var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleLTFCubePos); + var grabberScaleRBFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRBFCubePos); + var stretchPanelXDimensions = Vec3.subtract(grabberScaleLTFCubePosRot, grabberScaleRBFCubePosRot); var tempY = Math.abs(stretchPanelXDimensions.y); stretchPanelXDimensions.x = 0.01; stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); @@ -1493,7 +1508,9 @@ SelectionDisplay = (function() { position: stretchYPos, dimensions: stretchSphereDimensions }); - var stretchPanelYDimensions = Vec3.subtract(grabberScaleLTFCubePos, grabberScaleRTNCubePos); + var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleLTNCubePos); + var grabberScaleRTNCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRTFCubePos); + var stretchPanelYDimensions = Vec3.subtract(grabberScaleLTFCubePosRot, grabberScaleRTNCubePosRot); var tempX = Math.abs(stretchPanelYDimensions.x); stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); stretchPanelYDimensions.y = 0.01; @@ -1508,7 +1525,9 @@ SelectionDisplay = (function() { position: stretchZPos, dimensions: stretchSphereDimensions }); - var stretchPanelZDimensions = Vec3.subtract(grabberScaleRTFCubePos, grabberScaleRBNCubePos); + var grabberScaleRTFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRTFCubePos); + var grabberScaleRBNCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRBNCubePos); + var stretchPanelZDimensions = Vec3.subtract(grabberScaleRTFCubePosRot, grabberScaleRBNCubePosRot); var tempX = Math.abs(stretchPanelZDimensions.x); stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); stretchPanelZDimensions.y = tempX; @@ -1576,7 +1595,8 @@ SelectionDisplay = (function() { that.setGrabberStretchYVisible(!activeTool || isActiveTool(grabberStretchYSphere)); that.setGrabberStretchZVisible(!activeTool || isActiveTool(grabberStretchZSphere)); that.setGrabberScaleVisible(!activeTool || isActiveTool(grabberScaleLBNCube) || isActiveTool(grabberScaleRBNCube) || isActiveTool(grabberScaleLBFCube) || isActiveTool(grabberScaleRBFCube) - || isActiveTool(grabberScaleLTNCube) || isActiveTool(grabberScaleRTNCube) || isActiveTool(grabberScaleLTFCube) || isActiveTool(grabberScaleRTFCube)); + || isActiveTool(grabberScaleLTNCube) || isActiveTool(grabberScaleRTNCube) || isActiveTool(grabberScaleLTFCube) || isActiveTool(grabberScaleRTFCube) + || isActiveTool(grabberStretchXSphere) || isActiveTool(grabberStretchYSphere) || isActiveTool(grabberStretchZSphere)); that.setGrabberClonerVisible(!activeTool || isActiveTool(grabberCloner)); if (wantDebug) { From 2f0d92c3cde394770e6613ce2e434ad5d45e9908 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 29 Jan 2018 18:15:01 -0800 Subject: [PATCH 122/381] ctrl 22.5 snapping --- .../system/libraries/entitySelectionTool.js | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 3da7d1f083..d843b6282b 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -254,6 +254,10 @@ SelectionDisplay = (function() { var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.18; var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.14; + var ROTATION_CTRL_SNAP_ANGLE = 22.5; + var ROTATION_DEFAULT_SNAP_ANGLE = 1; + var ROTATION_DEFAULT_TICK_MARKS_ANGLE = 5; + var TRANSLATE_DIRECTION = { X : 0, Y : 1, @@ -300,6 +304,8 @@ SelectionDisplay = (function() { var worldRotationY; var worldRotationZ; + var ctrlPressed = false; + var activeTool = null; var grabberTools = {}; @@ -335,7 +341,7 @@ SelectionDisplay = (function() { innerRadius: 0.9, startAt: 0, endAt: 360, - majorTickMarksAngle: 5, + majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE, majorTickMarksLength: 0.1, visible: false, ignoreRayIntersection: false, @@ -584,6 +590,25 @@ SelectionDisplay = (function() { that.triggerMapping.from(Controller.Standard.RT).peek().to(makeTriggerHandler(Controller.Standard.RightHand)); that.triggerMapping.from(Controller.Standard.LT).peek().to(makeTriggerHandler(Controller.Standard.LeftHand)); + // Control key remains active only while key is held down + function keyReleaseEvent(key) { + if (key.key === 16777249) { + ctrlPressed = false; + that.updateActiveRotateRing(); + } + } + + // Triggers notification on specific key driven events + function keyPressEvent(key) { + if (key.key === 16777249) { + ctrlPressed = true; + that.updateActiveRotateRing(); + } + } + + Controller.keyPressEvent.connect(keyPressEvent); + Controller.keyReleaseEvent.connect(keyReleaseEvent); + function controllerComputePickRay() { var controllerPose = getControllerWorldLocation(activeHand, true); if (controllerPose.valid && that.triggered) { @@ -1227,7 +1252,9 @@ SelectionDisplay = (function() { var centerToIntersect = Vec3.subtract(result, rotCenter); // Note: orientedAngle which wants normalized centerToZero and centerToIntersect // handles that internally, so it's to pass unnormalized vectors here. - var angleFromZero = Math.floor((Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal))); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + var snapAngle = ctrlPressed ? ROTATION_CTRL_SNAP_ANGLE : ROTATION_DEFAULT_SNAP_ANGLE; + angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); updateSelectionsRotation(rotChange); updateRotationDegreesOverlay(-angleFromZero, direction, rotCenter); @@ -1310,12 +1337,6 @@ SelectionDisplay = (function() { } } - /* - Overlays.editOverlay(highlightBox, { - visible: false - }); - */ - that.updateGrabbers(); }; @@ -1544,24 +1565,28 @@ SelectionDisplay = (function() { Overlays.editOverlay(grabberRotatePitchRing, { position: SelectionManager.worldPosition, rotation: worldRotationY, - dimensions: rotateDimensions + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE }); } if (!isActiveTool(grabberRotateYawRing)) { Overlays.editOverlay(grabberRotateYawRing, { position: SelectionManager.worldPosition, rotation: worldRotationZ, - dimensions: rotateDimensions + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE }); } if (!isActiveTool(grabberRotateRollRing)) { Overlays.editOverlay(grabberRotateRollRing, { position: SelectionManager.worldPosition, rotation: worldRotationX, - dimensions: rotateDimensions + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE }); } Overlays.editOverlay(grabberRotateCurrentRing, { dimensions: rotateDimensions }); + that.updateActiveRotateRing(); var inModeRotate = isActiveTool(grabberRotatePitchRing) || isActiveTool(grabberRotateYawRing) || isActiveTool(grabberRotateRollRing); var inModeTranslate = isActiveTool(grabberTranslateXCone) || isActiveTool(grabberTranslateXCylinder) || @@ -1604,6 +1629,21 @@ SelectionDisplay = (function() { } }; + that.updateActiveRotateRing = function() { + var activeRotateRing = null; + if (isActiveTool(grabberRotatePitchRing)) { + activeRotateRing = grabberRotatePitchRing; + } else if (isActiveTool(grabberRotateYawRing)) { + activeRotateRing = grabberRotateYawRing; + } else if (isActiveTool(grabberRotateRollRing)) { + activeRotateRing = grabberRotateRollRing; + } + if (activeRotateRing != null) { + var tickMarksAngle = ctrlPressed ? ROTATION_CTRL_SNAP_ANGLE : ROTATION_DEFAULT_TICK_MARKS_ANGLE; + Overlays.editOverlay(activeRotateRing, { majorTickMarksAngle: tickMarksAngle }); + } + }; + // FUNCTION: SET OVERLAYS VISIBLE that.setOverlaysVisible = function(isVisible) { for (var i = 0; i < allOverlays.length; i++) { From e679b75e99a53f45a5d50619c0716339b1b82778 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 29 Jan 2018 19:02:36 -0800 Subject: [PATCH 123/381] fix degree display position --- .../system/libraries/entitySelectionTool.js | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index d843b6282b..fbda9e9eba 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -299,6 +299,7 @@ SelectionDisplay = (function() { var rotZero; var rotationNormal; + var rotDegreePos; var worldRotationX; var worldRotationY; @@ -1119,20 +1120,8 @@ SelectionDisplay = (function() { } // FUNCTION: UPDATE ROTATION DEGREES OVERLAY - function updateRotationDegreesOverlay(angleFromZero, direction, centerPosition) { + function updateRotationDegreesOverlay(angleFromZero, position) { var angle = angleFromZero * (Math.PI / 180); - var position = { - x: Math.cos(angle) * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - y: Math.sin(angle) * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - z: 0 - }; - if (direction === ROTATE_DIRECTION.PITCH) - position = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -90, 0), position); - else if (direction === ROTATE_DIRECTION.YAW) - position = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(90, 0, 0), position); - else if (direction === ROTATE_DIRECTION.ROLL) - position = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 180, 0), position); - position = Vec3.sum(centerPosition, position); var overlayProps = { position: position, dimensions: { @@ -1222,7 +1211,6 @@ SelectionDisplay = (function() { endAt: 0, visible: true }); - updateRotationDegreesOverlay(0, direction, rotCenter); // editOverlays may not have committed rotation changes. // Compute zero position based on where the overlay will be eventually. @@ -1230,6 +1218,11 @@ SelectionDisplay = (function() { // In case of a parallel ray, this will be null, which will cause early-out // in the onMove helper. rotZero = result; + + var rotCenterToZero = Vec3.subtract(rotZero, rotCenter); + var rotCenterToZeroLength = Vec3.length(rotCenterToZero); + rotDegreePos = Vec3.sum(rotCenter, Vec3.multiply(Vec3.normalize(rotCenterToZero), rotCenterToZeroLength * 1.2)); + updateRotationDegreesOverlay(0, rotDegreePos); }, onEnd: function(event, reason) { Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); @@ -1257,7 +1250,7 @@ SelectionDisplay = (function() { angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); updateSelectionsRotation(rotChange); - updateRotationDegreesOverlay(-angleFromZero, direction, rotCenter); + updateRotationDegreesOverlay(-angleFromZero, rotDegreePos); var startAtCurrent = 0; var endAtCurrent = angleFromZero; From 280264d7edec00af0dd7309fca5ad6937287920a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 30 Jan 2018 20:46:27 +1300 Subject: [PATCH 124/381] Add location.domainID as a synonym for location.domainId And deprecate location.domainId. --- .../src/entities/EntityServer.cpp | 2 +- libraries/networking/src/AddressManager.cpp | 2 +- libraries/networking/src/AddressManager.h | 9 ++++++--- scripts/system/pal.js | 2 +- scripts/system/snapshot.js | 18 +++++++++--------- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index ecdf14ebec..f72832f902 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -453,7 +453,7 @@ void EntityServer::domainSettingsRequestFailed() { void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; - QString thisDomainID = DependencyManager::get()->getDomainId().remove(QRegExp("\\{|\\}")); + QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); EntityTreePointer tree = std::static_pointer_cast(_tree); QHash localMap(tree->getEntityCertificateIDMap()); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index fa5507fcf7..4e1354d3aa 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -776,7 +776,7 @@ void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } -QString AddressManager::getDomainId() const { +QString AddressManager::getDomainID() const { return DependencyManager::get()->getDomainHandler().getUUID().toString(); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 0173a1fad7..6c9f06d2dd 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -35,9 +35,11 @@ const QString GET_PLACE = "/api/v1/places/%1"; * The location API provides facilities related to your current location in the metaverse. * * @namespace location - * @property {Uuid} domainId - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not + * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not * connected to the domain. * Read-only. + * @property {Uuid} domainId - Synonym for domainId. Read-only. Deprecated: This property + * is deprecated and will soon be removed. * @property {string} hostname - The name of the domain for your current metaverse address (e.g., "AvatarIsland", * localhost, or an IP address). * Read-only. @@ -66,7 +68,8 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) Q_PROPERTY(QString placename READ getPlaceName) - Q_PROPERTY(QString domainId READ getDomainId) + Q_PROPERTY(QString domainID READ getDomainID) + Q_PROPERTY(QString domainId READ getDomainID) public: /**jsdoc @@ -164,7 +167,7 @@ public: const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; } - QString getDomainId() const; + QString getDomainID() const; const QString& getHost() const { return _host; } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 1b93bdde32..b551276e91 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -493,7 +493,7 @@ function populateNearbyUserList(selectData, oldAudioData) { data.push(avatarPalDatum); print('PAL data:', JSON.stringify(avatarPalDatum)); }); - getConnectionData(false, location.domainId); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain). + getConnectionData(false, location.domainID); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain). conserveResources = Object.keys(avatarsOfInterest).length > 20; sendToQml({ method: 'nearbyUsers', params: data }); if (selectData) { diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index dad642075f..ae8ef52a15 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -337,7 +337,7 @@ function fillImageDataFromPrevious() { containsGif: previousAnimatedSnapPath !== "", processingGif: false, shouldUpload: false, - canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"), + canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"), isLoggedIn: isLoggedIn }; imageData = []; @@ -416,7 +416,7 @@ function snapshotUploaded(isError, reply) { } isUploadingPrintableStill = false; } -var href, domainId; +var href, domainID; function takeSnapshot() { tablet.emitScriptEvent(JSON.stringify({ type: "snapshot", @@ -443,11 +443,11 @@ function takeSnapshot() { MyAvatar.setClearOverlayWhenMoving(false); // We will record snapshots based on the starting location. That could change, e.g., when recording a .gif. - // Even the domainId could change (e.g., if the user falls into a teleporter while recording). + // Even the domainID could change (e.g., if the user falls into a teleporter while recording). href = location.href; Settings.setValue("previousSnapshotHref", href); - domainId = location.domainId; - Settings.setValue("previousSnapshotDomainID", domainId); + domainID = location.domainID; + Settings.setValue("previousSnapshotDomainID", domainID); maybeDeleteSnapshotStories(); @@ -548,7 +548,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { } HMD.openTablet(); - isDomainOpen(domainId, function (canShare) { + isDomainOpen(domainID, function (canShare) { snapshotOptions = { containsGif: false, processingGif: false, @@ -594,7 +594,7 @@ function processingGifStarted(pathStillSnapshot) { } HMD.openTablet(); - isDomainOpen(domainId, function (canShare) { + isDomainOpen(domainID, function (canShare) { snapshotOptions = { containsGif: true, processingGif: true, @@ -622,13 +622,13 @@ function processingGifCompleted(pathAnimatedSnapshot) { Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot); - isDomainOpen(domainId, function (canShare) { + isDomainOpen(domainID, function (canShare) { snapshotOptions = { containsGif: true, processingGif: false, canShare: canShare, isLoggedIn: isLoggedIn, - canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"), + canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"), }; imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ From bed2ea052df8dbe4f08b501dd0646b0eeb905d7a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 Jan 2018 09:44:49 -0800 Subject: [PATCH 125/381] better solution --- libraries/entities/src/EntityTree.cpp | 18 +++--------------- libraries/entities/src/EntityTree.h | 2 -- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8dad126d30..f485f4121e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -65,7 +65,7 @@ public: EntityTree::EntityTree(bool shouldReaverage) : - Octree(shouldReaverage) + Octree(shouldReaverage), _needsParentFixupLock(QReadWriteLock::Recursive) { resetClientEditStats(); @@ -1679,23 +1679,13 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } } -QVector EntityTree::getEntitiesParentFixup() const { - QReadLocker locker(&_needsParentFixupLock); - return _needsParentFixup; -} - -void EntityTree::setNeedsParentFixup(QVector entitiesFixup) { - QWriteLocker locker(&_needsParentFixupLock); - _needsParentFixup = entitiesFixup; -} - void EntityTree::fixupNeedsParentFixups() { PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; - QVector entitiesParentFixup = getEntitiesParentFixup(); + QWriteLocker locker(&_needsParentFixupLock); - QMutableVectorIterator iter(entitiesParentFixup); + QMutableVectorIterator iter(_needsParentFixup); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); @@ -1758,8 +1748,6 @@ void EntityTree::fixupNeedsParentFixups() { PerformanceTimer perfTimer("recurseTreeWithOperator"); recurseTreeWithOperator(&moveOperator); } - - setNeedsParentFixup(entitiesParentFixup); } void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index f4ff7ad118..8cb89d6493 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -385,8 +385,6 @@ private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); - QVector getEntitiesParentFixup() const; - void setNeedsParentFixup(QVector entitiesFixup); std::shared_ptr _myAvatar{ nullptr }; }; From 4c0a1732874c205d3235f1ae28536c8b648f31d7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 Jan 2018 10:23:19 -0800 Subject: [PATCH 126/381] trying another solution --- libraries/entities/src/EntityTree.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f485f4121e..bf29f3bec9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -65,7 +65,7 @@ public: EntityTree::EntityTree(bool shouldReaverage) : - Octree(shouldReaverage), _needsParentFixupLock(QReadWriteLock::Recursive) + Octree(shouldReaverage) { resetClientEditStats(); @@ -1682,10 +1682,14 @@ void EntityTree::entityChanged(EntityItemPointer entity) { void EntityTree::fixupNeedsParentFixups() { PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; + QVector entitiesToFixup; + { + QWriteLocker locker(&_needsParentFixupLock); + entitiesToFixup = _needsParentFixup; + _needsParentFixup.clear(); + } - QWriteLocker locker(&_needsParentFixupLock); - - QMutableVectorIterator iter(_needsParentFixup); + QMutableVectorIterator iter(entitiesToFixup); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); @@ -1748,6 +1752,12 @@ void EntityTree::fixupNeedsParentFixups() { PerformanceTimer perfTimer("recurseTreeWithOperator"); recurseTreeWithOperator(&moveOperator); } + + { + QWriteLocker locker(&_needsParentFixupLock); + // add back the entities that did not get fixup + _needsParentFixup.append(entitiesToFixup); + } } void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { From 5a771e3a168357708d646dcba70c79ceb7eb4cb4 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Fri, 26 Jan 2018 17:08:53 -0500 Subject: [PATCH 127/381] [Case 10865] Ctrl/Cmd/Shift+Click Multi-Select for Asset Browser. * Removes manual selectionModel update from controls-uit/Tree.qml * This was interfering with the Key+Click functionality. It was introduced via Commit 99617600c47 to address a selection issue; however, it's unclear what that issue was. Selection within the Asset Browser works without it at present. * Amends the ContextMenu within the AssetBrowser to work in conjunction with the multi-selection changes. * ContextMenu will _only_ display when triggered within the current selection be it one or more items as opposed to previous behavior of selecting the item the menu was triggered on. * CopyURL is only enabled when the selection size is 1 * Rename is only enabled when the selection size is 1 * Delete is enabled when the selection size is greater than 0 Changes Committed: modified: interface/resources/qml/controls-uit/Tree.qml modified: interface/resources/qml/hifi/AssetServer.qml --- interface/resources/qml/controls-uit/Tree.qml | 4 -- interface/resources/qml/hifi/AssetServer.qml | 41 +++++++++++-------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 6bd11295b1..5199a10a27 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -202,8 +202,4 @@ TreeView { } onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index) - - onClicked: { - selectionModel.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect); - } } diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 37c3c2adab..7c16b19865 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -694,7 +694,7 @@ Windows.ScrollingWindow { } } } - } + }// End_OF( itemLoader ) Rectangle { id: treeLabelToolTip @@ -731,50 +731,59 @@ Windows.ScrollingWindow { showTimer.stop(); treeLabelToolTip.visible = false; } - } + }// End_OF( treeLabelToolTip ) MouseArea { propagateComposedEvents: true anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: { - if (!HMD.active) { // Popup only displays properly on desktop - var index = treeView.indexAt(mouse.x, mouse.y); - treeView.selection.setCurrentIndex(index, 0x0002); - contextMenu.currentIndex = index; - contextMenu.popup(); + if (treeView.selection.hasSelection && !HMD.active) { // Popup only displays properly on desktop + // Only display the popup if the click triggered within + // the selection. + var clickedIndex = treeView.indexAt(mouse.x, mouse.y); + var displayContextMenu = false; + for ( var i = 0; i < selectedItems; ++i) { + var currentSelectedIndex = treeView.selection.selectedIndexes[i]; + if (clickedIndex === currentSelectedIndex) { + contextMenu.popup(); + break; + } + } } } } - + Menu { id: contextMenu title: "Edit" property var url: "" - property var currentIndex: null MenuItem { text: "Copy URL" + enabled: (selectedItems == 1) onTriggered: { - copyURLToClipboard(contextMenu.currentIndex); + copyURLToClipboard(treeView.selection.currentIndex); } } MenuItem { text: "Rename" + enabled: (selectedItems == 1) onTriggered: { - renameFile(contextMenu.currentIndex); + renameFile(treeView.selection.currentIndex); } } MenuItem { text: "Delete" + enabled: (selectedItems > 0) onTriggered: { - deleteFile(contextMenu.currentIndex); + deleteFile(); } } - } - } + }// End_OF( contextMenu ) + }// End_OF( treeView ) Row { id: infoRow @@ -885,7 +894,7 @@ Windows.ScrollingWindow { "Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors."); } } - } + }// End_OF( infoRow ) HifiControls.ContentSection { id: uploadSection @@ -945,7 +954,7 @@ Windows.ScrollingWindow { } } } - } + }// End_OF( uploadSection ) } } From dfdf28f37e4572bb95b6354be850356ad8bffa27 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Fri, 26 Jan 2018 17:34:05 -0500 Subject: [PATCH 128/381] [Case 10865] Some cleanup for multi-selection (details below). * Remove setCurrentIndex call when looping over current selection. * Changed selectedItems var name to selectedItemCount to clarify what it represents. * Change path arg name to paths to clarify that there can be more than a single path contained within it. Changes Committed: modified: interface/resources/qml/hifi/AssetServer.qml --- interface/resources/qml/hifi/AssetServer.qml | 49 ++++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 7c16b19865..30f76fecd6 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -37,7 +37,7 @@ Windows.ScrollingWindow { property var assetProxyModel: Assets.proxyModel; property var assetMappingsModel: Assets.mappingModel; property var currentDirectory; - property var selectedItems: treeView.selection.selectedIndexes.length; + property var selectedItemCount: treeView.selection.selectedIndexes.length; Settings { category: "Overlay.AssetServer" @@ -75,17 +75,17 @@ Windows.ScrollingWindow { }); } - function doDeleteFile(path) { - console.log("Deleting " + path); + function doDeleteFile(paths) { + console.log("Deleting " + paths); - Assets.deleteMappings(path, function(err) { + Assets.deleteMappings(paths, function(err) { if (err) { - console.log("Asset browser - error deleting path: ", path, err); + console.log("Asset browser - error deleting paths: ", paths, err); - box = errorMessageBox("There was an error deleting:\n" + path + "\n" + err); + box = errorMessageBox("There was an error deleting:\n" + paths + "\n" + err); box.selected.connect(reload); } else { - console.log("Asset browser - finished deleting path: ", path); + console.log("Asset browser - finished deleting paths: ", paths); reload(); } }); @@ -145,7 +145,7 @@ Windows.ScrollingWindow { function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i]; - if (selectedItems > 1) { + if (selectedItemCount > 1) { return false; } @@ -155,7 +155,7 @@ Windows.ScrollingWindow { } function canRename() { - if (treeView.selection.hasSelection && selectedItems == 1) { + if (treeView.selection.hasSelection && selectedItemCount == 1) { return true; } else { return false; @@ -333,29 +333,28 @@ Windows.ScrollingWindow { }); } function deleteFile(index) { - var path = []; + var paths = []; if (!index) { - for (var i = 0; i < selectedItems; i++) { - treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100); - index = treeView.selection.currentIndex; - path[i] = assetProxyModel.data(index, 0x100); + for (var i = 0; i < selectedItemCount; ++i) { + index = treeView.selection.selectedIndexes[i]; + paths[i] = assetProxyModel.data(index, 0x100); } } - if (!path) { + if (!paths) { return; } var modalMessage = ""; - var items = selectedItems.toString(); + var items = selectedItemCount.toString(); var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); var typeString = isFolder ? 'folder' : 'file'; - if (selectedItems > 1) { + if (selectedItemCount > 1) { modalMessage = "You are about to delete " + items + " items \nDo you want to continue?"; } else { - modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"; + modalMessage = "You are about to delete the following " + typeString + ":\n" + paths + "\nDo you want to continue?"; } var object = desktop.messageBox({ @@ -367,7 +366,7 @@ Windows.ScrollingWindow { }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { - doDeleteFile(path); + doDeleteFile(paths); } }); } @@ -743,7 +742,7 @@ Windows.ScrollingWindow { // the selection. var clickedIndex = treeView.indexAt(mouse.x, mouse.y); var displayContextMenu = false; - for ( var i = 0; i < selectedItems; ++i) { + for ( var i = 0; i < selectedItemCount; ++i) { var currentSelectedIndex = treeView.selection.selectedIndexes[i]; if (clickedIndex === currentSelectedIndex) { contextMenu.popup(); @@ -761,7 +760,7 @@ Windows.ScrollingWindow { MenuItem { text: "Copy URL" - enabled: (selectedItems == 1) + enabled: (selectedItemCount == 1) onTriggered: { copyURLToClipboard(treeView.selection.currentIndex); } @@ -769,7 +768,7 @@ Windows.ScrollingWindow { MenuItem { text: "Rename" - enabled: (selectedItems == 1) + enabled: (selectedItemCount == 1) onTriggered: { renameFile(treeView.selection.currentIndex); } @@ -777,7 +776,7 @@ Windows.ScrollingWindow { MenuItem { text: "Delete" - enabled: (selectedItems > 0) + enabled: (selectedItemCount > 0) onTriggered: { deleteFile(); } @@ -796,8 +795,8 @@ Windows.ScrollingWindow { function makeText() { var numPendingBakes = assetMappingsModel.numPendingBakes; - if (selectedItems > 1 || numPendingBakes === 0) { - return selectedItems + " items selected"; + if (selectedItemCount > 1 || numPendingBakes === 0) { + return selectedItemCount + " items selected"; } else { return numPendingBakes + " bakes pending" } From 70e23f3ce86f5837f24eaea39c422aeea23c64f0 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 30 Jan 2018 14:00:09 -0500 Subject: [PATCH 129/381] [Case 10865] Bringing in multi-select fix from Desktop AssetServer. Changes Committed: modified: interface/resources/qml/hifi/dialogs/TabletAssetServer.qml --- .../qml/hifi/dialogs/TabletAssetServer.qml | 82 ++++++++++--------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index a02496a252..9c61206592 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -39,7 +39,7 @@ Rectangle { property var assetProxyModel: Assets.proxyModel; property var assetMappingsModel: Assets.mappingModel; property var currentDirectory; - property var selectedItems: treeView.selection.selectedIndexes.length; + property var selectedItemCount: treeView.selection.selectedIndexes.length; Settings { category: "Overlay.AssetServer" @@ -76,17 +76,17 @@ Rectangle { }); } - function doDeleteFile(path) { - console.log("Deleting " + path); + function doDeleteFile(paths) { + console.log("Deleting " + paths); - Assets.deleteMappings(path, function(err) { + Assets.deleteMappings(paths, function(err) { if (err) { - console.log("Asset browser - error deleting path: ", path, err); + console.log("Asset browser - error deleting paths: ", paths, err); - box = errorMessageBox("There was an error deleting:\n" + path + "\n" + err); + box = errorMessageBox("There was an error deleting:\n" + paths + "\n" + err); box.selected.connect(reload); } else { - console.log("Asset browser - finished deleting path: ", path); + console.log("Asset browser - finished deleting paths: ", paths); reload(); } }); @@ -146,7 +146,7 @@ Rectangle { function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i]; - if (selectedItems > 1) { + if (selectedItemCount > 1) { return false; } @@ -156,7 +156,7 @@ Rectangle { } function canRename() { - if (treeView.selection.hasSelection && selectedItems == 1) { + if (treeView.selection.hasSelection && selectedItemCount == 1) { return true; } else { return false; @@ -334,29 +334,28 @@ Rectangle { }); } function deleteFile(index) { - var path = []; + var paths = []; if (!index) { - for (var i = 0; i < selectedItems; i++) { - treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100); - index = treeView.selection.currentIndex; - path[i] = assetProxyModel.data(index, 0x100); + for (var i = 0; i < selectedItemCount; ++i) { + index = treeView.selection.selectedIndexes[i]; + paths[i] = assetProxyModel.data(index, 0x100); } } - if (!path) { + if (!paths) { return; } var modalMessage = ""; - var items = selectedItems.toString(); + var items = selectedItemCount.toString(); var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); var typeString = isFolder ? 'folder' : 'file'; - if (selectedItems > 1) { + if (selectedItemCount > 1) { modalMessage = "You are about to delete " + items + " items \nDo you want to continue?"; } else { - modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"; + modalMessage = "You are about to delete the following " + typeString + ":\n" + paths + "\nDo you want to continue?"; } var object = tabletRoot.messageBox({ @@ -368,7 +367,7 @@ Rectangle { }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { - doDeleteFile(path); + doDeleteFile(paths); } }); } @@ -693,7 +692,7 @@ Rectangle { } } } - } + }// End_OF( itemLoader ) Rectangle { id: treeLabelToolTip @@ -730,50 +729,59 @@ Rectangle { showTimer.stop(); treeLabelToolTip.visible = false; } - } + }// End_OF( treeLabelToolTip ) MouseArea { propagateComposedEvents: true anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: { - if (!HMD.active) { // Popup only displays properly on desktop - var index = treeView.indexAt(mouse.x, mouse.y); - treeView.selection.setCurrentIndex(index, 0x0002); - contextMenu.currentIndex = index; - contextMenu.popup(); + if (treeView.selection.hasSelection && !HMD.active) { // Popup only displays properly on desktop + // Only display the popup if the click triggered within + // the selection. + var clickedIndex = treeView.indexAt(mouse.x, mouse.y); + var displayContextMenu = false; + for ( var i = 0; i < selectedItemCount; ++i) { + var currentSelectedIndex = treeView.selection.selectedIndexes[i]; + if (clickedIndex === currentSelectedIndex) { + contextMenu.popup(); + break; + } + } } } } - + Menu { id: contextMenu title: "Edit" property var url: "" - property var currentIndex: null MenuItem { text: "Copy URL" + enabled: (selectedItemCount == 1) onTriggered: { - copyURLToClipboard(contextMenu.currentIndex); + copyURLToClipboard(treeView.selection.currentIndex); } } MenuItem { text: "Rename" + enabled: (selectedItemCount == 1) onTriggered: { - renameFile(contextMenu.currentIndex); + renameFile(treeView.selection.currentIndex); } } MenuItem { text: "Delete" + enabled: (selectedItemCount > 0) onTriggered: { - deleteFile(contextMenu.currentIndex); + deleteFile(); } } - } - } + }// End_OF( contextMenu ) + }// End_OF( treeView ) Row { id: infoRow @@ -786,8 +794,8 @@ Rectangle { function makeText() { var numPendingBakes = assetMappingsModel.numPendingBakes; - if (selectedItems > 1 || numPendingBakes === 0) { - return selectedItems + " items selected"; + if (selectedItemCount > 1 || numPendingBakes === 0) { + return selectedItemCount + " items selected"; } else { return numPendingBakes + " bakes pending" } @@ -884,7 +892,7 @@ Rectangle { "Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors."); } } - } + }// End_OF( infoRow ) HifiControls.TabletContentSection { id: uploadSection @@ -961,7 +969,7 @@ Rectangle { } } } - } + }// End_OF( uploadSection ) } } From 2dcedf9f3901dac39067d6bf6aedad9b4ead28df Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 30 Jan 2018 12:52:05 -0800 Subject: [PATCH 130/381] cherry picking Tony's fix for shader compilation time not taking soo long and adding better feedback from shader compilation --- interface/src/Application.cpp | 23 ++- libraries/gl/src/gl/GLShaders.cpp | 161 +++++++++++------- libraries/gl/src/gl/GLShaders.h | 6 +- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 4 +- .../gpu-gl/src/gpu/gl/GLBackendShader.cpp | 44 +++-- libraries/gpu-gl/src/gpu/gl/GLShader.cpp | 8 +- libraries/gpu-gl/src/gpu/gl/GLShader.h | 4 +- libraries/gpu-gles/src/gpu/gl/GLBackend.h | 4 +- .../gpu-gles/src/gpu/gl/GLBackendShader.cpp | 65 ++++++- libraries/gpu-gles/src/gpu/gl/GLShader.cpp | 8 +- libraries/gpu-gles/src/gpu/gl/GLShader.h | 4 +- libraries/gpu/src/gpu/Context.cpp | 4 +- libraries/gpu/src/gpu/Context.h | 4 +- libraries/gpu/src/gpu/Shader.cpp | 11 +- libraries/gpu/src/gpu/Shader.h | 29 +++- libraries/render/src/render/ShapePipeline.cpp | 52 +++--- 16 files changed, 297 insertions(+), 134 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64e9feea02..f1d0378792 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -396,6 +396,7 @@ public: setObjectName("Deadlock Watchdog"); // Give the heartbeat an initial value _heartbeat = usecTimestampNow(); + _paused = false; connect(qApp, &QCoreApplication::aboutToQuit, [this] { _quit = true; }); @@ -413,11 +414,20 @@ public: *crashTrigger = 0xDEAD10CC; } + static void pause() { + _paused = true; + } + + static void resume() { + _paused = false; + updateHeartbeat(); + } + void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); // Don't do heartbeat detection under nsight - if (nsightActive()) { + if (nsightActive() || _paused) { continue; } uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us @@ -473,6 +483,7 @@ public: } } + static std::atomic _paused; static std::atomic _heartbeat; static std::atomic _maxElapsed; static std::atomic _maxElapsedAverage; @@ -481,6 +492,7 @@ public: bool _quit { false }; }; +std::atomic DeadlockWatchdogThread::_paused; std::atomic DeadlockWatchdogThread::_heartbeat; std::atomic DeadlockWatchdogThread::_maxElapsed; std::atomic DeadlockWatchdogThread::_maxElapsedAverage; @@ -2269,6 +2281,11 @@ void Application::initializeGL() { initDisplay(); qCDebug(interfaceapp, "Initialized Display."); +#ifdef Q_OS_OSX + // FIXME: on mac os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. + +DeadlockWatchdogThread::pause(); +#endif + // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; @@ -2283,6 +2300,10 @@ void Application::initializeGL() { // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. DependencyManager::get()->initializeShapePipelines(); +#ifdef Q_OS_OSX + DeadlockWatchdogThread::resume(); +#endif + _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->setObjectName("MainThreadContext"); _offscreenContext->create(_glWidget->qglContext()); diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index 017c92b71c..ecd6fe3323 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -6,9 +6,9 @@ namespace gl { #ifdef SEPARATE_PROGRAM - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& error) { + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message) { #else - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& error) { + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message) { #endif if (shaderSource.empty()) { qCDebug(glLogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; @@ -34,52 +34,57 @@ namespace gl { GLint compiled = 0; glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); - // if compilation fails - if (!compiled) { - - // save the source code to a temp file so we can debug easily - /* - std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << srcstr[0]; - filestream << srcstr[1]; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + GLint infoLength = 0; + glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + if ((infoLength > 0) || !compiled) { char* temp = new char[infoLength]; glGetShaderInfoLog(glshader, infoLength, NULL, temp); + message = std::string(temp); - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << std::string(temp); - filestream.close(); - } - */ - - qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; - int lineNumber = 0; - for (auto s : srcstr) { - QString str(s); - QStringList lines = str.split("\n"); - for (auto& line : lines) { - qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + // if compilation fails + if (!compiled) { + // save the source code to a temp file so we can debug easily + /* + std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << srcstr[0]; + filestream << srcstr[1]; + filestream.close(); } + */ + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); + filestream.close(); + } + */ + + qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + int lineNumber = 0; + for (auto s : srcstr) { + QString str(s); + QStringList lines = str.split("\n"); + for (auto& line : lines) { + qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + } + } + qCCritical(glLogging) << "GLShader::compileShader - errors:"; + qCCritical(glLogging) << temp; + + delete[] temp; + glDeleteShader(glshader); + return false; } - qCCritical(glLogging) << "GLShader::compileShader - errors:"; - qCCritical(glLogging) << temp; - error = std::string(temp); + // Compilation success + qCWarning(glLogging) << "GLShader::compileShader - Success:"; + qCWarning(glLogging) << temp; delete[] temp; - - glDeleteShader(glshader); - return false; } #ifdef SEPARATE_PROGRAM @@ -137,7 +142,7 @@ namespace gl { return true; } -GLuint compileProgram(const std::vector& glshaders, std::string& error) { +GLuint compileProgram(const std::vector& glshaders, std::string& message, std::vector& binary) { // A brand new program: GLuint glprogram = glCreateProgram(); if (!glprogram) { @@ -157,39 +162,65 @@ GLuint compileProgram(const std::vector& glshaders, std::string& error) GLint linked = 0; glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - /* - // save the source code to a temp file so we can debug easily - std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << shaderSource->source; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + GLint infoLength = 0; + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + if ((infoLength > 0) || !linked) { char* temp = new char[infoLength]; glGetProgramInfoLog(glprogram, infoLength, NULL, temp); - qCDebug(glLogging) << "GLShader::compileProgram - failed to LINK the gl program object :"; - qCDebug(glLogging) << temp; + message = std::string(temp); - error = std::string(temp); - delete[] temp; + if (!linked) { + /* + // save the source code to a temp file so we can debug easily + std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << shaderSource->source; + filestream.close(); + } + */ - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << std::string(temp); - filestream.close(); + qCDebug(glLogging) << "GLShader::compileProgram - failed to LINK the gl program object :"; + qCDebug(glLogging) << temp; + + delete[] temp; + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); + filestream.close(); + } + */ + + glDeleteProgram(glprogram); + return 0; + } else { + qCDebug(glLogging) << "GLShader::compileProgram - success:"; + qCDebug(glLogging) << temp; + delete[] temp; } - */ + } - glDeleteProgram(glprogram); - return 0; + // If linked get the binaries + if (linked) { + GLint binaryLength = 0; + glGetProgramiv(glprogram, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + + if (binaryLength > 0) { + GLint numBinFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinFormats); + if (numBinFormats > 0) { + binary.resize(binaryLength); + std::vector binFormats(numBinFormats); + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, binFormats.data()); + + GLenum programBinFormat; + glGetProgramBinary(glprogram, binaryLength, NULL, &programBinFormat, binary.data()); + } + } } return glprogram; diff --git a/libraries/gl/src/gl/GLShaders.h b/libraries/gl/src/gl/GLShaders.h index a6213fd280..c5262b6b61 100644 --- a/libraries/gl/src/gl/GLShaders.h +++ b/libraries/gl/src/gl/GLShaders.h @@ -17,12 +17,12 @@ namespace gl { #ifdef SEPARATE_PROGRAM - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& error); + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message); #else - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& error); + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message); #endif - GLuint compileProgram(const std::vector& glshaders, std::string& error); + GLuint compileProgram(const std::vector& glshaders, std::string& message, , std::vector& binary); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 5558d3ada1..004bfe1c85 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -64,7 +64,7 @@ protected: explicit GLBackend(bool syncCache); GLBackend(); public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet(), Shader::CompilationHandler handler = nullptr); virtual ~GLBackend(); @@ -424,7 +424,7 @@ protected: // Backend dependant compilation of the shader virtual GLShader* compileBackendProgram(const Shader& program); - virtual GLShader* compileBackendShader(const Shader& shader); + virtual GLShader* compileBackendShader(const Shader& shader, Shader::CompilationHandler handler); virtual std::string getBackendShaderHeader() const; virtual void makeProgramBindings(ShaderObject& shaderObject); class ElementResource { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp index 9adfd550ef..84170dbffd 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp @@ -56,28 +56,43 @@ static const std::array VERSION_DEFINES { { stereoVersion } }; -GLShader* GLBackend::compileBackendShader(const Shader& shader) { +GLShader* GLBackend::compileBackendShader(const Shader& shader, Shader::CompilationHandler handler) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; GLShader::ShaderObjects shaderObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; - std::string error; + if (handler) { + bool retest = true; + std::string currentSrc = shaderSource; + while (retest) { + bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + compilationLogs[version].compiled = result; + if (!result) { + std::string newSrc; + retest = handler(shader, currentSrc, compilationLogs[version], newSrc); + currentSrc = newSrc; + } else { + retest = false; + } + } + } else { + compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + } -#ifdef SEPARATE_PROGRAM - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error); -#else - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); -#endif - if (!result) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + if (!compilationLogs[version].compiled) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); + shader.setCompilationLogs(compilationLogs); return nullptr; } } + // Compilation feedback + shader.setCompilationLogs(compilationLogs); // So far so good, the shader is created successfully GLShader* object = new GLShader(this->shared_from_this()); @@ -93,6 +108,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program) { GLShader::ShaderObjects programObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + for (int version = 0; version < GLShader::NumVersions; version++) { auto& programObject = programObjects[version]; @@ -104,14 +121,15 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program) { shaderGLObjects.push_back(object->_shaderObjects[version].glshader); } else { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + program.setCompilationLogs(compilationLogs); return nullptr; } } - std::string error; - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str(); + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + program.setCompilationLogs(compilationLogs); return nullptr; } @@ -119,6 +137,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program) { makeProgramBindings(programObject); } + // Compilation feedback + program.setCompilationLogs(compilationLogs); // So far so good, the program versions have all been created successfully GLShader* object = new GLShader(this->shared_from_this()); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index 7ed9121978..a626376e27 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -30,7 +30,7 @@ GLShader::~GLShader() { } } -GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader, Shader::CompilationHandler handler) { GLShader* object = Backend::getGPUObject(shader); // If GPU object already created then good @@ -45,7 +45,7 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = backend.compileBackendShader(shader); + GLShader* tempObject = backend.compileBackendShader(shader, handler); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); @@ -56,10 +56,10 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { return object; } -bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, Shader::CompilationHandler handler) { // First make sure the Shader has been compiled - GLShader* object = sync(backend, shader); + GLShader* object = sync(backend, shader, handler); if (!object) { return false; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl/src/gpu/gl/GLShader.h index dcf2dc330d..42d63f8dfb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.h @@ -21,8 +21,8 @@ struct ShaderObject { class GLShader : public GPUObject { public: - static GLShader* sync(GLBackend& backend, const Shader& shader); - static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); + static GLShader* sync(GLBackend& backend, const Shader& shader, Shader::CompilationHandler handler = nullptr); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, Shader::CompilationHandler handler = nullptr); enum Version { Mono = 0, diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h index ea06b6b672..bb8e15bad3 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -61,7 +61,7 @@ protected: explicit GLBackend(bool syncCache); GLBackend(); public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet(), Shader::CompilationHandler handler = nullptr); virtual ~GLBackend(); @@ -421,7 +421,7 @@ protected: // Backend dependant compilation of the shader virtual GLShader* compileBackendProgram(const Shader& program); - virtual GLShader* compileBackendShader(const Shader& shader); + virtual GLShader* compileBackendShader(const Shader& shader, Shader::CompilationHandler handler); virtual std::string getBackendShaderHeader() const; virtual void makeProgramBindings(ShaderObject& shaderObject); class ElementResource { diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp index fd44ad462f..26dbb12cf7 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp @@ -56,11 +56,12 @@ static const std::array VERSION_DEFINES { { stereoVersion } }; -GLShader* GLBackend::compileBackendShader(const Shader& shader) { +GLShader* GLBackend::compileBackendShader(const Shader& shader, Shader::CompilationHandler handler) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; GLShader::ShaderObjects shaderObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; @@ -90,6 +91,55 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader) { return object; } +GLShader* GLBackend::compileBackendShader(const Shader& shader, Shader::CompilationHandler handler) { + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + GLShader::ShaderObjects shaderObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = shaderObjects[version]; + + std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] + + "\n#extension GL_EXT_texture_buffer : enable" + + "\nprecision lowp float; // check precision 2" + + "\nprecision lowp samplerBuffer;" + + "\nprecision lowp sampler2DShadow;"; + if (handler) { + bool retest = true; + std::string currentSrc = shaderSource; + while (retest) { + bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + compilationLogs[version].compiled = result; + if (!result) { + std::string newSrc; + retest = handler(shader, currentSrc, compilationLogs[version], newSrc); + currentSrc = newSrc; + } else { + retest = false; + } + } + } else { + compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + } + + if (!compilationLogs[version].compiled) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); + shader.setCompilationLogs(compilationLogs); + return nullptr; + } + } + // Compilation feedback + shader.setCompilationLogs(compilationLogs); + + // So far so good, the shader is created successfully + GLShader* object = new GLShader(this->shared_from_this()); + object->_shaderObjects = shaderObjects; + + return object; +} + GLShader* GLBackend::compileBackendProgram(const Shader& program) { if (!program.isProgram()) { return nullptr; @@ -97,25 +147,28 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program) { GLShader::ShaderObjects programObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + for (int version = 0; version < GLShader::NumVersions; version++) { auto& programObject = programObjects[version]; // Let's go through every shaders and make sure they are ready to go - std::vector shaderGLObjects; + std::vector< GLuint > shaderGLObjects; for (auto subShader : program.getShaders()) { auto object = GLShader::sync((*this), *subShader); if (object) { shaderGLObjects.push_back(object->_shaderObjects[version].glshader); } else { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + program.setCompilationLogs(compilationLogs); return nullptr; } } - std::string error; - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str(); + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + program.setCompilationLogs(compilationLogs); return nullptr; } @@ -123,6 +176,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program) { makeProgramBindings(programObject); } + // Compilation feedback + program.setCompilationLogs(compilationLogs); // So far so good, the program versions have all been created successfully GLShader* object = new GLShader(this->shared_from_this()); diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp index 7ed9121978..a626376e27 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp @@ -30,7 +30,7 @@ GLShader::~GLShader() { } } -GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader, Shader::CompilationHandler handler) { GLShader* object = Backend::getGPUObject(shader); // If GPU object already created then good @@ -45,7 +45,7 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = backend.compileBackendShader(shader); + GLShader* tempObject = backend.compileBackendShader(shader, handler); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); @@ -56,10 +56,10 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { return object; } -bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, Shader::CompilationHandler handler) { // First make sure the Shader has been compiled - GLShader* object = sync(backend, shader); + GLShader* object = sync(backend, shader, handler); if (!object) { return false; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h index dcf2dc330d..42d63f8dfb 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.h @@ -21,8 +21,8 @@ struct ShaderObject { class GLShader : public GPUObject { public: - static GLShader* sync(GLBackend& backend, const Shader& shader); - static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); + static GLShader* sync(GLBackend& backend, const Shader& shader, Shader::CompilationHandler handler = nullptr); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, Shader::CompilationHandler handler = nullptr); enum Version { Mono = 0, diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 24128524da..2399d3ddc3 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -127,7 +127,7 @@ void Context::executeFrame(const FramePointer& frame) const { _frameStats.evalDelta(beginStats, endStats); } -bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { +bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings, Shader::CompilationHandler handler) { // If we're running in another DLL context, we need to fetch the program callback out of the application // FIXME find a way to do this without reliance on Qt app properties if (!_makeProgramCallback) { @@ -135,7 +135,7 @@ bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { _makeProgramCallback = reinterpret_cast(rawCallback); } if (shader.isProgram() && _makeProgramCallback) { - return _makeProgramCallback(shader, bindings); + return _makeProgramCallback(shader, bindings, handler); } return false; } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 7b7575e9ed..7d04463609 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -143,7 +143,7 @@ class Context { public: using Size = Resource::Size; typedef BackendPointer (*CreateBackend)(); - typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings); + typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings, Shader::CompilationHandler handler); // This one call must happen before any context is created or used (Shader::MakeProgram) in order to setup the Backend and any singleton data needed @@ -262,7 +262,7 @@ protected: // makeProgramShader(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings, Shader::CompilationHandler handler = nullptr); static CreateBackend _createBackendCallback; static MakeProgram _makeProgramCallback; diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 398a269f3f..35c9ad8ae2 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -82,9 +82,16 @@ void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& uniformBuffers, _outputs = outputs; } -bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { +bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings, CompilationHandler handler) { if (shader.isProgram()) { - return Context::makeProgram(shader, bindings); + return Context::makeProgram(shader, bindings, handler); } return false; } + +void Shader::setCompilationLogs(const CompilationLogs& logs) const { + _compilationLogs.clear(); + for (const auto& log : logs) { + _compilationLogs.emplace_back(CompilationLog(log)); + } +} diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 181c9b5e78..33449fe656 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -44,6 +44,19 @@ public: Language _lang = GLSL; }; + struct CompilationLog { + std::string message; + std::vector binary; + bool compiled{ false }; + + CompilationLog() {} + CompilationLog(const CompilationLog& src) : + message(src.message), + binary(src.binary), + compiled(src.compiled) {} + }; + using CompilationLogs = std::vector; + static const int32 INVALID_LOCATION = -1; class Slot { @@ -155,6 +168,8 @@ public: const SlotSet& inputs, const SlotSet& outputs); + typedef bool(*CompilationHandler)(const Shader& shader, const std::string& src, CompilationLog& log, std::string& newSrc); + // makeProgram(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. @@ -168,7 +183,16 @@ public: // on a gl Context and the driver to compile the glsl shader. // Hoppefully in a few years the shader compilation will be completely abstracted in a separate shader compiler library // independant of the graphics api in use underneath (looking at you opengl & vulkan). - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet(), CompilationHandler handler = nullptr); + + // Check the compilation state + bool compilationHasFailed() const { return _compilationHasFailed; } + const CompilationLogs& getCompilationLogs() const { return _compilationLogs; } + + // Set COmpilation logs can only be called by the Backend layers + void setCompilationHasFailed(bool compilationHasFailed) { _compilationHasFailed = compilationHasFailed; } + void setCompilationLogs(const CompilationLogs& logs) const; + const GPUObjectPointer gpuObject {}; @@ -198,6 +222,9 @@ protected: // The type of the shader, the master key Type _type; + // Compilation logs (one for each versions generated) + mutable CompilationLogs _compilationLogs; + // Whether or not the shader compilation failed bool _compilationHasFailed { false }; }; diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index fb58c21dde..597a6a0fab 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -72,34 +72,36 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p BatchSetter batchSetter, ItemSetter itemSetter) { ShapeKey key{ filter._flags }; - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), Slot::BUFFER::LIGHTING_MODEL)); - slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), Slot::BUFFER::SKINNING)); - slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), Slot::BUFFER::MATERIAL)); - slotBindings.insert(gpu::Shader::Binding(std::string("texMapArrayBuffer"), Slot::BUFFER::TEXMAPARRAY)); - slotBindings.insert(gpu::Shader::Binding(std::string("albedoMap"), Slot::MAP::ALBEDO)); - slotBindings.insert(gpu::Shader::Binding(std::string("roughnessMap"), Slot::MAP::ROUGHNESS)); - slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), Slot::MAP::NORMAL)); - slotBindings.insert(gpu::Shader::Binding(std::string("metallicMap"), Slot::MAP::METALLIC)); - slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), Slot::MAP::EMISSIVE_LIGHTMAP)); - slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), Slot::MAP::OCCLUSION)); - slotBindings.insert(gpu::Shader::Binding(std::string("scatteringMap"), Slot::MAP::SCATTERING)); - slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), Slot::BUFFER::KEY_LIGHT)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::BUFFER::LIGHT)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), Slot::BUFFER::LIGHT_AMBIENT_BUFFER)); - slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT)); - slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK)); - slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS)); - slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL)); + if (program->getInputs().empty()) { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), Slot::BUFFER::LIGHTING_MODEL)); + slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), Slot::BUFFER::SKINNING)); + slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), Slot::BUFFER::MATERIAL)); + slotBindings.insert(gpu::Shader::Binding(std::string("texMapArrayBuffer"), Slot::BUFFER::TEXMAPARRAY)); + slotBindings.insert(gpu::Shader::Binding(std::string("albedoMap"), Slot::MAP::ALBEDO)); + slotBindings.insert(gpu::Shader::Binding(std::string("roughnessMap"), Slot::MAP::ROUGHNESS)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), Slot::MAP::NORMAL)); + slotBindings.insert(gpu::Shader::Binding(std::string("metallicMap"), Slot::MAP::METALLIC)); + slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), Slot::MAP::EMISSIVE_LIGHTMAP)); + slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), Slot::MAP::OCCLUSION)); + slotBindings.insert(gpu::Shader::Binding(std::string("scatteringMap"), Slot::MAP::SCATTERING)); + slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), Slot::BUFFER::KEY_LIGHT)); + slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::BUFFER::LIGHT)); + slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), Slot::BUFFER::LIGHT_AMBIENT_BUFFER)); + slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS)); + slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL)); - if (key.isTranslucent()) { - slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT)); + if (key.isTranslucent()) { + slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT)); + } + + gpu::Shader::makeProgram(*program, slotBindings); } - gpu::Shader::makeProgram(*program, slotBindings); - auto locations = std::make_shared(); locations->albedoTextureUnit = program->getTextures().findLocation("albedoMap"); From d5e52834ef7873270e28d0b86785d34a0679a642 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 30 Jan 2018 13:07:20 -0800 Subject: [PATCH 131/381] cherry picking Tony's fix for shader compilation time not taking soo long and adding better feedback from shader compilation --- libraries/gl/src/gl/GLShaders.h | 2 +- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 ++-- libraries/gpu-gles/src/gpu/gl/GLBackend.cpp | 4 ++-- libraries/gpu/src/gpu/Shader.h | 3 --- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/libraries/gl/src/gl/GLShaders.h b/libraries/gl/src/gl/GLShaders.h index c5262b6b61..fc070d7659 100644 --- a/libraries/gl/src/gl/GLShaders.h +++ b/libraries/gl/src/gl/GLShaders.h @@ -22,7 +22,7 @@ namespace gl { bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message); #endif - GLuint compileProgram(const std::vector& glshaders, std::string& message, , std::vector& binary); + GLuint compileProgram(const std::vector& glshaders, std::string& message, std::vector& binary); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index eb6de5df13..2a052c5210 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -68,8 +68,8 @@ GLBackend& getBackend() { return *INSTANCE; } -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { - return GLShader::makeProgram(getBackend(), shader, slotBindings); +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, Shader::CompilationHandler handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); } GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp index 6fd5df6f81..8a118b7b71 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -61,8 +61,8 @@ GLBackend& getBackend() { return *INSTANCE; } -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { - return GLShader::makeProgram(getBackend(), shader, slotBindings); +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, Shader::CompilationHandler handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); } diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 33449fe656..d78077a396 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -138,9 +138,6 @@ public: bool isProgram() const { return getType() > NUM_DOMAINS; } bool isDomain() const { return getType() < NUM_DOMAINS; } - void setCompilationHasFailed(bool compilationHasFailed) { _compilationHasFailed = compilationHasFailed; } - bool compilationHasFailed() const { return _compilationHasFailed; } - const Source& getSource() const { return _source; } const Shaders& getShaders() const { return _shaders; } From d0a0120dfe129e095525349b94a6bb3038ff7828 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Tue, 30 Jan 2018 13:34:38 -0800 Subject: [PATCH 132/381] removing old game sample from script-archive --- .../exterminatorGame/gameServer/.gitignore | 1 - .../exterminatorGame/gameServer/Procfile | 1 - .../exterminatorGame/gameServer/README.txt | 5 - .../games/exterminatorGame/gameServer/app.js | 76 - .../gameServer/client/app.jsx | 87 - .../exterminatorGame/gameServer/gulpfile.js | 15 - .../exterminatorGame/gameServer/package.json | 21 - .../gameServer/public/css/style.css | 36 - .../gameServer/public/index.html | 12 - .../gameServer/public/js/app.js | 30691 ---------------- .../pistolScriptSpawnerSpawner.js | 31 - 11 files changed, 30976 deletions(-) delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/.gitignore delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/Procfile delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/README.txt delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/app.js delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/client/app.jsx delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/gulpfile.js delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/package.json delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/public/css/style.css delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/public/index.html delete mode 100644 script-archive/example/games/exterminatorGame/gameServer/public/js/app.js delete mode 100644 script-archive/example/games/exterminatorGame/pistolScriptSpawnerSpawner.js diff --git a/script-archive/example/games/exterminatorGame/gameServer/.gitignore b/script-archive/example/games/exterminatorGame/gameServer/.gitignore deleted file mode 100644 index 30bc162798..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/node_modules \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/Procfile b/script-archive/example/games/exterminatorGame/gameServer/Procfile deleted file mode 100644 index 207d22f803..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: node app.js \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/README.txt b/script-archive/example/games/exterminatorGame/gameServer/README.txt deleted file mode 100644 index 6180f96212..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -This gameserver sets up a server with websockets that listen for messages from interface regarding when users shoot rats, and updates a real-time game board with that information. This is just a first pass, and the plan is to abstract this to work with any kind of game content creators wish to make with High Fidelity. - -To enter the game: Run pistol.js and shoot at rats. -For every rat you kill, you get a point. -You're score will be displayed at https://desolate-bastion-1742.herokuapp.com/ \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/app.js b/script-archive/example/games/exterminatorGame/gameServer/app.js deleted file mode 100644 index 30af1c2fae..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/app.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -/** - * Module dependencies. - */ - -var express = require('express'); -var http = require('http'); -var _ = require('underscore'); -var shortid = require('shortid'); - - -var app = express(); -var server = http.createServer(app); - -var WebSocketServer = require('websocket').server; -var wsServer = new WebSocketServer({ - httpServer: server -}); - -var users = []; -var connections = []; -wsServer.on('request', function(request) { - console.log("SOMEONE JOINED"); - var connection = request.accept(null, request.origin); - connections.push(connection); - connection.on('message', function(data) { - var userData = JSON.parse(data.utf8Data); - var user = _.find(users, function(user) { - return user.username === userData.username; - }); - if (user) { - // This user already exists, so just update score - users[users.indexOf(user)].score = userData.score; - } else { - users.push({ - id: shortid.generate(), - username: userData.username, - score: userData.score - }); - } - connections.forEach(function(aConnection) { - aConnection.sendUTF(JSON.stringify({ - users: users - })); - }) - }); -}); - -app.get('/users', function(req, res) { - res.send({ - users: users - }); -}); - - - -/* Configuration */ -app.set('views', __dirname + '/views'); -app.use(express.static(__dirname + '/public')); -app.set('port', (process.env.PORT || 5000)); - -if (process.env.NODE_ENV === 'development') { - app.use(express.errorHandler({ - dumpExceptions: true, - showStack: true - })); -} - - -/* Start server */ -server.listen(app.get('port'), function() { - console.log('Express server listening on port %d in %s mode', app.get('port'), app.get('env')); -}); - -module.exports = app; \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/client/app.jsx b/script-archive/example/games/exterminatorGame/gameServer/client/app.jsx deleted file mode 100644 index a8bd5fdb08..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/client/app.jsx +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -var React = require('react'); -var _ = require('underscore') -var $ = require('jquery'); - -var UserList = React.createClass({ -render: function(){ - var sortedUsers = _.sortBy(this.props.data.users, function(users){ - //Show higher scorers at top of board - return 1 - users.score; - }); - var users = sortedUsers.map(function(user) { - - return ( - - ) - }); - return ( -

{users}
- ) - } -}); - -var GameBoard = React.createClass({ - loadDataFromServer: function(data) { - $.ajax({ - url: this.props.url, - dataType: 'json', - cache: false, - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - getInitialState: function() { - return {data: {users: []}}; - }, - componentDidMount: function() { - this.loadDataFromServer(); - //set up web socket - var path = window.location.hostname + ":" + window.location.port; - console.log("LOCATION ", path) - var socketClient = new WebSocket("wss://" + path); - var self = this; - socketClient.onopen = function() { - console.log("CONNECTED"); - socketClient.onmessage = function(data) { - console.log("ON MESSAGE"); - self.setState({data: JSON.parse(data.data)}); - }; - }; - - }, - render: function() { - - return ( -
-
Kill All The Rats!
-
-
PLAYER
-
SCORE
-
- -
- ); - } -}); - -var User = React.createClass({ - render: function() { - return ( -
-
{this.props.username}
-
{this.props.score}
-
- ); - } -}) - -React.render( - , - document.getElementById('app') -); \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/gulpfile.js b/script-archive/example/games/exterminatorGame/gameServer/gulpfile.js deleted file mode 100644 index 84543dc28c..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/gulpfile.js +++ /dev/null @@ -1,15 +0,0 @@ -var gulp = require('gulp'); -var exec = require('child_process').exec; - -gulp.task('build', function() { - exec('npm run build', function(msg){ - console.log(msg); - }); -}); - -gulp.task('watch', function() { - gulp.watch('client/*.jsx', ['build']); -}); - - -gulp.task('default', ['build', 'watch']) \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/package.json b/script-archive/example/games/exterminatorGame/gameServer/package.json deleted file mode 100644 index 49befaad70..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "KillAllTheRats", - "version": "0.6.9", - "scripts": { - "build": "browserify ./client/app.jsx -t babelify --outfile ./public/js/app.js", - "start": "node app.js" - }, - "dependencies": { - "express": "^4.13.1", - "gulp": "^3.9.0", - "jquery": "^2.1.4", - "react": "^0.13.3", - "shortid": "^2.2.4", - "underscore": "^1.8.3", - "websocket": "^1.0.22" - }, - "devDependencies": { - "babelify": "^6.1.3", - "browserify": "^10.2.6" - } -} diff --git a/script-archive/example/games/exterminatorGame/gameServer/public/css/style.css b/script-archive/example/games/exterminatorGame/gameServer/public/css/style.css deleted file mode 100644 index b001103ac2..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/public/css/style.css +++ /dev/null @@ -1,36 +0,0 @@ -body { - font-family: Impact; - background-color: #009DC0 ; - font-size: 60px; -} - -.gameTitle { - color: #D61010; -} - -.entry{ - width:100%; - height:50px; - border:1px solid #A9D1E1; - color: white; - margin-right:10px; - padding: 10px; - float:left; - font-size: 40px; -} - -.boardHeader{ - width:100%; - height:50px; - border:5px solid #A9D1E1; - color: white; - margin-right:10px; - padding: 10px; - float:left; - font-size: 40px; -} -.username{ - font-weight: bold; - float: left; - margin-right: 50%; -} \ No newline at end of file diff --git a/script-archive/example/games/exterminatorGame/gameServer/public/index.html b/script-archive/example/games/exterminatorGame/gameServer/public/index.html deleted file mode 100644 index 125039a934..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/public/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Kill The Rats! - - -
- - - diff --git a/script-archive/example/games/exterminatorGame/gameServer/public/js/app.js b/script-archive/example/games/exterminatorGame/gameServer/public/js/app.js deleted file mode 100644 index 38f8bf3727..0000000000 --- a/script-archive/example/games/exterminatorGame/gameServer/public/js/app.js +++ /dev/null @@ -1,30691 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - setTimeout(drainQueue, 0); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],3:[function(require,module,exports){ -/*! - * jQuery JavaScript Library v2.1.4 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2015-04-28T16:01Z - */ - -(function( global, factory ) { - - if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Support: Firefox 18+ -// Can't be in strict mode, several libs including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -// - -var arr = []; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var support = {}; - - - -var - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - version = "2.1.4", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android<4.1 - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // Start with an empty selector - selector: "", - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num != null ? - - // Return just the one element from the set - ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - - // Return all the elements in a clean array - slice.call( this ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - // adding 1 corrects loss of precision from parseFloat (#15100) - return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; - }, - - isPlainObject: function( obj ) { - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.constructor && - !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - // Support: Android<4.0, iOS<6 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - globalEval: function( code ) { - var script, - indirect = eval; - - code = jQuery.trim( code ); - - if ( code ) { - // If the code includes a valid, prologue position - // strict mode pragma, execute code by injecting a - // script tag into the document. - if ( code.indexOf("use strict") === 1 ) { - script = document.createElement("script"); - script.text = code; - document.head.appendChild( script ).parentNode.removeChild( script ); - } else { - // Otherwise, avoid the DOM node creation, insertion - // and removal by using an indirect global eval - indirect( code ); - } - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Support: IE9-11+ - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } - } - - return obj; - }, - - // Support: Android<4.1 - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -function isArraylike( obj ) { - - // Support: iOS 8.2 (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = "length" in obj && obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.2.0-pre - * http://sizzlejs.com/ - * - * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-12-16 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // General-purpose constants - MAX_NEGATIVE = 1 << 31, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf as it's faster than native - // http://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + characterEncoding + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - rescape = /'|\\/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }; - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - nodeType = context.nodeType; - - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - if ( !seed && documentIsHTML ) { - - // Try to shortcut find operations when possible (e.g., not under DocumentFragment) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType !== 1 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - // release memory in IE - div = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = attrs.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, parent, - doc = node ? node.ownerDocument || node : preferredDoc; - - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - parent = doc.defaultView; - - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer - if ( parent.addEventListener ) { - parent.addEventListener( "unload", unloadHandler, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", unloadHandler ); - } - } - - /* Support tests - ---------------------------------------------------------------------- */ - documentIsHTML = !isXML( doc ); - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); - - // ID find and filter - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - docElem.appendChild( div ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowcapture^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ - if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push("~="); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibing-combinator selector` fails - if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push(".#.+[+~]"); - } - }); - - assert(function( div ) { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch (e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - // Don't keep the element (issue #299) - input[0] = null; - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context !== document && context; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is no seed and only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( div1 ) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( document.createElement("div") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - - -var rneedsContext = jQuery.expr.match.needsContext; - -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); - - - -var risSimple = /^.[^:#\[\.,]*$/; - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); - - } - - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); - - } - - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; - }); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); -}; - -jQuery.fn.extend({ - find: function( selector ) { - var i, - len = this.length, - ret = [], - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -}); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - init = jQuery.fn.init = function( selector, context ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Support: Blackberry 4.6 - // gEBID returns nodes no longer in the document (#6963) - if ( elem && elem.parentNode ) { - // Inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : - // Execute immediately if ready is not present - selector( jQuery ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; - }, - - sibling: function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - } -}); - -jQuery.fn.extend({ - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter(function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.unique( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return elem.contentDocument || jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.unique( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -}); -var rnotwhite = (/\S+/g); - - - -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // Add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // If we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); - - -// The deferred used on DOM ready -var readyList; - -jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; -}; - -jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } - } -}); - -/** - * The ready event handler and self cleanup method - */ -function completed() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); -} - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // We once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - -// Kick off the DOM ready check even if the user does not -jQuery.ready.promise(); - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - len ? fn( elems[0], key ) : emptyGet; -}; - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( owner ) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - /* jshint -W018 */ - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - -function Data() { - // Support: Android<4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty( this.cache = {}, 0, { - get: function() { - return {}; - } - }); - - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; -Data.accepts = jQuery.acceptData; - -Data.prototype = { - key: function( owner ) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if ( !Data.accepts( owner ) ) { - return 0; - } - - var descriptor = {}, - // Check if the owner object already has a cache key - unlock = owner[ this.expando ]; - - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; - - // Secure it in a non-enumerable, non-writable property - try { - descriptor[ this.expando ] = { value: unlock }; - Object.defineProperties( owner, descriptor ); - - // Support: Android<4 - // Fallback to a less secure definition - } catch ( e ) { - descriptor[ this.expando ] = unlock; - jQuery.extend( owner, descriptor ); - } - } - - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; - } - - return unlock; - }, - set: function( owner, data, value ) { - var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - // Handle: [ owner, key, value ] args - if ( typeof data === "string" ) { - cache[ data ] = value; - - // Handle: [ owner, { properties } ] args - } else { - // Fresh assignments by object are shallow copied - if ( jQuery.isEmptyObject( cache ) ) { - jQuery.extend( this.cache[ unlock ], data ); - // Otherwise, copy the properties one-by-one to the cache object - } else { - for ( prop in data ) { - cache[ prop ] = data[ prop ]; - } - } - } - return cache; - }, - get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - var cache = this.cache[ this.key( owner ) ]; - - return key === undefined ? - cache : cache[ key ]; - }, - access: function( owner, key, value ) { - var stored; - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ((key && typeof key === "string") && value === undefined) ) { - - stored = this.get( owner, key ); - - return stored !== undefined ? - stored : this.get( owner, jQuery.camelCase(key) ); - } - - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, name, camel, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - if ( key === undefined ) { - this.cache[ unlock ] = {}; - - } else { - // Support array or space separated string of keys - if ( jQuery.isArray( key ) ) { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = key.concat( key.map( jQuery.camelCase ) ); - } else { - camel = jQuery.camelCase( key ); - // Try the string as a key before any manipulation - if ( key in cache ) { - name = [ key, camel ]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = camel; - name = name in cache ? - [ name ] : ( name.match( rnotwhite ) || [] ); - } - } - - i = name.length; - while ( i-- ) { - delete cache[ name[ i ] ]; - } - } - }, - hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ owner[ this.expando ] ] || {} - ); - }, - discard: function( owner ) { - if ( owner[ this.expando ] ) { - delete this.cache[ owner[ this.expando ] ]; - } - } -}; -var data_priv = new Data(); - -var data_user = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend({ - hasData: function( elem ) { - return data_user.hasData( elem ) || data_priv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return data_user.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - data_user.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to data_priv methods, these can be deprecated. - _data: function( elem, name, data ) { - return data_priv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - data_priv.remove( elem, name ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = data_user.get( elem ); - - if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE11+ - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - data_priv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - data_user.set( this, key ); - }); - } - - return access( this, function( value ) { - var data, - camelKey = jQuery.camelCase( key ); - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - // Attempt to get data from the cache - // with the key as-is - data = data_user.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to get data from the cache - // with the key camelized - data = data_user.get( elem, camelKey ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, camelKey, undefined ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each(function() { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - var data = data_user.get( this, camelKey ); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set( this, camelKey, value ); - - // *... In the case of properties that might _actually_ - // have dashes, we need to also store a copy of that - // unchanged property. - if ( key.indexOf("-") !== -1 && data !== undefined ) { - data_user.set( this, key, value ); - } - }); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each(function() { - data_user.remove( this, key ); - }); - } -}); - - -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = data_priv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray( data ) ) { - queue = data_priv.access( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return data_priv.get( elem, key ) || data_priv.access( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - data_priv.remove( elem, [ type + "queue", key ] ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = data_priv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHidden = function( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); - }; - -var rcheckableType = (/^(?:checkbox|radio)$/i); - - - -(function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Safari<=5.1 - // Check state lost if the name is set (#11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Safari<=5.1, Android<4.2 - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE<=11+ - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; -})(); -var strundefined = typeof undefined; - - - -support.focusinBubbles = "onfocusin" in window; - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.hasData( elem ) && data_priv.get( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - data_priv.remove( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, j, ret, matched, handleObj, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or 2) have namespace(s) - // a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, matches, sel, handleObj, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: Cordova 2.5 (WebKit) (#13255) - // All events should have a target; Cordova deviceready doesn't - if ( !event.target ) { - event.target = document; - } - - // Support: Safari 6.0+, Chrome<28 - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } -}; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - // Support: Android<4.0 - src.returnValue === false ? - returnTrue : - returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && e.preventDefault ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && e.stopPropagation ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && e.stopImmediatePropagation ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -// Support: Chrome 15+ -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// Support: Firefox, Chrome, Safari -// Create "bubbling" focus and blur events -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = data_priv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = data_priv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - data_priv.remove( doc, fix ); - - } else { - data_priv.access( doc, fix, attaches ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); - - -var - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rhtml = /<|&#?\w+;/, - rnoInnerhtml = /<(?:script|style|link)/i, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /^$|\/(?:java|ecma)script/i, - rscriptTypeMasked = /^true\/(.*)/, - rcleanScript = /^\s*\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - - // Support: IE9 - option: [ 1, "" ], - - thead: [ 1, "", "
" ], - col: [ 2, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - - _default: [ 0, "", "" ] - }; - -// Support: IE9 -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: 1.x compatibility -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute("type"); - } - - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - data_priv.set( - elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) - ); - } -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( data_priv.hasData( src ) ) { - pdataOld = data_priv.access( src ); - pdataCur = data_priv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( data_user.hasData( src ) ) { - udataOld = data_user.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - data_user.set( dest, udataCur ); - } -} - -function getAll( context, tag ) { - var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : - context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : - []; - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], ret ) : - ret; -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var elem, tmp, tag, wrap, contains, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - // Support: QtWebKit, PhantomJS - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: QtWebKit, PhantomJS - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (#12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; - }, - - cleanData: function( elems ) { - var data, elem, type, key, - special = jQuery.event.special, - i = 0; - - for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( jQuery.acceptData( elem ) ) { - key = elem[ data_priv.expando ]; - - if ( key && (data = data_priv.cache[ key ]) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - if ( data_priv.cache[ key ] ) { - // Discard any remaining `private` data - delete data_priv.cache[ key ]; - } - } - } - // Discard any remaining `user` data - delete data_user.cache[ elem[ data_user.expando ] ]; - } - } -}); - -jQuery.fn.extend({ - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each(function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - }); - }, null, value, arguments.length ); - }, - - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, - - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map(function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var arg = arguments[ 0 ]; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; - - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); - } - } - } - } - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: QtWebKit - // .get() because push.apply(_, arraylike) throws - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - - -var iframe, - elemdisplay = {}; - -/** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ -// Called only from within defaultDisplay -function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optimization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; -} - -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - - // Use the already-created iframe if possible - iframe = (iframe || jQuery( "