From c3801fee7e5093405a460be2b5df19f9625baa4b Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 27 Dec 2017 11:58:00 -0800 Subject: [PATCH 001/121] 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 002/121] 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 003/121] 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 004/121] 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 005/121] 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 006/121] 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 007/121] 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 008/121] 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 009/121] 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 010/121] 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 011/121] 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 012/121] 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 013/121] 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 014/121] 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 015/121] 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 016/121] 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 017/121] 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 0145b770da39868ab0209a7cbed7aabbbf0d5ec3 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 11 Jan 2018 17:16:22 -0800 Subject: [PATCH 018/121] 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 514eea5477e5aa73a01fb2e1a610f52787a63dff Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 17 Jan 2018 17:02:10 -0800 Subject: [PATCH 019/121] 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 020/121] 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 c4359de859a0cf0e1edd112ea828f807cbb7b99e Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 19 Jan 2018 18:20:36 -0800 Subject: [PATCH 021/121] 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 6b0b17ff633f72bb16171da4f34ad9f672ecefec Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 26 Jan 2018 17:57:20 +0100 Subject: [PATCH 022/121] 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 0324f41565c77acdda2d326560aeba2a2f260498 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 29 Jan 2018 17:23:35 +0100 Subject: [PATCH 023/121] 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 9ee715364185b913bf928864867278a11ac35af0 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 29 Jan 2018 16:53:02 -0800 Subject: [PATCH 024/121] 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 025/121] 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 026/121] 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 f344e44d26bf8d307a00593e34481bd25954bebf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 31 Jan 2018 10:19:17 +0100 Subject: [PATCH 027/121] Switched to a simpler manual fixed/slope based shadow bias system. Automatic stuff fail most of the time --- libraries/render-utils/src/LightStage.cpp | 22 +------ libraries/render-utils/src/LightStage.h | 4 +- .../render-utils/src/RenderShadowTask.cpp | 8 +-- libraries/render-utils/src/RenderShadowTask.h | 9 ++- libraries/render-utils/src/Shadow.slh | 18 ++--- libraries/render-utils/src/ShadowCore.slh | 13 +--- libraries/render-utils/src/Shadows_shared.slh | 4 +- .../developer/utilities/render/debugShadow.js | 4 +- scripts/developer/utilities/render/shadow.qml | 65 +++++++++++++++---- 9 files changed, 78 insertions(+), 69 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index e06d24f1b1..259d0dd665 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -70,18 +70,6 @@ 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 }, @@ -222,17 +210,14 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, 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++; } } void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float nearDepth, float farDepth, float baseBias) { + float nearDepth, float farDepth, float fixedBias, float slopeBias) { assert(nearDepth < farDepth); assert(cascadeIndex < _cascades.size()); @@ -283,8 +268,8 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co auto& schema = _schemaBuffer.edit(); auto& schemaCascade = schema.cascades[cascadeIndex]; schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); - schemaCascade.fixedBias = baseBias; - schema.updateCascade(cascadeIndex, *cascade._frustum); + schemaCascade.fixedBias = fixedBias; + schemaCascade.slopeBias = slopeBias; } void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { @@ -298,7 +283,6 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View 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 922ec5eb4a..9812426fa6 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 baseBias = 0.005f); + float nearDepth = 1.0f, float farDepth = 1000.0f, float fixedBias = 0.005f, float slopeBias = 0.005f); void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } @@ -110,8 +110,6 @@ public: Schema(); - void updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum); - }; UniformBufferView _schemaBuffer = nullptr; }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 2172dda3e3..2b5ab09c06 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -256,9 +256,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { } void RenderShadowCascadeSetup::configure(const Config& configuration) { - // I'm not very proud of this empirical adjustment - auto cascadeBias = configuration.bias * powf(1.1f, _cascadeIndex); - _baseBias = cascadeBias * cascadeBias * 0.01f; + _fixedBias = configuration.fixedBias * configuration.fixedBias * configuration.fixedBias * 0.004f; + _slopeBias = configuration.slopeBias * configuration.slopeBias * configuration.slopeBias * 0.01f; } void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { @@ -274,7 +273,8 @@ 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, _baseBias); + globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, + _fixedBias, _slopeBias); // 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 c4f0c65bfc..7e5655b375 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -64,10 +64,12 @@ public: class RenderShadowCascadeSetupConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float bias MEMBER bias NOTIFY dirty) + Q_PROPERTY(float fixedBias MEMBER fixedBias NOTIFY dirty) + Q_PROPERTY(float slopeBias MEMBER slopeBias NOTIFY dirty) public: - float bias{ 0.25f }; + float fixedBias{ 0.15f }; + float slopeBias{ 0.55f }; signals: void dirty(); @@ -86,7 +88,8 @@ public: private: unsigned int _cascadeIndex; - float _baseBias{ 0.1f }; + float _fixedBias{ 0.1f }; + float _slopeBias{ 0.1f }; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index c11f5fa6a7..6575e68090 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -83,19 +83,12 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias, - vec3 worldPosition, vec3 worldLightDir) { +float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) { 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); + float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL; return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } @@ -106,12 +99,11 @@ float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDe 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 = 1.0/(ndotl*ndotl)-1.0; + float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0, 1); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias, worldPosition.xyz, worldLightDir); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], oneMinusNdotL); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias, worldPosition.xyz, worldLightDir); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], oneMinusNdotL); } 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 2d48e16ef4..782e2bc2b8 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -37,21 +37,14 @@ float getShadowScale() { return shadow.invMapSize; } -float getShadowBias(int cascadeIndex) { +float getShadowFixedBias(int cascadeIndex) { return shadow.cascades[cascadeIndex].fixedBias; } -vec3 getShadowFrustumPosition(int cascadeIndex) { - return shadow.cascades[cascadeIndex].frustumPosition; +float getShadowSlopeBias(int cascadeIndex) { + return shadow.cascades[cascadeIndex].slopeBias; } -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 vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index def6b1b4d4..0d49fc037e 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -11,10 +11,8 @@ struct ShadowTransform { MAT4 reprojection; - VEC3 frustumPosition; float fixedBias; - float adaptiveBiasUnitScale; - float adaptiveBiasTransformScale; + float slopeBias; float _padding1; float _padding2; }; diff --git a/scripts/developer/utilities/render/debugShadow.js b/scripts/developer/utilities/render/debugShadow.js index a0d2142258..1f1d00e6b4 100644 --- a/scripts/developer/utilities/render/debugShadow.js +++ b/scripts/developer/utilities/render/debugShadow.js @@ -14,7 +14,7 @@ var qml = Script.resolvePath('shadow.qml'); var window = new OverlayWindow({ title: 'Shadow Debug', source: qml, - width: 200, - height: 90 + width: 250, + height: 300 }); window.closed.connect(function() { Script.stop(); }); \ No newline at end of file diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 32405a5260..a077ab9f50 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -36,6 +36,14 @@ Column { shadow1Config.enabled = false; shadow2Config.enabled = false; shadow3Config.enabled = false; + shadow0Config.isFrozen = false; + shadow1Config.isFrozen = false; + shadow2Config.isFrozen = false; + shadow3Config.isFrozen = false; + shadow0BoundConfig.isFrozen = false; + shadow1BoundConfig.isFrozen = false; + shadow2BoundConfig.isFrozen = false; + shadow3BoundConfig.isFrozen = false; } CheckBox { @@ -72,35 +80,68 @@ Column { } } ConfigSlider { - label: qsTr("Cascade 0 bias") + label: qsTr("Cascade 0 fixed bias") integral: false config: Render.getConfig("RenderMainView.ShadowCascadeSetup0") - property: "bias" + property: "fixedBias" max: 1.0 - min: 0.01 + min: 0.0 } ConfigSlider { - label: qsTr("Cascade 1 bias") + label: qsTr("Cascade 1 fixed bias") integral: false config: Render.getConfig("RenderMainView.ShadowCascadeSetup1") - property: "bias" + property: "fixedBias" max: 1.0 - min: 0.01 + min: 0.0 } ConfigSlider { - label: qsTr("Cascade 2 bias") + label: qsTr("Cascade 2 fixed bias") integral: false config: Render.getConfig("RenderMainView.ShadowCascadeSetup2") - property: "bias" + property: "fixedBias" max: 1.0 - min: 0.01 + min: 0.0 } ConfigSlider { - label: qsTr("Cascade 3 bias") + label: qsTr("Cascade 3 fixed bias") integral: false config: Render.getConfig("RenderMainView.ShadowCascadeSetup3") - property: "bias" + property: "fixedBias" max: 1.0 - min: 0.01 + min: 0.0 + } + + ConfigSlider { + label: qsTr("Cascade 0 slope bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup0") + property: "slopeBias" + max: 1.0 + min: 0.0 + } + ConfigSlider { + label: qsTr("Cascade 1 slope bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup1") + property: "slopeBias" + max: 1.0 + min: 0.0 + } + ConfigSlider { + label: qsTr("Cascade 2 slope bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup2") + property: "slopeBias" + max: 1.0 + min: 0.0 + } + ConfigSlider { + label: qsTr("Cascade 3 slope bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup3") + property: "slopeBias" + max: 1.0 + min: 0.0 } } From 3fa2babec2ea97178ae2030982459f6b511366b7 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 31 Jan 2018 11:55:46 +0100 Subject: [PATCH 028/121] Moved cascade frustum pre-computation to single ShadowSetup job --- .../render-utils/src/RenderShadowTask.cpp | 75 ++++++++++++++++--- libraries/render-utils/src/RenderShadowTask.h | 61 ++++++++++----- scripts/developer/utilities/render/shadow.qml | 41 +++++----- 3 files changed, 128 insertions(+), 49 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 2b5ab09c06..b83911582c 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -213,7 +213,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - task.addJob("ShadowSetup"); + const auto coarseFrustum = task.addJob("ShadowSetup"); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; @@ -243,7 +243,31 @@ void RenderShadowTask::configure(const Config& configuration) { // Task::configure(configuration); } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { +RenderShadowSetup::RenderShadowSetup() : + _coarseShadowFrustum{ std::make_shared() } { + +} + +void RenderShadowSetup::configure(const Config& configuration) { + setConstantBias(0, configuration.constantBias0); + setConstantBias(1, configuration.constantBias1); + setConstantBias(2, configuration.constantBias2); + setConstantBias(3, configuration.constantBias3); + setSlopeBias(0, configuration.slopeBias0); + setSlopeBias(1, configuration.slopeBias1); + setSlopeBias(2, configuration.slopeBias2); + setSlopeBias(3, configuration.slopeBias3); +} + +void RenderShadowSetup::setConstantBias(int cascadeIndex, float value) { + _bias[cascadeIndex]._constant = value * value * value * 0.004f; +} + +void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) { + _bias[cascadeIndex]._slope = value * value * value * 0.01f; +} + +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); // Cache old render args @@ -252,12 +276,46 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); - } -} + auto firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); + unsigned int cascadeIndex; + _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); + _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); -void RenderShadowCascadeSetup::configure(const Config& configuration) { - _fixedBias = configuration.fixedBias * configuration.fixedBias * configuration.fixedBias * 0.004f; - _slopeBias = configuration.slopeBias * configuration.slopeBias * configuration.slopeBias * 0.01f; + // Adjust each cascade frustum + for (cascadeIndex = 0; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { + auto& bias = _bias[cascadeIndex]; + globalShadow->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(), + SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, + bias._constant, bias._slope); + } + // Now adjust coarse frustum bounds + auto left = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight()); + auto right = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight()); + auto top = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp()); + auto bottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp()); + auto near = firstCascadeFrustum->getNearClip(); + auto far = firstCascadeFrustum->getFarClip(); + for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { + auto cascadeLeft = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight()); + auto cascadeRight = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight()); + auto cascadeTop = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp()); + auto cascadeBottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp()); + auto cascadeNear = firstCascadeFrustum->getNearClip(); + auto cascadeFar = firstCascadeFrustum->getFarClip(); + left = glm::min(left, cascadeLeft); + right = glm::max(right, cascadeRight); + bottom = glm::min(bottom, cascadeBottom); + top = glm::max(top, cascadeTop); + near = glm::min(near, cascadeNear); + far = glm::max(far, cascadeFar); + } + _coarseShadowFrustum->setProjection(glm::ortho(left, right, bottom, top, near, far)); + _coarseShadowFrustum->calculate(); + + output = _coarseShadowFrustum; + } else { + output = nullptr; + } } void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { @@ -273,9 +331,6 @@ 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, - _fixedBias, _slopeBias); - // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 7e5655b375..f488c46b8e 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -17,6 +17,8 @@ #include +#include "Shadows_shared.slh" + class ViewFrustum; class RenderShadowMap { @@ -53,43 +55,64 @@ public: void configure(const Config& configuration); }; -class RenderShadowSetup { -public: - using JobModel = render::Job::Model; - - RenderShadowSetup() {} - void run(const render::RenderContextPointer& renderContext); - -}; - -class RenderShadowCascadeSetupConfig : public render::Job::Config { +class RenderShadowSetupConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float fixedBias MEMBER fixedBias NOTIFY dirty) - Q_PROPERTY(float slopeBias MEMBER slopeBias NOTIFY dirty) + Q_PROPERTY(float constantBias0 MEMBER constantBias0 NOTIFY dirty) + Q_PROPERTY(float constantBias1 MEMBER constantBias1 NOTIFY dirty) + Q_PROPERTY(float constantBias2 MEMBER constantBias2 NOTIFY dirty) + Q_PROPERTY(float constantBias3 MEMBER constantBias3 NOTIFY dirty) + Q_PROPERTY(float slopeBias0 MEMBER slopeBias0 NOTIFY dirty) + Q_PROPERTY(float slopeBias1 MEMBER slopeBias1 NOTIFY dirty) + Q_PROPERTY(float slopeBias2 MEMBER slopeBias2 NOTIFY dirty) + Q_PROPERTY(float slopeBias3 MEMBER slopeBias3 NOTIFY dirty) public: - float fixedBias{ 0.15f }; - float slopeBias{ 0.55f }; + float constantBias0{ 0.15f }; + float constantBias1{ 0.15f }; + float constantBias2{ 0.15f }; + float constantBias3{ 0.15f }; + float slopeBias0{ 0.55f }; + float slopeBias1{ 0.55f }; + float slopeBias2{ 0.55f }; + float slopeBias3{ 0.55f }; signals: void dirty(); }; +class RenderShadowSetup { +public: + using Output = ViewFrustumPointer; + using Config = RenderShadowSetupConfig; + using JobModel = render::Job::ModelO; + + RenderShadowSetup(); + void configure(const Config& configuration); + void run(const render::RenderContextPointer& renderContext, Output& output); + +private: + + ViewFrustumPointer _coarseShadowFrustum; + struct { + float _constant; + float _slope; + } _bias[SHADOW_CASCADE_MAX_COUNT]; + + void setConstantBias(int cascadeIndex, float value); + void setSlopeBias(int cascadeIndex, float value); +}; + class RenderShadowCascadeSetup { public: using Outputs = render::VaryingSet3; - using Config = RenderShadowCascadeSetupConfig; - using JobModel = render::Job::ModelO; + 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 _fixedBias{ 0.1f }; - float _slopeBias{ 0.1f }; }; class RenderShadowCascadeTeardown { diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index a077ab9f50..3400dcd847 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -18,6 +18,7 @@ Column { id: root spacing: 8 property var viewConfig: Render.getConfig("RenderMainView.DrawViewFrustum"); + property var shadowConfig : Render.getConfig("RenderMainView.ShadowSetup"); property var shadow0Config: Render.getConfig("RenderMainView.DrawShadowFrustum0"); property var shadow1Config: Render.getConfig("RenderMainView.DrawShadowFrustum1"); property var shadow2Config: Render.getConfig("RenderMainView.DrawShadowFrustum2"); @@ -80,34 +81,34 @@ Column { } } ConfigSlider { - label: qsTr("Cascade 0 fixed bias") + label: qsTr("Cascade 0 constant bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup0") - property: "fixedBias" + config: shadowConfig + property: "constantBias0" max: 1.0 min: 0.0 } ConfigSlider { - label: qsTr("Cascade 1 fixed bias") + label: qsTr("Cascade 1 constant bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup1") - property: "fixedBias" + config: shadowConfig + property: "constantBias1" max: 1.0 min: 0.0 } ConfigSlider { - label: qsTr("Cascade 2 fixed bias") + label: qsTr("Cascade 2 constant bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup2") - property: "fixedBias" + config: shadowConfig + property: "constantBias2" max: 1.0 min: 0.0 } ConfigSlider { - label: qsTr("Cascade 3 fixed bias") + label: qsTr("Cascade 3 constant bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup3") - property: "fixedBias" + config: shadowConfig + property: "constantBias3" max: 1.0 min: 0.0 } @@ -115,32 +116,32 @@ Column { ConfigSlider { label: qsTr("Cascade 0 slope bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup0") - property: "slopeBias" + config: shadowConfig + property: "slopeBias0" max: 1.0 min: 0.0 } ConfigSlider { label: qsTr("Cascade 1 slope bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup1") - property: "slopeBias" + config: shadowConfig + property: "slopeBias1" max: 1.0 min: 0.0 } ConfigSlider { label: qsTr("Cascade 2 slope bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup2") - property: "slopeBias" + config: shadowConfig + property: "slopeBias2" max: 1.0 min: 0.0 } ConfigSlider { label: qsTr("Cascade 3 slope bias") integral: false - config: Render.getConfig("RenderMainView.ShadowCascadeSetup3") - property: "slopeBias" + config: shadowConfig + property: "slopeBias3" max: 1.0 min: 0.0 } From d422545c78a999041f956e02a5d2e74203e79493 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 31 Jan 2018 17:13:06 +0100 Subject: [PATCH 029/121] Changed shadow task to do a single octree query as well as pipeline/depth sort for all cascades. Still issue with disapearing objects from shadow map with viewpoint --- .../render-utils/src/RenderShadowTask.cpp | 105 +++++---- libraries/render-utils/src/RenderShadowTask.h | 17 +- libraries/render-utils/src/RenderViewTask.cpp | 9 +- libraries/render/src/render/CullTask.cpp | 203 ++++++++++++++---- libraries/render/src/render/CullTask.h | 40 +++- .../src/render/RenderFetchCullSortTask.cpp | 6 +- 6 files changed, 273 insertions(+), 107 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b83911582c..c9df67dad1 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -213,28 +213,34 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto coarseFrustum = task.addJob("ShadowSetup"); + const auto setupOutput = task.addJob("ShadowSetup"); + // Fetch and cull the items from the scene + static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + const auto fetchInput = render::Varying(shadowCasterFilter); + const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); + const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying(); + const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs); + + // Sort + const auto sortedPipelines = task.addJob("PipelineSortShadow", shadowItems); + const auto sortedShapes = task.addJob("DepthSortShadow", sortedPipelines, true); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); - const auto setupOutput = task.addJob(jobName, i); - const auto shadowFilter = setupOutput.getN(1); + const auto shadowFilter = task.addJob(jobName, i); - // CPU jobs: - // Fetch and cull the items from the scene - const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); - const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying(); - const auto culledShadowSelection = task.addJob("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW); - - // Sort - const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); + // CPU jobs: finer grained culling + const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter).asVarying(); + const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, cullFunctor, RenderDetails::SHADOW); // GPU jobs: Render to shadow map - task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowCascadeTeardown", setupOutput); + sprintf(jobName, "RenderShadowMap%d", i); + task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i); + task.addJob("ShadowCascadeTeardown", shadowFilter); } + + task.addJob("ShadowTeardown", setupOutput); } void RenderShadowTask::configure(const Config& configuration) { @@ -267,16 +273,19 @@ void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) { _bias[cascadeIndex]._slope = value * value * value * 0.01f; } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); // Cache old render args RenderArgs* args = renderContext->args; + output.edit0() = args->_renderMode; + output.edit1() = args->_sizeScale; + const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); - auto firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); + auto& firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); unsigned int cascadeIndex; _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); @@ -296,12 +305,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O auto near = firstCascadeFrustum->getNearClip(); auto far = firstCascadeFrustum->getFarClip(); for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { - auto cascadeLeft = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight()); - auto cascadeRight = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight()); - auto cascadeTop = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp()); - auto cascadeBottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp()); - auto cascadeNear = firstCascadeFrustum->getNearClip(); - auto cascadeFar = firstCascadeFrustum->getFarClip(); + auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum(); + auto cascadeLeft = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getRight()); + auto cascadeRight = glm::dot(cascadeFrustum->getFarTopRight(), cascadeFrustum->getRight()); + auto cascadeTop = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getUp()); + auto cascadeBottom = glm::dot(cascadeFrustum->getFarBottomRight(), cascadeFrustum->getUp()); + auto cascadeNear = cascadeFrustum->getNearClip(); + auto cascadeFar = cascadeFrustum->getFarClip(); left = glm::min(left, cascadeLeft); right = glm::max(right, cascadeRight); bottom = glm::min(bottom, cascadeBottom); @@ -312,9 +322,14 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O _coarseShadowFrustum->setProjection(glm::ortho(left, right, bottom, top, near, far)); _coarseShadowFrustum->calculate(); - output = _coarseShadowFrustum; - } else { - output = nullptr; + // Push frustum for further culling and selection + args->pushViewFrustum(*_coarseShadowFrustum); + + args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; + if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { + // Set to ridiculously high amount to prevent solid angle culling in octree selection + args->_sizeScale = 1e16f; + } } } @@ -324,37 +339,41 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Cache old render args RenderArgs* args = renderContext->args; - 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 = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); // Set the keylight render args - args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); - args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; - if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { - const float shadowSizeScale = 1e16f; - // Set the size scale to a ridiculously high value to prevent small object culling which assumes - // the view frustum is a perspective projection. But this isn't the case for the sun which - // is an orthographic projection. - args->_sizeScale = shadowSizeScale; - } - + auto& cascade = globalShadow->getCascade(_cascadeIndex); + auto& cascadeFrustum = cascade.getFrustum(); + args->pushViewFrustum(*cascadeFrustum); + // Set the cull threshold to 2 shadow texels. + auto texelSize = glm::max(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x; + texelSize *= 2.0f; + // SizeScale is used in the shadow cull function defined ine RenderViewTask + args->_sizeScale = texelSize * texelSize; } else { - output.edit1() = ItemFilter::Builder::nothing(); + output = ItemFilter::Builder::nothing(); } } void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { RenderArgs* args = renderContext->args; - if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) { + if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.selectsNothing()) { + args->popViewFrustum(); + } + assert(args->hasViewFrustum()); +} + +void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { + RenderArgs* args = renderContext->args; + + if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) { args->popViewFrustum(); } assert(args->hasViewFrustum()); // Reset the render args args->_renderMode = input.get0(); - args->_sizeScale = input.get2(); -}; + args->_sizeScale = input.get1(); +} diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index f488c46b8e..1736d07fd5 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -82,13 +82,13 @@ signals: class RenderShadowSetup { public: - using Output = ViewFrustumPointer; + using Outputs = render::VaryingSet2; using Config = RenderShadowSetupConfig; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::ModelO; RenderShadowSetup(); void configure(const Config& configuration); - void run(const render::RenderContextPointer& renderContext, Output& output); + void run(const render::RenderContextPointer& renderContext, Outputs& output); private: @@ -104,7 +104,7 @@ private: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet3; + using Outputs = render::ItemFilter; using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} @@ -117,9 +117,16 @@ private: class RenderShadowCascadeTeardown { public: - using Input = RenderShadowCascadeSetup::Outputs; + using Input = render::ItemFilter; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, const Input& input); }; +class RenderShadowTeardown { +public: + using Input = RenderShadowSetup::Outputs; + using JobModel = render::Job::ModelI; + void run(const render::RenderContextPointer& renderContext, const Input& input); +}; + #endif // hifi_RenderShadowTask_h diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index dc6c66e058..c2e43582cd 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -21,13 +21,8 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: // but the cullFunctor passed is probably tailored for perspective projection and culls too much. task.addJob("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) { // Cull only objects that are too small relatively to shadow frustum - auto& frustum = args->getViewFrustum(); - auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth()); - const auto boundsRadius = bounds.getDimensions().length(); - const auto relativeBoundRadius = boundsRadius / frustumSize; - const auto threshold = 1e-3f; - return relativeBoundRadius > threshold; - return true; + const auto boundsSquareRadius = glm::dot(bounds.getDimensions(), bounds.getDimensions()); + return boundsSquareRadius > args->_sizeScale; }); const auto items = task.addJob("FetchCullSort", cullFunctor); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 70331cdb47..c6ff224560 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -19,6 +19,50 @@ using namespace render; +// Culling Frustum / solidAngle test helper class +struct Test { + CullFunctor _functor; + RenderArgs* _args; + RenderDetails::Item& _renderDetails; + glm::vec3 _eyePos; + float _squareTanAlpha; + + Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : + _functor(functor), + _args(pargs), + _renderDetails(renderDetails) { + // FIXME: Keep this code here even though we don't use it yet + /*_eyePos = _args->getViewFrustum().getPosition(); + float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); + auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees + angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree + auto tanAlpha = tan(angle); + _squareTanAlpha = (float)(tanAlpha * tanAlpha); + */ + } + + bool frustumTest(const AABox& bound) { + if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) { + _renderDetails._outOfView++; + return false; + } + return true; + } + + bool solidAngleTest(const AABox& bound) { + // FIXME: Keep this code here even though we don't use it yet + //auto eyeToPoint = bound.calcCenter() - _eyePos; + //auto boundSize = bound.getDimensions(); + //float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; + //if (test < 0.0f) { + if (!_functor(_args, bound)) { + _renderDetails._tooSmall++; + return false; + } + return true; + } +}; + void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, const ItemBounds& inItems, ItemBounds& outItems) { assert(renderContext->args); @@ -82,18 +126,20 @@ void FetchSpatialTree::configure(const Config& config) { _lodAngle = config.lodAngle; } -void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) { +void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection) { // start fresh outSelection.clear(); + auto& filter = inputs; + if (!filter.selectsNothing()) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; auto& scene = renderContext->_scene; - // Eventually use a frozen frustum auto queryFrustum = args->getViewFrustum(); + // Eventually use a frozen frustum if (_freezeFrustum) { if (_justFrozeFrustum) { _justFrozeFrustum = false; @@ -134,50 +180,6 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one } - // Culling Frustum / solidAngle test helper class - struct Test { - CullFunctor _functor; - RenderArgs* _args; - RenderDetails::Item& _renderDetails; - glm::vec3 _eyePos; - float _squareTanAlpha; - - Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : - _functor(functor), - _args(pargs), - _renderDetails(renderDetails) - { - // FIXME: Keep this code here even though we don't use it yet - /*_eyePos = _args->getViewFrustum().getPosition(); - float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); - auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees - angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree - auto tanAlpha = tan(angle); - _squareTanAlpha = (float)(tanAlpha * tanAlpha); - */ - } - - bool frustumTest(const AABox& bound) { - if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) { - _renderDetails._outOfView++; - return false; - } - return true; - } - - bool solidAngleTest(const AABox& bound) { - // FIXME: Keep this code here even though we don't use it yet - //auto eyeToPoint = bound.calcCenter() - _eyePos; - //auto boundSize = bound.getDimensions(); - //float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; - //if (test < 0.0f) { - if (!_functor(_args, bound)) { - _renderDetails._tooSmall++; - return false; - } - return true; - } - }; Test test(_cullFunctor, args, details); // Now we have a selection of items to render @@ -309,3 +311,112 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } + +void CullShapeBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + const auto& inShapes = inputs.get0(); + const auto& filter = inputs.get1(); + auto& outShapes = outputs.edit0(); + auto& outBounds = outputs.edit1(); + + outShapes.clear(); + outBounds = AABox(); + + if (!filter.selectsNothing()) { + auto& details = args->_details.edit(_detailType); + Test test(_cullFunctor, args, details); + + for (auto& inItems : inShapes) { + auto key = inItems.first; + auto outItems = outShapes.find(key); + if (outItems == outShapes.end()) { + outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first; + outItems->second.reserve(inItems.second.size()); + } + + details._considered += (int)inItems.second.size(); + + for (auto& item : inItems.second) { + if (test.frustumTest(item.bound) && test.solidAngleTest(item.bound)) { + outItems->second.emplace_back(item); + outBounds += item.bound; + } + } + + details._rendered += (int)outItems->second.size(); + } + + for (auto& items : outShapes) { + items.second.shrink_to_fit(); + } + } +} + +void FetchSpatialSelection::run(const RenderContextPointer& renderContext, + const Inputs& inputs, ItemBounds& outItems) { + assert(renderContext->args); + RenderArgs* args = renderContext->args; + auto& scene = renderContext->_scene; + auto& inSelection = inputs.get0(); + + // Now we have a selection of items to render + outItems.clear(); + outItems.reserve(inSelection.numItems()); + + const auto filter = inputs.get1(); + if (!filter.selectsNothing()) { + // Now get the bound, and + // filter individually against the _filter + + // inside & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // inside & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + } +} diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 486c4f4cdf..a140a86aee 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -55,12 +55,13 @@ namespace render { float _lodAngle; public: using Config = FetchSpatialTreeConfig; - using JobModel = Job::ModelIO; + using Inputs = ItemFilter; + using JobModel = Job::ModelIO; FetchSpatialTree() {} void configure(const Config& config); - void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection); + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection); }; class CullSpatialSelectionConfig : public Job::Config { @@ -96,7 +97,8 @@ namespace render { _detailType(type) {} CullSpatialSelection(CullFunctor cullFunctor) : - _cullFunctor{ cullFunctor } {} + _cullFunctor{ cullFunctor } { + } CullFunctor _cullFunctor; RenderDetails::Type _detailType{ RenderDetails::OTHER }; @@ -105,6 +107,38 @@ namespace render { void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; + class CullShapeBounds { + public: + using Inputs = render::VaryingSet2; + using Outputs = render::VaryingSet2; + using JobModel = Job::ModelIO; + + CullShapeBounds(CullFunctor cullFunctor, RenderDetails::Type type) : + _cullFunctor{ cullFunctor }, + _detailType(type) {} + + CullShapeBounds(CullFunctor cullFunctor) : + _cullFunctor{ cullFunctor } { + } + + void run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + + private: + + CullFunctor _cullFunctor; + RenderDetails::Type _detailType{ RenderDetails::OTHER }; + + }; + + class FetchSpatialSelection { + public: + using Inputs = render::VaryingSet2; + using JobModel = Job::ModelIO; + + FetchSpatialSelection() {} + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); + }; + } #endif // hifi_render_CullTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index d7294fa2bd..a1b4f079e7 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -23,9 +23,9 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // CPU jobs: // Fetch and cull the items from the scene const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto spatialFilter = render::Varying(filter); - const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); - const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); + const auto fetchInput = render::Varying(filter); + const auto spatialSelection = task.addJob("FetchSceneSelection", fetchInput); + const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, render::Varying(filter)).asVarying(); const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); // Overlays are not culled From c01790bd3e2deefe8789bfeceebff1182a0da76f Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 31 Jan 2018 18:27:15 -0800 Subject: [PATCH 030/121] punch list updates and various fixes --- .../system/libraries/entitySelectionTool.js | 342 ++++++++++++------ 1 file changed, 231 insertions(+), 111 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index fbda9e9eba..87dc089c1a 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -94,6 +94,7 @@ SelectionManager = (function() { for (var i = 0; i < entityIDs.length; i++) { var entityID = entityIDs[i]; that.selections.push(entityID); + //Selection.addToSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } that._update(true); @@ -110,8 +111,10 @@ SelectionManager = (function() { } if (idx === -1) { that.selections.push(entityID); + //Selection.addToSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } else if (toggleSelection) { that.selections.splice(idx, 1); + //Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } } @@ -122,6 +125,7 @@ SelectionManager = (function() { var idx = that.selections.indexOf(entityID); if (idx >= 0) { that.selections.splice(idx, 1); + //Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } that._update(true); }; @@ -145,9 +149,9 @@ SelectionManager = (function() { that.localPosition = properties.position; that.localRotation = properties.rotation; that.localRegistrationPoint = properties.registrationPoint; - that.worldDimensions = properties.dimensions; // properties.boundingbox.dimensions; - that.worldPosition = properties.position; - that.worldRotation = properties.rotation; + that.worldDimensions = properties.boundingBox.dimensions; + that.worldPosition = properties.boundingBox.center; + that.worldRotation = properties.boundingBox.rotation; SelectionDisplay.setSpaceMode(SPACE_LOCAL); } else { that.localRotation = null; @@ -225,39 +229,43 @@ function getRelativeCenterPosition(dimensions, registrationPoint) { SelectionDisplay = (function() { var that = {}; - 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 COLOR_GREEN = { red:44, green:142, blue:14 }; + var COLOR_BLUE = { red:0, green:147, blue:197 }; + var COLOR_RED = { red:183, green:10, blue:55 }; - var GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET = 1.35; - var GRABBER_TRANSLATE_ARROW_CYLINDER_DIMENSION_MULTIPLE = 0.05; + var GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET = 0.1; + var GRABBER_TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005; 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_TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE = 0.025; + var GRABBER_ROTATE_RINGS_CAMERA_DISTANCE_MULTIPLE = 0.15; + var GRABBER_STRETCH_SPHERE_OFFSET = 0.06; + var GRABBER_STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE = 0.01; + var GRABBER_SCALE_CUBE_OFFSET = 0.5; + var GRABBER_SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.015; 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 GRABBER_SCALE_CUBE_IDLE_COLOR = { red:106, green:106, blue:106 }; + var GRABBER_SCALE_CUBE_SELECTED_COLOR = { red:18, green:18, blue:18 }; + var GRABBER_SCALE_EDGE_COLOR = { red:87, green:87, blue:87 }; + var GRABBER_HOVER_COLOR = { red:227, green:227, blue:227 }; var SCALE_MINIMUM_DIMENSION = 0.02; var STRETCH_MINIMUM_DIMENSION = 0.001; - var STRETCH_DIRECTION_ALL_FACTOR = 15; + var STRETCH_DIRECTION_ALL_FACTOR = 25; // These are multipliers for sizing the rotation degrees display while rotating an entity 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 ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.3; + var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.09; + var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.07; var ROTATION_CTRL_SNAP_ANGLE = 22.5; var ROTATION_DEFAULT_SNAP_ANGLE = 1; var ROTATION_DEFAULT_TICK_MARKS_ANGLE = 5; + var ROTATION_RING_IDLE_INNER_RADIUS = 0.97; + var ROTATION_RING_SELECTED_INNER_RADIUS = 0.9; + var TRANSLATE_DIRECTION = { X : 0, Y : 1, @@ -307,6 +315,10 @@ SelectionDisplay = (function() { var ctrlPressed = false; + var previousHandle = null; + var previousHandleHelper = null; + var previousHandleColor; + var activeTool = null; var grabberTools = {}; @@ -339,9 +351,10 @@ SelectionDisplay = (function() { var grabberPropertiesRotateRings = { alpha: 1, - innerRadius: 0.9, + solid: true, startAt: 0, endAt: 360, + innerRadius: ROTATION_RING_IDLE_INNER_RADIUS, majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE, majorTickMarksLength: 0.1, visible: false, @@ -472,7 +485,7 @@ SelectionDisplay = (function() { var selectionBox = Overlays.addOverlay("cube", { size: 1, color: COLOR_RED, - alpha: 1, + alpha: 0, // setting to 0 alpha for now to keep this hidden vs using visible false because its used as the translate xz tool overlay solid: false, visible: false, dashed: false @@ -644,12 +657,13 @@ SelectionDisplay = (function() { pickNormal = { x:0, y:1, z:0 }; } - var rotation = SelectionManager.worldRotation; + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; pickNormal = Vec3.multiplyQbyV(rotation, pickNormal); lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); that.setGrabberTranslateXVisible(direction === TRANSLATE_DIRECTION.X); that.setGrabberTranslateYVisible(direction === TRANSLATE_DIRECTION.Y); @@ -696,7 +710,7 @@ SelectionDisplay = (function() { projectionVector = { x:0, y:0, z:1 }; } - var rotation = SelectionManager.worldRotation; + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); var dotVector = Vec3.dot(vector, projectionVector); @@ -924,6 +938,9 @@ SelectionDisplay = (function() { that.setGrabberStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); that.setGrabberStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); that.setGrabberClonerVisible(false); + + SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); if (stretchPanel != null) { Overlays.editOverlay(stretchPanel, { visible: true }); @@ -931,8 +948,6 @@ SelectionDisplay = (function() { if (scaleGrabber != null) { Overlays.editOverlay(scaleGrabber, { color: GRABBER_SCALE_CUBE_SELECTED_COLOR }); } - - SelectionManager.saveProperties(); }; var onEnd = function(event, reason) { @@ -1122,13 +1137,15 @@ SelectionDisplay = (function() { // FUNCTION: UPDATE ROTATION DEGREES OVERLAY function updateRotationDegreesOverlay(angleFromZero, position) { var angle = angleFromZero * (Math.PI / 180); + var cameraPosition = Camera.getPosition(); + var entityToCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); var overlayProps = { position: position, dimensions: { - x: ROTATION_DISPLAY_SIZE_X_MULTIPLIER, - y: ROTATION_DISPLAY_SIZE_Y_MULTIPLIER + x: entityToCameraDistance * ROTATION_DISPLAY_SIZE_X_MULTIPLIER, + y: entityToCameraDistance * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER }, - lineHeight: ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, + lineHeight: entityToCameraDistance * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, text: normalizeDegrees(-angleFromZero) + "°" }; Overlays.editOverlay(rotationDegreesDisplay, overlayProps); @@ -1173,6 +1190,7 @@ SelectionDisplay = (function() { mode: mode, onBegin: function(event, pickRay, pickResult) { SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); that.setGrabberTranslateVisible(false); that.setGrabberRotatePitchVisible(direction === ROTATE_DIRECTION.PITCH); @@ -1196,9 +1214,13 @@ SelectionDisplay = (function() { selectedGrabber = grabberRotateRollRing; } - Overlays.editOverlay(selectedGrabber, { hasTickMarks: true }); + Overlays.editOverlay(selectedGrabber, { + hasTickMarks: true, + solid: false, + innerRadius: ROTATION_RING_SELECTED_INNER_RADIUS + }); - var rotation = SelectionManager.worldRotation; + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; rotationNormal = Vec3.multiplyQbyV(rotation, rotationNormal); var rotCenter = SelectionManager.worldPosition; @@ -1221,12 +1243,16 @@ SelectionDisplay = (function() { var rotCenterToZero = Vec3.subtract(rotZero, rotCenter); var rotCenterToZeroLength = Vec3.length(rotCenterToZero); - rotDegreePos = Vec3.sum(rotCenter, Vec3.multiply(Vec3.normalize(rotCenterToZero), rotCenterToZeroLength * 1.2)); + rotDegreePos = Vec3.sum(rotCenter, Vec3.multiply(Vec3.normalize(rotCenterToZero), rotCenterToZeroLength * 1.75)); updateRotationDegreesOverlay(0, rotDegreePos); }, onEnd: function(event, reason) { Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); - Overlays.editOverlay(selectedGrabber, { hasTickMarks: false }); + Overlays.editOverlay(selectedGrabber, { + hasTickMarks: false, + solid: true, + innerRadius: ROTATION_RING_IDLE_INNER_RADIUS + }); Overlays.editOverlay(grabberRotateCurrentRing, { visible: false }); pushCommandForSelections(); }, @@ -1266,6 +1292,7 @@ SelectionDisplay = (function() { if (direction === ROTATE_DIRECTION.YAW) { Overlays.editOverlay(grabberRotateCurrentRing, { rotation: worldRotationZ }); } + } } }); @@ -1307,7 +1334,6 @@ SelectionDisplay = (function() { }; that.highlightSelectable = function(entityID) { - var properties = Entities.getEntityProperties(entityID); }; that.unhighlightSelectable = function(entityID) { @@ -1370,118 +1396,119 @@ SelectionDisplay = (function() { } if (SelectionManager.hasSelection()) { - var worldPosition = SelectionManager.worldPosition; - var worldRotation = SelectionManager.worldRotation; - var worldRotationInverse = Quat.inverse(worldRotation); - var worldDimensions = SelectionManager.worldDimensions; + var position = SelectionManager.worldPosition; + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; + var dimensions = spaceMode === SPACE_LOCAL ? SelectionManager.localDimensions : SelectionManager.worldDimensions; + var rotationInverse = Quat.inverse(rotation); - var worldDimensionsX = worldDimensions.x; - var worldDimensionsY = worldDimensions.y; - var worldDimensionsZ = worldDimensions.z; - var dimensionAverage = (worldDimensionsX + worldDimensionsY + worldDimensionsZ) / 3; + var cameraPosition = Camera.getPosition(); + var entityToCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); - worldRotationX = Quat.multiply(worldRotation, localRotationX); + rotationX = Quat.multiply(rotation, localRotationX); + worldRotationX = rotationX; var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); - worldRotationY = Quat.multiply(worldRotation, localRotationY); + rotationY = Quat.multiply(rotation, localRotationY); + worldRotationY = rotationY; var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); - worldRotationZ = Quat.multiply(worldRotation, localRotationZ); + rotationZ = Quat.multiply(rotation, localRotationZ); + worldRotationZ = rotationZ; - var arrowCylinderDimension = dimensionAverage * GRABBER_TRANSLATE_ARROW_CYLINDER_DIMENSION_MULTIPLE; + var arrowCylinderDimension = entityToCameraDistance * GRABBER_TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_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 arrowConeDimension = entityToCameraDistance * GRABBER_TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_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 })); + var cylinderXPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * entityToCameraDistance, y:0, z:0 })); Overlays.editOverlay(grabberTranslateXCylinder, { position: cylinderXPos, - rotation: worldRotationX, + rotation: rotationX, dimensions: arrowCylinderDimensions }); - var cylinderXDiff = Vec3.subtract(cylinderXPos, worldPosition); + var cylinderXDiff = Vec3.subtract(cylinderXPos, position); var coneXPos = Vec3.sum(cylinderXPos, Vec3.multiply(Vec3.normalize(cylinderXDiff), arrowCylinderDimensions.y * 0.83)); Overlays.editOverlay(grabberTranslateXCone, { position: coneXPos, - rotation: worldRotationX, + rotation: rotationX, dimensions: arrowConeDimensions }); - var cylinderYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * dimensionAverage, z:0 })); + var cylinderYPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * entityToCameraDistance, z:0 })); Overlays.editOverlay(grabberTranslateYCylinder, { position: cylinderYPos, - rotation: worldRotationY, + rotation: rotationY, dimensions: arrowCylinderDimensions }); - var cylinderYDiff = Vec3.subtract(cylinderYPos, worldPosition); + var cylinderYDiff = Vec3.subtract(cylinderYPos, position); var coneYPos = Vec3.sum(cylinderYPos, Vec3.multiply(Vec3.normalize(cylinderYDiff), arrowCylinderDimensions.y * 0.83)); Overlays.editOverlay(grabberTranslateYCone, { position: coneYPos, - rotation: worldRotationY, + rotation: rotationY, dimensions: arrowConeDimensions }); - var cylinderZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * dimensionAverage })); + var cylinderZPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * entityToCameraDistance })); Overlays.editOverlay(grabberTranslateZCylinder, { position: cylinderZPos, - rotation: worldRotationZ, + rotation: rotationZ, dimensions: arrowCylinderDimensions }); - var cylinderZDiff = Vec3.subtract(cylinderZPos, worldPosition); + var cylinderZDiff = Vec3.subtract(cylinderZPos, position); var coneZPos = Vec3.sum(cylinderZPos, Vec3.multiply(Vec3.normalize(cylinderZDiff), arrowCylinderDimensions.y * 0.83)); Overlays.editOverlay(grabberTranslateZCone, { position: coneZPos, - rotation: worldRotationZ, + rotation: rotationZ, 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 grabberScaleCubeOffsetX = GRABBER_SCALE_CUBE_OFFSET * dimensions.x; + var grabberScaleCubeOffsetY = GRABBER_SCALE_CUBE_OFFSET * dimensions.y; + var grabberScaleCubeOffsetZ = GRABBER_SCALE_CUBE_OFFSET * dimensions.z; + var scaleDimension = entityToCameraDistance * GRABBER_SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE; var scaleDimensions = { x:scaleDimension, y:scaleDimension, z:scaleDimension }; - var grabberScaleLBNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + var grabberScaleLBNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleLBNCube, { position: grabberScaleLBNCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleRBNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + var grabberScaleRBNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleRBNCube, { position: grabberScaleRBNCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleLBFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + var grabberScaleLBFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleLBFCube, { position: grabberScaleLBFCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleRBFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + var grabberScaleRBFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleRBFCube, { position: grabberScaleRBFCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleLTNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + var grabberScaleLTNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleLTNCube, { position: grabberScaleLTNCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleRTNCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + var grabberScaleRTNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleRTNCube, { position: grabberScaleRTNCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleLTFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); + var grabberScaleLTFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleLTFCube, { position: grabberScaleLTFCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); - var grabberScaleRTFCubePos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); + var grabberScaleRTFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); Overlays.editOverlay(grabberScaleRTFCube, { position: grabberScaleRTFCubePos, - rotation: worldRotation, + rotation: rotation, dimensions: scaleDimensions }); @@ -1498,82 +1525,85 @@ SelectionDisplay = (function() { Overlays.editOverlay(grabberScaleFREdge, { start: grabberScaleRTFCubePos, end: grabberScaleRBFCubePos }); Overlays.editOverlay(grabberScaleFLEdge, { start: grabberScaleLTFCubePos, end: grabberScaleLBFCubePos }); - var stretchSphereDimension = dimensionAverage * GRABBER_STRETCH_SPHERE_DIMENSION_MULTIPLE; + var stretchSphereDimension = entityToCameraDistance * GRABBER_STRETCH_SPHERE_CAMERA_DISTANCE_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 })); + var stretchXPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:GRABBER_STRETCH_SPHERE_OFFSET * entityToCameraDistance, y:0, z:0 })); Overlays.editOverlay(grabberStretchXSphere, { position: stretchXPos, dimensions: stretchSphereDimensions }); - var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleLTFCubePos); - var grabberScaleRBFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRBFCubePos); + var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleLTFCubePos); + var grabberScaleRBFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRBFCubePos); var stretchPanelXDimensions = Vec3.subtract(grabberScaleLTFCubePosRot, grabberScaleRBFCubePosRot); var tempY = Math.abs(stretchPanelXDimensions.y); stretchPanelXDimensions.x = 0.01; stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); stretchPanelXDimensions.z = tempY; + var stretchPanelXPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:dimensions.x / 2, y:0, z:0 })); Overlays.editOverlay(grabberStretchXPanel, { - position: stretchXPos, - rotation: worldRotationZ, + position: stretchPanelXPos, + rotation: rotationZ, dimensions: stretchPanelXDimensions }); - var stretchYPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:GRABBER_STRETCH_SPHERE_OFFSET * worldDimensionsY, z:0 })); + var stretchYPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:GRABBER_STRETCH_SPHERE_OFFSET * entityToCameraDistance, z:0 })); Overlays.editOverlay(grabberStretchYSphere, { position: stretchYPos, dimensions: stretchSphereDimensions }); - var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleLTNCubePos); - var grabberScaleRTNCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRTFCubePos); + var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleLTNCubePos); + var grabberScaleRTNCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRTFCubePos); var stretchPanelYDimensions = Vec3.subtract(grabberScaleLTFCubePosRot, grabberScaleRTNCubePosRot); var tempX = Math.abs(stretchPanelYDimensions.x); stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); stretchPanelYDimensions.y = 0.01; stretchPanelYDimensions.z = tempX; + var stretchPanelYPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:dimensions.y / 2, z:0 })); Overlays.editOverlay(grabberStretchYPanel, { - position: stretchYPos, - rotation: worldRotationY, + position: stretchPanelYPos, + rotation: rotationY, dimensions: stretchPanelYDimensions }); - var stretchZPos = Vec3.sum(worldPosition, Vec3.multiplyQbyV(worldRotation, { x:0, y:0, z:GRABBER_STRETCH_SPHERE_OFFSET * worldDimensionsZ })); + var stretchZPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:GRABBER_STRETCH_SPHERE_OFFSET * entityToCameraDistance })); Overlays.editOverlay(grabberStretchZSphere, { position: stretchZPos, dimensions: stretchSphereDimensions }); - var grabberScaleRTFCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRTFCubePos); - var grabberScaleRBNCubePosRot = Vec3.multiplyQbyV(worldRotationInverse, grabberScaleRBNCubePos); + var grabberScaleRTFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRTFCubePos); + var grabberScaleRBNCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRBNCubePos); var stretchPanelZDimensions = Vec3.subtract(grabberScaleRTFCubePosRot, grabberScaleRBNCubePosRot); var tempX = Math.abs(stretchPanelZDimensions.x); stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); stretchPanelZDimensions.y = tempX; stretchPanelZDimensions.z = 0.01; + var stretchPanelZPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:dimensions.z / 2 })); Overlays.editOverlay(grabberStretchZPanel, { - position: stretchZPos, - rotation: worldRotationX, + position: stretchPanelZPos, + rotation: rotationX, dimensions: stretchPanelZDimensions }); - var rotateDimension = dimensionAverage * GRABBER_ROTATE_RINGS_DIMENSION_MULTIPLE; + var rotateDimension = entityToCameraDistance * GRABBER_ROTATE_RINGS_CAMERA_DISTANCE_MULTIPLE; var rotateDimensions = { x:rotateDimension, y:rotateDimension, z:rotateDimension }; if (!isActiveTool(grabberRotatePitchRing)) { Overlays.editOverlay(grabberRotatePitchRing, { - position: SelectionManager.worldPosition, - rotation: worldRotationY, + position: position, + rotation: rotationY, dimensions: rotateDimensions, majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE }); } if (!isActiveTool(grabberRotateYawRing)) { Overlays.editOverlay(grabberRotateYawRing, { - position: SelectionManager.worldPosition, - rotation: worldRotationZ, + position: position, + rotation: rotationZ, dimensions: rotateDimensions, majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE }); } if (!isActiveTool(grabberRotateRollRing)) { Overlays.editOverlay(grabberRotateRollRing, { - position: SelectionManager.worldPosition, - rotation: worldRotationX, + position: position, + rotation: rotationX, dimensions: rotateDimensions, majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE }); @@ -1588,18 +1618,18 @@ SelectionDisplay = (function() { isActiveTool(grabberCloner) || isActiveTool(selectionBox); Overlays.editOverlay(selectionBox, { - position: worldPosition, - rotation: worldRotation, - dimensions: worldDimensions, + position: position, + rotation: rotation, + dimensions: dimensions, 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)); + var grabberClonerOffset = { x:GRABBER_CLONER_OFFSET.x * dimensions.x, y:GRABBER_CLONER_OFFSET.y * dimensions.y, z:GRABBER_CLONER_OFFSET.z * dimensions.z }; + var grabberClonerPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, grabberClonerOffset)); Overlays.editOverlay(grabberCloner, { position: grabberClonerPos, - rotation: worldRotation, - dimensions: scaleDimensions, + rotation: rotation, + dimensions: scaleDimensions }); } @@ -1615,12 +1645,14 @@ SelectionDisplay = (function() { that.setGrabberScaleVisible(!activeTool || isActiveTool(grabberScaleLBNCube) || isActiveTool(grabberScaleRBNCube) || isActiveTool(grabberScaleLBFCube) || isActiveTool(grabberScaleRBFCube) || isActiveTool(grabberScaleLTNCube) || isActiveTool(grabberScaleRTNCube) || isActiveTool(grabberScaleLTFCube) || isActiveTool(grabberScaleRTFCube) || isActiveTool(grabberStretchXSphere) || isActiveTool(grabberStretchYSphere) || isActiveTool(grabberStretchZSphere)); - that.setGrabberClonerVisible(!activeTool || isActiveTool(grabberCloner)); + //keep cloner always hidden for now since you can hold Alt to clone while dragging to translate - we may bring cloner back for HMD only later + //that.setGrabberClonerVisible(!activeTool || isActiveTool(grabberCloner)); if (wantDebug) { print("====== Update Grabbers <======="); } }; + Script.update.connect(that.updateGrabbers); that.updateActiveRotateRing = function() { var activeRotateRing = null; @@ -1756,6 +1788,7 @@ SelectionDisplay = (function() { } SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); that.setGrabberTranslateVisible(false); that.setGrabberRotateVisible(false); @@ -2059,6 +2092,33 @@ SelectionDisplay = (function() { return activeTool; }; + that.resetPreviousHandleColor = function() { + if (previousHandle != null) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor }); + previousHandle = null; + } + if (previousHandleHelper != null) { + Overlays.editOverlay(previousHandleHelper, { color: previousHandleColor }); + previousHandleHelper = null; + } + }; + + that.getHandleHelper = function(overlay) { + if (overlay === grabberTranslateXCone) { + return grabberTranslateXCylinder; + } else if (overlay === grabberTranslateXCylinder) { + return grabberTranslateXCone; + } else if (overlay === grabberTranslateYCone) { + return grabberTranslateYCylinder; + } else if (overlay === grabberTranslateYCylinder) { + return grabberTranslateYCone; + } else if (overlay === grabberTranslateZCone) { + return grabberTranslateZCylinder; + } else if (overlay === grabberTranslateZCylinder) { + return grabberTranslateZCone; + } + }; + // FUNCTION: MOUSE MOVE EVENT that.mouseMoveEvent = function(event) { var wantDebug = false; @@ -2083,6 +2143,66 @@ 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 highlightNeeded = false; + + if (result.intersects) { + switch (result.overlayID) { + case grabberTranslateXCone: + case grabberTranslateXCylinder: + case grabberRotatePitchRing: + case grabberStretchXSphere: + pickedColor = COLOR_RED; + highlightNeeded = true; + break; + case grabberTranslateYCone: + case grabberTranslateYCylinder: + case grabberRotateYawRing: + case grabberStretchYSphere: + pickedColor = COLOR_GREEN; + highlightNeeded = true; + break; + case grabberTranslateZCone: + case grabberTranslateZCylinder: + case grabberRotateRollRing: + case grabberStretchZSphere: + pickedColor = COLOR_BLUE; + highlightNeeded = true; + break; + case grabberScaleLBNCube: + case grabberScaleRBNCube: + case grabberScaleLBFCube: + case grabberScaleRBFCube: + case grabberScaleLTNCube: + case grabberScaleRTNCube: + case grabberScaleLTFCube: + case grabberScaleRTFCube: + pickedColor = GRABBER_SCALE_CUBE_IDLE_COLOR; + highlightNeeded = true; + break; + default: + that.resetPreviousHandleColor(); + break; + } + + if (highlightNeeded) { + that.resetPreviousHandleColor(); + Overlays.editOverlay(result.overlayID, { color: GRABBER_HOVER_COLOR }); + previousHandle = result.overlayID; + previousHandleHelper = that.getHandleHelper(result.overlayID); + if (previousHandleHelper != null) { + Overlays.editOverlay(previousHandleHelper, { color: GRABBER_HOVER_COLOR }); + } + previousHandleColor = pickedColor; + } + + } else { + that.resetPreviousHandleColor(); + } + if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); } From db56246cd6b1e88bd438ee5d8e4714b0948cc285 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 1 Feb 2018 17:30:56 -0800 Subject: [PATCH 031/121] more fixes, clean up, updates --- scripts/system/edit.js | 3 +- .../system/libraries/entitySelectionTool.js | 2525 +++++++++-------- 2 files changed, 1313 insertions(+), 1215 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 8dd2980ee6..4ef88fcd21 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.updateGrabbers(); + selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); // Update particle explorer @@ -1250,7 +1250,6 @@ var lastPosition = null; // Do some stuff regularly, like check for placement of various overlays Script.update.connect(function (deltaTime) { progressDialog.move(); - selectionDisplay.checkMove(); selectionDisplay.checkControllerMove(); var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); var dPosition = Vec3.distance(Camera.position, lastPosition); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 87dc089c1a..c68fb6e71c 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -19,6 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; SPACE_LOCAL = "local"; SPACE_WORLD = "world"; +HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; Script.include("./controllers.js"); @@ -58,6 +59,22 @@ SelectionManager = (function() { subscribeToUpdateMessages(); + var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 } + var editHandleOutlineStyle = { + outlineUnoccludedColor: COLOR_ORANGE_HIGHLIGHT, + outlineOccludedColor: COLOR_ORANGE_HIGHLIGHT, + fillUnoccludedColor: COLOR_ORANGE_HIGHLIGHT, + fillOccludedColor: COLOR_ORANGE_HIGHLIGHT, + outlineUnoccludedAlpha: 1, + outlineOccludedAlpha: 0, + fillUnoccludedAlpha: 0, + fillOccludedAlpha: 0, + outlineWidth: 3, + isOutlineSmooth: true + }; + //disabling this for now as it is causing rendering issues with the other handle overlays + //Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle); + that.savedProperties = {}; that.selections = []; var listeners = []; @@ -94,7 +111,7 @@ SelectionManager = (function() { for (var i = 0; i < entityIDs.length; i++) { var entityID = entityIDs[i]; that.selections.push(entityID); - //Selection.addToSelectedItemsList("contextOverlayHighlightList", "entity", entityID); + Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } that._update(true); @@ -111,10 +128,10 @@ SelectionManager = (function() { } if (idx === -1) { that.selections.push(entityID); - //Selection.addToSelectedItemsList("contextOverlayHighlightList", "entity", entityID); + Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } else if (toggleSelection) { that.selections.splice(idx, 1); - //Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); + Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } } @@ -125,7 +142,7 @@ SelectionManager = (function() { var idx = that.selections.indexOf(entityID); if (idx >= 0) { that.selections.splice(idx, 1); - //Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); + Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } that._update(true); }; @@ -149,9 +166,11 @@ SelectionManager = (function() { that.localPosition = properties.position; that.localRotation = properties.rotation; that.localRegistrationPoint = properties.registrationPoint; + that.worldDimensions = properties.boundingBox.dimensions; that.worldPosition = properties.boundingBox.center; that.worldRotation = properties.boundingBox.rotation; + SelectionDisplay.setSpaceMode(SPACE_LOCAL); } else { that.localRotation = null; @@ -163,7 +182,7 @@ SelectionManager = (function() { var brn = properties.boundingBox.brn; var tfl = properties.boundingBox.tfl; - for (var i = 1; i < that.selections.length; i++) { + for (var i = 0; i < that.selections.length; i++) { properties = Entities.getEntityProperties(that.selections[i]); var bb = properties.boundingBox; brn.x = Math.min(bb.brn.x, brn.x); @@ -213,18 +232,6 @@ function normalizeDegrees(degrees) { return degrees; } -// FUNCTION: getRelativeCenterPosition -// Return the enter position of an entity relative to it's registrationPoint -// A registration point of (0.5, 0.5, 0.5) will have an offset of (0, 0, 0) -// A registration point of (1.0, 1.0, 1.0) will have an offset of (-dimensions.x / 2, -dimensions.y / 2, -dimensions.z / 2) -function getRelativeCenterPosition(dimensions, registrationPoint) { - return { - x: -dimensions.x * (registrationPoint.x - 0.5), - y: -dimensions.y * (registrationPoint.y - 0.5), - z: -dimensions.z * (registrationPoint.z - 0.5) - }; -} - // SELECTION DISPLAY DEFINITION SelectionDisplay = (function() { var that = {}; @@ -232,39 +239,39 @@ SelectionDisplay = (function() { var COLOR_GREEN = { red:44, green:142, blue:14 }; var COLOR_BLUE = { red:0, green:147, blue:197 }; var COLOR_RED = { red:183, green:10, blue:55 }; + var COLOR_HOVER = { red:227, green:227, blue:227 }; + var COLOR_SCALE_EDGE = { red:87, green:87, blue:87 }; + var COLOR_SCALE_CUBE = { red:106, green:106, blue:106 }; + var COLOR_SCALE_CUBE_SELECTED = { red:18, green:18, blue:18 }; - var GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET = 0.1; - var GRABBER_TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005; - var GRABBER_TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE = 7.5; - var GRABBER_TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE = 0.025; - var GRABBER_ROTATE_RINGS_CAMERA_DISTANCE_MULTIPLE = 0.15; - var GRABBER_STRETCH_SPHERE_OFFSET = 0.06; - var GRABBER_STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE = 0.01; - var GRABBER_SCALE_CUBE_OFFSET = 0.5; - var GRABBER_SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.015; - var GRABBER_CLONER_OFFSET = { x:0.9, y:-0.9, z:0.9 }; + var TRANSLATE_ARROW_CYLINDER_OFFSET = 0.1; + var TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005; + var TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE = 7.5; + var TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE = 0.025; - var GRABBER_SCALE_CUBE_IDLE_COLOR = { red:106, green:106, blue:106 }; - var GRABBER_SCALE_CUBE_SELECTED_COLOR = { red:18, green:18, blue:18 }; - var GRABBER_SCALE_EDGE_COLOR = { red:87, green:87, blue:87 }; - var GRABBER_HOVER_COLOR = { red:227, green:227, blue:227 }; - - var SCALE_MINIMUM_DIMENSION = 0.02; - var STRETCH_MINIMUM_DIMENSION = 0.001; - var STRETCH_DIRECTION_ALL_FACTOR = 25; + var ROTATE_RING_CAMERA_DISTANCE_MULTIPLE = 0.15; + var ROTATE_CTRL_SNAP_ANGLE = 22.5; + var ROTATE_DEFAULT_SNAP_ANGLE = 1; + var ROTATE_DEFAULT_TICK_MARKS_ANGLE = 5; + var ROTATE_RING_IDLE_INNER_RADIUS = 0.97; + var ROTATE_RING_SELECTED_INNER_RADIUS = 0.9; // These are multipliers for sizing the rotation degrees display while rotating an entity - var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.0; - var ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.3; - var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.09; - var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.07; + var ROTATE_DISPLAY_DISTANCE_MULTIPLIER = 1.0; + var ROTATE_DISPLAY_SIZE_X_MULTIPLIER = 0.3; + var ROTATE_DISPLAY_SIZE_Y_MULTIPLIER = 0.09; + var ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.07; - var ROTATION_CTRL_SNAP_ANGLE = 22.5; - var ROTATION_DEFAULT_SNAP_ANGLE = 1; - var ROTATION_DEFAULT_TICK_MARKS_ANGLE = 5; + var STRETCH_SPHERE_OFFSET = 0.06; + var STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE = 0.01; + var STRETCH_MINIMUM_DIMENSION = 0.001; + var STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE = 2; - var ROTATION_RING_IDLE_INNER_RADIUS = 0.97; - var ROTATION_RING_SELECTED_INNER_RADIUS = 0.9; + var SCALE_CUBE_OFFSET = 0.5; + var SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.015; + var SCALE_MINIMUM_DIMENSION = 0.02; + + var CLONER_OFFSET = { x:0.9, y:-0.9, z:0.9 }; var TRANSLATE_DIRECTION = { X : 0, @@ -305,79 +312,76 @@ SelectionDisplay = (function() { getControllerWorldLocation(Controller.Standard.RightHand, true) ]; - var rotZero; + var rotationZero; var rotationNormal; - var rotDegreePos; + var rotationDegreesPosition; var worldRotationX; var worldRotationY; var worldRotationZ; - var ctrlPressed = false; - var previousHandle = null; var previousHandleHelper = null; var previousHandleColor; - var activeTool = null; - var grabberTools = {}; + var ctrlPressed = false; - var grabberPropertiesTranslateArrowCones = { + var handlePropertiesTranslateArrowCones = { shape: "Cone", solid: true, visible: false, ignoreRayIntersection: false, drawInFront: true }; - var grabberPropertiesTranslateArrowCylinders = { + var handlePropertiesTranslateArrowCylinders = { shape: "Cylinder", solid: true, visible: false, 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 handleTranslateXCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); + var handleTranslateXCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); + Overlays.editOverlay(handleTranslateXCone, { color : COLOR_RED }); + Overlays.editOverlay(handleTranslateXCylinder, { color : COLOR_RED }); + var handleTranslateYCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); + var handleTranslateYCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); + Overlays.editOverlay(handleTranslateYCone, { color : COLOR_GREEN }); + Overlays.editOverlay(handleTranslateYCylinder, { color : COLOR_GREEN }); + var handleTranslateZCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); + var handleTranslateZCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); + Overlays.editOverlay(handleTranslateZCone, { color : COLOR_BLUE }); + Overlays.editOverlay(handleTranslateZCylinder, { color : COLOR_BLUE }); - var grabberPropertiesRotateRings = { + var handlePropertiesRotateRings = { alpha: 1, solid: true, startAt: 0, endAt: 360, - innerRadius: ROTATION_RING_IDLE_INNER_RADIUS, - majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE, + innerRadius: ROTATE_RING_IDLE_INNER_RADIUS, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE, majorTickMarksLength: 0.1, visible: false, ignoreRayIntersection: false, drawInFront: true }; - var grabberRotatePitchRing = Overlays.addOverlay("circle3d", grabberPropertiesRotateRings); - Overlays.editOverlay(grabberRotatePitchRing, { + var handleRotatePitchRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); + Overlays.editOverlay(handleRotatePitchRing, { color : COLOR_RED, majorTickMarksColor: COLOR_RED, }); - var grabberRotateYawRing = Overlays.addOverlay("circle3d", grabberPropertiesRotateRings); - Overlays.editOverlay(grabberRotateYawRing, { + var handleRotateYawRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); + Overlays.editOverlay(handleRotateYawRing, { color : COLOR_GREEN, majorTickMarksColor: COLOR_GREEN, }); - var grabberRotateRollRing = Overlays.addOverlay("circle3d", grabberPropertiesRotateRings); - Overlays.editOverlay(grabberRotateRollRing, { + var handleRotateRollRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); + Overlays.editOverlay(handleRotateRollRing, { color : COLOR_BLUE, majorTickMarksColor: COLOR_BLUE, }); - var grabberRotateCurrentRing = Overlays.addOverlay("circle3d", { + var handleRotateCurrentRing = Overlays.addOverlay("circle3d", { alpha: 1, color: { red: 255, green: 99, blue: 9 }, solid: true, @@ -405,21 +409,21 @@ SelectionDisplay = (function() { leftMargin: 0 }); - var grabberPropertiesStretchSpheres = { + var handlePropertiesStretchSpheres = { shape: "Sphere", solid: true, visible: false, ignoreRayIntersection: false, drawInFront: true }; - 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 handleStretchXSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres); + Overlays.editOverlay(handleStretchXSphere, { color : COLOR_RED }); + var handleStretchYSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres); + Overlays.editOverlay(handleStretchYSphere, { color : COLOR_GREEN }); + var handleStretchZSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres); + Overlays.editOverlay(handleStretchZSphere, { color : COLOR_BLUE }); - var grabberPropertiesStretchPanel = { + var handlePropertiesStretchPanel = { shape: "Quad", alpha: 0.5, solid: true, @@ -427,52 +431,52 @@ SelectionDisplay = (function() { 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 handleStretchXPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + Overlays.editOverlay(handleStretchXPanel, { color : COLOR_RED }); + var handleStretchYPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + Overlays.editOverlay(handleStretchYPanel, { color : COLOR_GREEN }); + var handleStretchZPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + Overlays.editOverlay(handleStretchZPanel, { color : COLOR_BLUE }); - var grabberPropertiesScaleCubes = { + var handlePropertiesScaleCubes = { size: 0.025, - color: GRABBER_SCALE_CUBE_IDLE_COLOR, + color: COLOR_SCALE_CUBE, solid: true, visible: false, ignoreRayIntersection: false, drawInFront: true, borderSize: 1.4 }; - 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 handleScaleLBNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, -y, -z) + var handleScaleRBNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, -y, z) + var handleScaleLBFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, -y, -z) + var handleScaleRBFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, -y, z) + var handleScaleLTNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, y, -z) + var handleScaleRTNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, y, z) + var handleScaleLTFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, y, -z) + var handleScaleRTFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, y, z) - var grabberPropertiesScaleEdge = { - color: GRABBER_SCALE_EDGE_COLOR, + var handlePropertiesScaleEdge = { + color: COLOR_SCALE_EDGE, visible: false, 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 handleScaleTREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleTLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleTFEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleTNEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBFEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBNEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleNREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleNLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleFREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleFLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); - var grabberCloner = Overlays.addOverlay("cube", { + var handleCloner = Overlays.addOverlay("cube", { size: 0.05, color: COLOR_GREEN, solid: true, @@ -482,97 +486,107 @@ SelectionDisplay = (function() { borderSize: 1.4 }); + // setting to 0 alpha for now to keep this hidden vs using visible false + // because its used as the translate xz tool handle overlay var selectionBox = Overlays.addOverlay("cube", { size: 1, color: COLOR_RED, - alpha: 0, // setting to 0 alpha for now to keep this hidden vs using visible false because its used as the translate xz tool overlay + alpha: 0, solid: false, visible: false, dashed: false }); var allOverlays = [ - grabberTranslateXCone, - grabberTranslateXCylinder, - grabberTranslateYCone, - grabberTranslateYCylinder, - grabberTranslateZCone, - grabberTranslateZCylinder, - grabberRotatePitchRing, - grabberRotateYawRing, - grabberRotateRollRing, - grabberRotateCurrentRing, + handleTranslateXCone, + handleTranslateXCylinder, + handleTranslateYCone, + handleTranslateYCylinder, + handleTranslateZCone, + handleTranslateZCylinder, + handleRotatePitchRing, + handleRotateYawRing, + handleRotateRollRing, + handleRotateCurrentRing, rotationDegreesDisplay, - 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, - grabberCloner, + handleStretchXSphere, + handleStretchYSphere, + handleStretchZSphere, + handleStretchXPanel, + handleStretchYPanel, + handleStretchZPanel, + handleScaleLBNCube, + handleScaleRBNCube, + handleScaleLBFCube, + handleScaleRBFCube, + handleScaleLTNCube, + handleScaleRTNCube, + handleScaleLTFCube, + handleScaleRTFCube, + handleScaleTREdge, + handleScaleTLEdge, + handleScaleTFEdge, + handleScaleTNEdge, + handleScaleBREdge, + handleScaleBLEdge, + handleScaleBFEdge, + handleScaleBNEdge, + handleScaleNREdge, + handleScaleNLEdge, + handleScaleFREdge, + handleScaleFLEdge, + handleCloner, selectionBox ]; - 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[handleTranslateXCone] = "handleTranslateXCone"; + overlayNames[handleTranslateXCylinder] = "handleTranslateXCylinder"; + overlayNames[handleTranslateYCone] = "handleTranslateYCone"; + overlayNames[handleTranslateYCylinder] = "handleTranslateYCylinder"; + overlayNames[handleTranslateZCone] = "handleTranslateZCone"; + overlayNames[handleTranslateZCylinder] = "handleTranslateZCylinder"; + + overlayNames[handleRotatePitchRing] = "handleRotatePitchRing"; + overlayNames[handleRotateYawRing] = "handleRotateYawRing"; + overlayNames[handleRotateRollRing] = "handleRotateRollRing"; + overlayNames[handleRotateCurrentRing] = "handleRotateCurrentRing"; 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"; - overlayNames[grabberCloner] = "grabberCloner"; + + overlayNames[handleStretchXSphere] = "handleStretchXSphere"; + overlayNames[handleStretchYSphere] = "handleStretchYSphere"; + overlayNames[handleStretchZSphere] = "handleStretchZSphere"; + overlayNames[handleStretchXPanel] = "handleStretchXPanel"; + overlayNames[handleStretchYPanel] = "handleStretchYPanel"; + overlayNames[handleStretchZPanel] = "handleStretchZPanel"; + + overlayNames[handleScaleLBNCube] = "handleScaleLBNCube"; + overlayNames[handleScaleRBNCube] = "handleScaleRBNCube"; + overlayNames[handleScaleLBFCube] = "handleScaleLBFCube"; + overlayNames[handleScaleRBFCube] = "handleScaleRBFCube"; + overlayNames[handleScaleLTNCube] = "handleScaleLTNCube"; + overlayNames[handleScaleRTNCube] = "handleScaleRTNCube"; + overlayNames[handleScaleLTFCube] = "handleScaleLTFCube"; + overlayNames[handleScaleRTFCube] = "handleScaleRTFCube"; + + overlayNames[handleScaleTREdge] = "handleScaleTREdge"; + overlayNames[handleScaleTLEdge] = "handleScaleTLEdge"; + overlayNames[handleScaleTFEdge] = "handleScaleTFEdge"; + overlayNames[handleScaleTNEdge] = "handleScaleTNEdge"; + overlayNames[handleScaleBREdge] = "handleScaleBREdge"; + overlayNames[handleScaleBLEdge] = "handleScaleBLEdge"; + overlayNames[handleScaleBFEdge] = "handleScaleBFEdge"; + overlayNames[handleScaleBNEdge] = "handleScaleBNEdge"; + overlayNames[handleScaleNREdge] = "handleScaleNREdge"; + overlayNames[handleScaleNLEdge] = "handleScaleNLEdge"; + overlayNames[handleScaleFREdge] = "handleScaleFREdge"; + overlayNames[handleScaleFLEdge] = "handleScaleFLEdge"; + + overlayNames[handleCloner] = "handleCloner"; overlayNames[selectionBox] = "selectionBox"; + var activeTool = null; + var handleTools = {}; + // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we dont' get mousePressEvents. that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); @@ -604,8 +618,247 @@ 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 DEF(s): Intersection Check Helpers + function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { + var wantDebug = false; + if ((queryRay === undefined) || (queryRay === null)) { + if (wantDebug) { + print("testRayIntersect - EARLY EXIT -> queryRay is undefined OR null!"); + } + return null; + } + + var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludes, overlayExcludes); + + if (wantDebug) { + if (!overlayIncludes) { + print("testRayIntersect - no overlayIncludes provided."); + } + if (!overlayExcludes) { + print("testRayIntersect - no overlayExcludes provided."); + } + print("testRayIntersect - Hit: " + intersectObj.intersects); + print(" intersectObj.overlayID:" + intersectObj.overlayID + "[" + overlayNames[intersectObj.overlayID] + "]"); + print(" OverlayName: " + overlayNames[intersectObj.overlayID]); + print(" intersectObj.distance:" + intersectObj.distance); + print(" intersectObj.face:" + intersectObj.face); + Vec3.print(" intersectObj.intersection:", intersectObj.intersection); + } + + return intersectObj; + } + + // FUNCTION: MOUSE PRESS EVENT + that.mousePressEvent = function (event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MousePressEvent BEG ======================="); + } + if (!event.isLeftButton && !that.triggered) { + // EARLY EXIT-(if another mouse button than left is pressed ignore it) + return false; + } + + 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]; + for (var key in handleTools) { + if (handleTools.hasOwnProperty(key)) { + interactiveOverlays.push(key); + } + } + + // Start with unknown mode, in case no tool can handle this. + activeTool = null; + + var results = testRayIntersect(pickRay, interactiveOverlays); + if (results.intersects) { + var hitOverlayID = results.overlayID; + if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || (hitOverlayID === HMD.homeButtonID)) { + // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) + return false; + } + + entityIconOverlayManager.setIconsSelectable(SelectionManager.selections, true); + + var hitTool = handleTools[ hitOverlayID ]; + if (hitTool) { + activeTool = hitTool; + if (activeTool.onBegin) { + activeTool.onBegin(event, pickRay, results); + } else { + print("ERROR: entitySelectionTool.mousePressEvent - ActiveTool(" + activeTool.mode + ") missing onBegin"); + } + } else { + print("ERROR: entitySelectionTool.mousePressEvent - Hit unexpected object, check interactiveOverlays"); + }// End_if (hitTool) + }// End_If(results.intersects) + + if (wantDebug) { + print(" DisplayMode: " + getMode()); + print("=============== eST::MousePressEvent END ======================="); + } + + // If mode is known then we successfully handled this; + // otherwise, we're missing a tool. + return activeTool; + }; + + that.resetPreviousHandleColor = function() { + if (previousHandle != null) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor }); + previousHandle = null; + } + if (previousHandleHelper != null) { + Overlays.editOverlay(previousHandleHelper, { color: previousHandleColor }); + previousHandleHelper = null; + } + }; + + that.getHandleHelper = function(overlay) { + if (overlay === handleTranslateXCone) { + return handleTranslateXCylinder; + } else if (overlay === handleTranslateXCylinder) { + return handleTranslateXCone; + } else if (overlay === handleTranslateYCone) { + return handleTranslateYCylinder; + } else if (overlay === handleTranslateYCylinder) { + return handleTranslateYCone; + } else if (overlay === handleTranslateZCone) { + return handleTranslateZCylinder; + } else if (overlay === handleTranslateZCylinder) { + return handleTranslateZCone; + } + }; + + // FUNCTION: MOUSE MOVE EVENT + that.mouseMoveEvent = function(event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MouseMoveEvent BEG ======================="); + } + if (activeTool) { + if (wantDebug) { + print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); + } + activeTool.onMove(event); + + if (wantDebug) { + print(" Trigger SelectionManager::update"); + } + SelectionManager._update(); + + if (wantDebug) { + print("=============== eST::MouseMoveEvent END ======================="); + } + // EARLY EXIT--(Move handled via active tool) + 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 highlightNeeded = false; + + if (result.intersects) { + switch (result.overlayID) { + case handleTranslateXCone: + case handleTranslateXCylinder: + case handleRotatePitchRing: + case handleStretchXSphere: + pickedColor = COLOR_RED; + highlightNeeded = true; + break; + case handleTranslateYCone: + case handleTranslateYCylinder: + case handleRotateYawRing: + case handleStretchYSphere: + pickedColor = COLOR_GREEN; + highlightNeeded = true; + break; + case handleTranslateZCone: + case handleTranslateZCylinder: + case handleRotateRollRing: + case handleStretchZSphere: + pickedColor = COLOR_BLUE; + highlightNeeded = true; + break; + case handleScaleLBNCube: + case handleScaleRBNCube: + case handleScaleLBFCube: + case handleScaleRBFCube: + case handleScaleLTNCube: + case handleScaleRTNCube: + case handleScaleLTFCube: + case handleScaleRTFCube: + pickedColor = COLOR_SCALE_CUBE; + highlightNeeded = true; + break; + default: + that.resetPreviousHandleColor(); + break; + } + + if (highlightNeeded) { + that.resetPreviousHandleColor(); + Overlays.editOverlay(result.overlayID, { color: COLOR_HOVER }); + previousHandle = result.overlayID; + previousHandleHelper = that.getHandleHelper(result.overlayID); + if (previousHandleHelper != null) { + Overlays.editOverlay(previousHandleHelper, { color: COLOR_HOVER }); + } + previousHandleColor = pickedColor; + } + + } else { + that.resetPreviousHandleColor(); + } + + if (wantDebug) { + print("=============== eST::MouseMoveEvent END ======================="); + } + return false; + }; + + // FUNCTION: MOUSE RELEASE EVENT + that.mouseReleaseEvent = function(event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MouseReleaseEvent BEG ======================="); + } + var showHandles = false; + if (activeTool) { + if (activeTool.onEnd) { + if (wantDebug) { + print(" Triggering ActiveTool(" + activeTool.mode + ")'s onEnd"); + } + activeTool.onEnd(event); + } else if (wantDebug) { + print(" ActiveTool(" + activeTool.mode + ")'s missing onEnd"); + } + } + + showHandles = activeTool; // base on prior tool value + activeTool = null; + + // if something is selected, then reset the "original" properties for any potential next click+move operation + if (SelectionManager.hasSelection()) { + if (showHandles) { + if (wantDebug) { + print(" Triggering that.select"); + } + that.select(SelectionManager.selections[0], event); + } + } + + if (wantDebug) { + print("=============== eST::MouseReleaseEvent END ======================="); + } + }; + // Control key remains active only while key is held down - function keyReleaseEvent(key) { + that.keyReleaseEvent = function(key) { if (key.key === 16777249) { ctrlPressed = false; that.updateActiveRotateRing(); @@ -613,15 +866,33 @@ SelectionDisplay = (function() { } // Triggers notification on specific key driven events - function keyPressEvent(key) { + that.keyPressEvent = function(key) { if (key.key === 16777249) { ctrlPressed = true; that.updateActiveRotateRing(); } } - Controller.keyPressEvent.connect(keyPressEvent); - Controller.keyReleaseEvent.connect(keyReleaseEvent); + // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: + // Controller.mousePressEvent.connect(that.mousePressEvent); + // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); + Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); + Controller.keyPressEvent.connect(that.keyPressEvent); + Controller.keyReleaseEvent.connect(that.keyReleaseEvent); + + that.checkControllerMove = function() { + if (SelectionManager.hasSelection()) { + var controllerPose = getControllerWorldLocation(activeHand, true); + var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1; + if (controllerPose.valid && lastControllerPoses[hand].valid) { + if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) || + !Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) { + that.mouseMoveEvent({}); + } + } + lastControllerPoses[hand] = controllerPose; + } + }; function controllerComputePickRay() { var controllerPose = getControllerWorldLocation(activeHand, true); @@ -636,17 +907,703 @@ SelectionDisplay = (function() { function generalComputePickRay(x, y) { return controllerComputePickRay() || Camera.computePickRay(x, y); } - - function addGrabberTool(overlay, tool) { - grabberTools[overlay] = tool; + + function getDistanceToCamera(position) { + var cameraPosition = Camera.getPosition(); + var toCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); + return toCameraDistance; + } + + // @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.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); + } + } + + that.updateHandles(); + }; + + // 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 addHandleTool(overlay, tool) { + handleTools[overlay] = tool; return tool; } - function addGrabberTranslateTool(overlay, mode, direction) { + // @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 (!handleTools.hasOwnProperty(toolHandle)) { + print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + toolHandle + ". Tools should be registered via addHandleTool."); + // EARLY EXIT + return false; + } + + return (activeTool === handleTools[ toolHandle ]); + } + + // 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; + } + + if (SelectionManager.hasSelection()) { + var position = SelectionManager.worldPosition; + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; + var dimensions = spaceMode === SPACE_LOCAL ? SelectionManager.localDimensions : SelectionManager.worldDimensions; + var rotationInverse = Quat.inverse(rotation); + var toCameraDistance = getDistanceToCamera(position); + + var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); + rotationX = Quat.multiply(rotation, localRotationX); + worldRotationX = rotationX; + var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); + rotationY = Quat.multiply(rotation, localRotationY); + worldRotationY = rotationY; + var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); + rotationZ = Quat.multiply(rotation, localRotationZ); + worldRotationZ = rotationZ; + + // UPDATE TRANSLATION ARROWS + var arrowCylinderDimension = toCameraDistance * TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE; + var arrowCylinderDimensions = { + x:arrowCylinderDimension, + y:arrowCylinderDimension * TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE, + z:arrowCylinderDimension + }; + var arrowConeDimension = toCameraDistance * TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE; + var arrowConeDimensions = { x:arrowConeDimension, y:arrowConeDimension, z:arrowConeDimension }; + var cylinderXPosition = { x:TRANSLATE_ARROW_CYLINDER_OFFSET * toCameraDistance, y:0, z:0 }; + cylinderXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderXPosition)); + Overlays.editOverlay(handleTranslateXCylinder, { + position: cylinderXPosition, + rotation: rotationX, + dimensions: arrowCylinderDimensions + }); + var cylinderXDiff = Vec3.subtract(cylinderXPosition, position); + var coneXPosition = Vec3.sum(cylinderXPosition, Vec3.multiply(Vec3.normalize(cylinderXDiff), arrowCylinderDimensions.y * 0.83)); + Overlays.editOverlay(handleTranslateXCone, { + position: coneXPosition, + rotation: rotationX, + dimensions: arrowConeDimensions + }); + var cylinderYPosition = { x:0, y:TRANSLATE_ARROW_CYLINDER_OFFSET * toCameraDistance, z:0 }; + cylinderYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderYPosition)); + Overlays.editOverlay(handleTranslateYCylinder, { + position: cylinderYPosition, + rotation: rotationY, + dimensions: arrowCylinderDimensions + }); + var cylinderYDiff = Vec3.subtract(cylinderYPosition, position); + var coneYPosition = Vec3.sum(cylinderYPosition, Vec3.multiply(Vec3.normalize(cylinderYDiff), arrowCylinderDimensions.y * 0.83)); + Overlays.editOverlay(handleTranslateYCone, { + position: coneYPosition, + rotation: rotationY, + dimensions: arrowConeDimensions + }); + var cylinderZPosition = { x:0, y:0, z:TRANSLATE_ARROW_CYLINDER_OFFSET * toCameraDistance }; + cylinderZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderZPosition)); + Overlays.editOverlay(handleTranslateZCylinder, { + position: cylinderZPosition, + rotation: rotationZ, + dimensions: arrowCylinderDimensions + }); + var cylinderZDiff = Vec3.subtract(cylinderZPosition, position); + var coneZPosition = Vec3.sum(cylinderZPosition, Vec3.multiply(Vec3.normalize(cylinderZDiff), arrowCylinderDimensions.y * 0.83)); + Overlays.editOverlay(handleTranslateZCone, { + position: coneZPosition, + rotation: rotationZ, + dimensions: arrowConeDimensions + }); + + // UPDATE ROTATION RINGS + var rotateDimension = toCameraDistance * ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var rotateDimensions = { x:rotateDimension, y:rotateDimension, z:rotateDimension }; + if (!isActiveTool(handleRotatePitchRing)) { + Overlays.editOverlay(handleRotatePitchRing, { + position: position, + rotation: rotationY, + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE + }); + } + if (!isActiveTool(handleRotateYawRing)) { + Overlays.editOverlay(handleRotateYawRing, { + position: position, + rotation: rotationZ, + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE + }); + } + if (!isActiveTool(handleRotateRollRing)) { + Overlays.editOverlay(handleRotateRollRing, { + position: position, + rotation: rotationX, + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE + }); + } + Overlays.editOverlay(handleRotateCurrentRing, { dimensions: rotateDimensions }); + that.updateActiveRotateRing(); + + // UPDATE SCALE CUBES + var scaleCubeOffsetX = SCALE_CUBE_OFFSET * dimensions.x; + var scaleCubeOffsetY = SCALE_CUBE_OFFSET * dimensions.y; + var scaleCubeOffsetZ = SCALE_CUBE_OFFSET * dimensions.z; + var scaleCubeDimension = toCameraDistance * SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE; + var scaleCubeDimensions = { x:scaleCubeDimension, y:scaleCubeDimension, z:scaleCubeDimension }; + var scaleCubeRotation = spaceMode === SPACE_LOCAL ? rotation : Quat.IDENTITY; + var scaleLBNCubePosition = { x:-scaleCubeOffsetX, y:-scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBNCubePosition)); + Overlays.editOverlay(handleScaleLBNCube, { + position: scaleLBNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRBNCubePosition = { x:-scaleCubeOffsetX, y:-scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBNCubePosition)); + Overlays.editOverlay(handleScaleRBNCube, { + position: scaleRBNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleLBFCubePosition = { x:scaleCubeOffsetX, y:-scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBFCubePosition)); + Overlays.editOverlay(handleScaleLBFCube, { + position: scaleLBFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRBFCubePosition = { x:scaleCubeOffsetX, y:-scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBFCubePosition)); + Overlays.editOverlay(handleScaleRBFCube, { + position: scaleRBFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleLTNCubePosition = { x:-scaleCubeOffsetX, y:scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTNCubePosition)); + Overlays.editOverlay(handleScaleLTNCube, { + position: scaleLTNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRTNCubePosition = { x:-scaleCubeOffsetX, y:scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTNCubePosition)); + Overlays.editOverlay(handleScaleRTNCube, { + position: scaleRTNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleLTFCubePosition = { x:scaleCubeOffsetX, y:scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTFCubePosition)); + Overlays.editOverlay(handleScaleLTFCube, { + position: scaleLTFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRTFCubePosition = { x:scaleCubeOffsetX, y:scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTFCubePosition)); + Overlays.editOverlay(handleScaleRTFCube, { + position: scaleRTFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + + // UPDATE SCALE EDGES + Overlays.editOverlay(handleScaleTREdge, { start: scaleRTNCubePosition, end: scaleRTFCubePosition }); + Overlays.editOverlay(handleScaleTLEdge, { start: scaleLTNCubePosition, end: scaleLTFCubePosition }); + Overlays.editOverlay(handleScaleTFEdge, { start: scaleLTFCubePosition, end: scaleRTFCubePosition }); + Overlays.editOverlay(handleScaleTNEdge, { start: scaleLTNCubePosition, end: scaleRTNCubePosition }); + Overlays.editOverlay(handleScaleBREdge, { start: scaleRBNCubePosition, end: scaleRBFCubePosition }); + Overlays.editOverlay(handleScaleBLEdge, { start: scaleLBNCubePosition, end: scaleLBFCubePosition }); + Overlays.editOverlay(handleScaleBFEdge, { start: scaleLBFCubePosition, end: scaleRBFCubePosition }); + Overlays.editOverlay(handleScaleBNEdge, { start: scaleLBNCubePosition, end: scaleRBNCubePosition }); + Overlays.editOverlay(handleScaleNREdge, { start: scaleRTNCubePosition, end: scaleRBNCubePosition }); + Overlays.editOverlay(handleScaleNLEdge, { start: scaleLTNCubePosition, end: scaleLBNCubePosition }); + Overlays.editOverlay(handleScaleFREdge, { start: scaleRTFCubePosition, end: scaleRBFCubePosition }); + Overlays.editOverlay(handleScaleFLEdge, { start: scaleLTFCubePosition, end: scaleLBFCubePosition }); + + // UPDATE STRETCH SPHERES + var stretchSphereDimension = toCameraDistance * STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE; + var stretchSphereDimensions = { x:stretchSphereDimension, y:stretchSphereDimension, z:stretchSphereDimension }; + var stretchXPosition = { x:STRETCH_SPHERE_OFFSET * toCameraDistance, y:0, z:0 }; + stretchXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchXPosition)); + Overlays.editOverlay(handleStretchXSphere, { + position: stretchXPosition, + dimensions: stretchSphereDimensions + }); + var stretchYPosition = { x:0, y:STRETCH_SPHERE_OFFSET * toCameraDistance, z:0 }; + stretchYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchYPosition)); + Overlays.editOverlay(handleStretchYSphere, { + position: stretchYPosition, + dimensions: stretchSphereDimensions + }); + var stretchZPosition = { x:0, y:0, z:STRETCH_SPHERE_OFFSET * toCameraDistance }; + stretchZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchZPosition)); + Overlays.editOverlay(handleStretchZSphere, { + position: stretchZPosition, + dimensions: stretchSphereDimensions + }); + + // UPDATE STRETCH HIGHLIGHT PANELS + var scaleLTFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleLTFCubePosition); + var scaleRBFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRBFCubePosition); + var stretchPanelXDimensions = Vec3.subtract(scaleLTFCubePositionRotated, scaleRBFCubePositionRotated); + var tempY = Math.abs(stretchPanelXDimensions.y); + stretchPanelXDimensions.x = 0.01; + stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); + stretchPanelXDimensions.z = tempY; + var stretchPanelXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:dimensions.x / 2, y:0, z:0 })); + Overlays.editOverlay(handleStretchXPanel, { + position: stretchPanelXPosition, + rotation: rotationZ, + dimensions: stretchPanelXDimensions + }); + var scaleLTNCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleLTNCubePosition); + var scaleRTFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRTFCubePosition); + var stretchPanelYDimensions = Vec3.subtract(scaleLTNCubePositionRotated, scaleRTFCubePositionRotated); + var tempX = Math.abs(stretchPanelYDimensions.x); + stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); + stretchPanelYDimensions.y = 0.01; + stretchPanelYDimensions.z = tempX; + var stretchPanelYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:dimensions.y / 2, z:0 })); + Overlays.editOverlay(handleStretchYPanel, { + position: stretchPanelYPosition, + rotation: rotationY, + dimensions: stretchPanelYDimensions + }); + var scaleRTFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRTFCubePosition); + var scaleRBNCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRBNCubePosition); + var stretchPanelZDimensions = Vec3.subtract(scaleRTFCubePositionRotated, scaleRBNCubePositionRotated); + var tempX = Math.abs(stretchPanelZDimensions.x); + stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); + stretchPanelZDimensions.y = tempX; + stretchPanelZDimensions.z = 0.01; + var stretchPanelZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:dimensions.z / 2 })); + Overlays.editOverlay(handleStretchZPanel, { + position: stretchPanelZPosition, + rotation: rotationX, + dimensions: stretchPanelZDimensions + }); + + // UPDATE SELECTION BOX (CURRENTLY INVISIBLE WITH 0 ALPHA FOR TRANSLATE XZ TOOL) + var inModeRotate = isActiveTool(handleRotatePitchRing) || + isActiveTool(handleRotateYawRing) || + isActiveTool(handleRotateRollRing); + Overlays.editOverlay(selectionBox, { + position: position, + rotation: rotation, + dimensions: dimensions, + visible: !inModeRotate + }); + + // UPDATE CLONER (CURRENTLY HIDDEN FOR NOW) + var handleClonerOffset = { + x:CLONER_OFFSET.x * dimensions.x, + y:CLONER_OFFSET.y * dimensions.y, + z:CLONER_OFFSET.z * dimensions.z + }; + var handleClonerPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, handleClonerOffset)); + Overlays.editOverlay(handleCloner, { + position: handleClonerPos, + rotation: rotation, + dimensions: scaleCubeDimensions + }); + } + + that.setHandleTranslateXVisible(!activeTool || isActiveTool(handleTranslateXCone) || isActiveTool(handleTranslateXCylinder)); + that.setHandleTranslateYVisible(!activeTool || isActiveTool(handleTranslateYCone) || isActiveTool(handleTranslateYCylinder)); + that.setHandleTranslateZVisible(!activeTool || isActiveTool(handleTranslateZCone) || isActiveTool(handleTranslateZCylinder)); + that.setHandleRotatePitchVisible(!activeTool || isActiveTool(handleRotatePitchRing)); + that.setHandleRotateYawVisible(!activeTool || isActiveTool(handleRotateYawRing)); + that.setHandleRotateRollVisible(!activeTool || isActiveTool(handleRotateRollRing)); + + var showScaleStretch = !activeTool && SelectionManager.selections.length === 1; + that.setHandleStretchXVisible(showScaleStretch || isActiveTool(handleStretchXSphere)); + that.setHandleStretchYVisible(showScaleStretch || isActiveTool(handleStretchYSphere)); + that.setHandleStretchZVisible(showScaleStretch || isActiveTool(handleStretchZSphere)); + that.setHandleScaleCubeVisible(showScaleStretch || isActiveTool(handleScaleLBNCube) || isActiveTool(handleScaleRBNCube) || isActiveTool(handleScaleLBFCube) || isActiveTool(handleScaleRBFCube) + || isActiveTool(handleScaleLTNCube) || isActiveTool(handleScaleRTNCube) || isActiveTool(handleScaleLTFCube) || isActiveTool(handleScaleRTFCube) + || isActiveTool(handleStretchXSphere) || isActiveTool(handleStretchYSphere) || isActiveTool(handleStretchZSphere)); + that.setHandleScaleEdgeVisible(!isActiveTool(handleRotatePitchRing) && !isActiveTool(handleRotateYawRing) && !isActiveTool(handleRotateRollRing)); + + //keep cloner always hidden for now since you can hold Alt to clone while dragging to translate - we may bring cloner back for HMD only later + //that.setHandleClonerVisible(!activeTool || isActiveTool(handleCloner)); + + if (wantDebug) { + print("====== Update Handles <======="); + } + }; + Script.update.connect(that.updateHandles); + + // FUNCTION: UPDATE ACTIVE ROTATE RING + that.updateActiveRotateRing = function() { + var activeRotateRing = null; + if (isActiveTool(handleRotatePitchRing)) { + activeRotateRing = handleRotatePitchRing; + } else if (isActiveTool(handleRotateYawRing)) { + activeRotateRing = handleRotateYawRing; + } else if (isActiveTool(handleRotateRollRing)) { + activeRotateRing = handleRotateRollRing; + } + if (activeRotateRing != null) { + var tickMarksAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_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++) { + Overlays.editOverlay(allOverlays[i], { visible: isVisible }); + } + }; + + // FUNCTION: SET HANDLE TRANSLATE VISIBLE + that.setHandleTranslateVisible = function(isVisible) { + that.setHandleTranslateXVisible(isVisible); + that.setHandleTranslateYVisible(isVisible); + that.setHandleTranslateZVisible(isVisible); + }; + + that.setHandleTranslateXVisible = function(isVisible) { + Overlays.editOverlay(handleTranslateXCone, { visible: isVisible }); + Overlays.editOverlay(handleTranslateXCylinder, { visible: isVisible }); + }; + + that.setHandleTranslateYVisible = function(isVisible) { + Overlays.editOverlay(handleTranslateYCone, { visible: isVisible }); + Overlays.editOverlay(handleTranslateYCylinder, { visible: isVisible }); + }; + + that.setHandleTranslateZVisible = function(isVisible) { + Overlays.editOverlay(handleTranslateZCone, { visible: isVisible }); + Overlays.editOverlay(handleTranslateZCylinder, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE ROTATE VISIBLE + that.setHandleRotateVisible = function(isVisible) { + that.setHandleRotatePitchVisible(isVisible); + that.setHandleRotateYawVisible(isVisible); + that.setHandleRotateRollVisible(isVisible); + }; + + that.setHandleRotatePitchVisible = function(isVisible) { + Overlays.editOverlay(handleRotatePitchRing, { visible: isVisible }); + }; + + that.setHandleRotateYawVisible = function(isVisible) { + Overlays.editOverlay(handleRotateYawRing, { visible: isVisible }); + }; + + that.setHandleRotateRollVisible = function(isVisible) { + Overlays.editOverlay(handleRotateRollRing, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE STRETCH VISIBLE + that.setHandleStretchVisible = function(isVisible) { + that.setHandleStretchXVisible(isVisible); + that.setHandleStretchYVisible(isVisible); + that.setHandleStretchZVisible(isVisible); + }; + + that.setHandleStretchXVisible = function(isVisible) { + Overlays.editOverlay(handleStretchXSphere, { visible: isVisible }); + }; + + that.setHandleStretchYVisible = function(isVisible) { + Overlays.editOverlay(handleStretchYSphere, { visible: isVisible }); + }; + + that.setHandleStretchZVisible = function(isVisible) { + Overlays.editOverlay(handleStretchZSphere, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE SCALE VISIBLE + that.setHandleScaleVisible = function(isVisible) { + that.setHandleScaleCubeVisible(isVisible); + that.setHandleScaleEdgeVisible(isVisible); + }; + + that.setHandleScaleCubeVisible = function(isVisible) { + Overlays.editOverlay(handleScaleLBNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRBNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleLBFCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRBFCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleLTNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRTNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleLTFCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRTFCube, { visible: isVisible }); + }; + + that.setHandleScaleEdgeVisible = function(isVisible) { + Overlays.editOverlay(handleScaleTREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleTLEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleTFEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleTNEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBLEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBFEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBNEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleNREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleNLEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleFREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleFLEdge, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE CLONER VISIBLE + that.setHandleClonerVisible = function(isVisible) { + Overlays.editOverlay(handleCloner, { visible: isVisible }); + }; + + // TOOL DEFINITION: TRANSLATE XZ TOOL + var initialXZPick = null; + var isConstrained = false; + var constrainMajorOnly = false; + var startPosition = null; + var duplicatedEntityIDs = null; + var translateXZTool = addHandleTool(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.resetPreviousHandleColor(); + + that.setHandleTranslateVisible(false); + that.setHandleRotateVisible(false); + that.setHandleScaleCubeVisible(false); + that.setHandleStretchVisible(false); + that.setHandleClonerVisible(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(); + } + }); + + // TOOL DEFINITION: HANDLE TRANSLATE TOOL + function addHandleTranslateTool(overlay, mode, direction) { var pickNormal = null; var lastPick = null; var projectionVector = null; - addGrabberTool(overlay, { + addHandleTool(overlay, { mode: mode, onBegin: function(event, pickRay, pickResult) { if (direction === TRANSLATE_DIRECTION.X) { @@ -665,13 +1622,13 @@ SelectionDisplay = (function() { SelectionManager.saveProperties(); that.resetPreviousHandleColor(); - 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); - that.setGrabberClonerVisible(false); + that.setHandleTranslateXVisible(direction === TRANSLATE_DIRECTION.X); + that.setHandleTranslateYVisible(direction === TRANSLATE_DIRECTION.Y); + that.setHandleTranslateZVisible(direction === TRANSLATE_DIRECTION.Z); + that.setHandleRotateVisible(false); + that.setHandleStretchVisible(false); + that.setHandleScaleCubeVisible(false); + that.setHandleClonerVisible(false); // Duplicate entities if alt is pressed. This will make a // copy of the selected entities and move the _original_ entities, not @@ -747,7 +1704,8 @@ SelectionDisplay = (function() { }; }; - function makeStretchTool(stretchMode, directionEnum, directionVec, pivot, offset, stretchPanel, scaleGrabber) { + // TOOL DEFINITION: HANDLE STRETCH TOOL + function makeStretchTool(stretchMode, directionEnum, directionVec, pivot, offset, stretchPanel, scaleHandle) { var directionFor3DStretch = directionVec; var distanceFor3DStretch = 0; var DISTANCE_INFLUENCE_THRESHOLD = 1.2; @@ -931,13 +1889,13 @@ SelectionDisplay = (function() { distanceFor3DStretch = Vec3.length(Vec3.subtract(pickRayPosition3D, pickRay.origin)); } - that.setGrabberTranslateVisible(false); - that.setGrabberRotateVisible(false); - that.setGrabberScaleVisible(true); - that.setGrabberStretchXVisible(directionEnum === STRETCH_DIRECTION.X); - that.setGrabberStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); - that.setGrabberStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); - that.setGrabberClonerVisible(false); + that.setHandleTranslateVisible(false); + that.setHandleRotateVisible(false); + that.setHandleScaleCubeVisible(true); + that.setHandleStretchXVisible(directionEnum === STRETCH_DIRECTION.X); + that.setHandleStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); + that.setHandleStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); + that.setHandleClonerVisible(false); SelectionManager.saveProperties(); that.resetPreviousHandleColor(); @@ -945,8 +1903,8 @@ SelectionDisplay = (function() { if (stretchPanel != null) { Overlays.editOverlay(stretchPanel, { visible: true }); } - if (scaleGrabber != null) { - Overlays.editOverlay(scaleGrabber, { color: GRABBER_SCALE_CUBE_SELECTED_COLOR }); + if (scaleHandle != null) { + Overlays.editOverlay(scaleHandle, { color: COLOR_SCALE_CUBE_SELECTED }); } }; @@ -954,8 +1912,8 @@ SelectionDisplay = (function() { if (stretchPanel != null) { Overlays.editOverlay(stretchPanel, { visible: false }); } - if (scaleGrabber != null) { - Overlays.editOverlay(scaleGrabber, { color: GRABBER_SCALE_CUBE_IDLE_COLOR }); + if (scaleHandle != null) { + Overlays.editOverlay(scaleHandle, { color: COLOR_SCALE_CUBE }); } pushCommandForSelections(); }; @@ -1008,7 +1966,6 @@ SelectionDisplay = (function() { vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); vector = vec3Mult(mask, vector); - } vector = grid.snapToSpacing(vector); @@ -1016,7 +1973,9 @@ SelectionDisplay = (function() { var changeInDimensions = Vec3.multiply(-1, vec3Mult(localSigns, vector)); if (directionEnum === STRETCH_DIRECTION.ALL) { - changeInDimensions = Vec3.multiply(changeInDimensions, STRETCH_DIRECTION_ALL_FACTOR); + var toCameraDistance = getDistanceToCamera(position); + var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE; + changeInDimensions = Vec3.multiply(changeInDimensions, dimensionsMultiple); } var newDimensions; @@ -1083,76 +2042,74 @@ SelectionDisplay = (function() { }; } - function addGrabberStretchTool(overlay, mode, directionEnum) { - var directionVec, pivot, offset, stretchPanel; + function addHandleStretchTool(overlay, mode, directionEnum) { + var directionVec, offset, stretchPanel; if (directionEnum === STRETCH_DIRECTION.X) { - stretchPanel = grabberStretchXPanel; + stretchPanel = handleStretchXPanel; directionVec = { x:-1, y:0, z:0 }; } else if (directionEnum === STRETCH_DIRECTION.Y) { - stretchPanel = grabberStretchYPanel; + stretchPanel = handleStretchYPanel; directionVec = { x:0, y:-1, z:0 }; } else if (directionEnum === STRETCH_DIRECTION.Z) { - stretchPanel = grabberStretchZPanel + stretchPanel = handleStretchZPanel directionVec = { x:0, y:0, z:-1 }; } - pivot = directionVec; offset = Vec3.multiply(directionVec, -1); - var tool = makeStretchTool(mode, directionEnum, directionVec, pivot, offset, stretchPanel, null); - return addGrabberTool(overlay, tool); + var tool = makeStretchTool(mode, directionEnum, directionVec, directionVec, offset, stretchPanel, null); + return addHandleTool(overlay, tool); } - function addGrabberScaleTool(overlay, mode, directionEnum) { - var directionVec, pivot, offset, selectedGrabber; + // TOOL DEFINITION: HANDLE SCALE TOOL + function addHandleScaleTool(overlay, mode, directionEnum) { + var directionVec, offset, selectedHandle; if (directionEnum === SCALE_DIRECTION.LBN) { directionVec = { x:1, y:1, z:1 }; - selectedGrabber = grabberScaleLBNCube; + selectedHandle = handleScaleLBNCube; } else if (directionEnum === SCALE_DIRECTION.RBN) { directionVec = { x:1, y:1, z:-1 }; - selectedGrabber = grabberScaleRBNCube; + selectedHandle = handleScaleRBNCube; } else if (directionEnum === SCALE_DIRECTION.LBF) { directionVec = { x:-1, y:1, z:1 }; - selectedGrabber = grabberScaleLBFCube; + selectedHandle = handleScaleLBFCube; } else if (directionEnum === SCALE_DIRECTION.RBF) { directionVec = { x:-1, y:1, z:-1 }; - selectedGrabber = grabberScaleRBFCube; + selectedHandle = handleScaleRBFCube; } else if (directionEnum === SCALE_DIRECTION.LTN) { directionVec = { x:1, y:-1, z:1 }; - selectedGrabber = grabberScaleLTNCube; + selectedHandle = handleScaleLTNCube; } else if (directionEnum === SCALE_DIRECTION.RTN) { directionVec = { x:1, y:-1, z:-1 }; - selectedGrabber = grabberScaleRTNCube; + selectedHandle = handleScaleRTNCube; } else if (directionEnum === SCALE_DIRECTION.LTF) { directionVec = { x:-1, y:-1, z:1 }; - selectedGrabber = grabberScaleLTFCube; + selectedHandle = handleScaleLTFCube; } else if (directionEnum === SCALE_DIRECTION.RTF) { directionVec = { x:-1, y:-1, z:-1 }; - selectedGrabber = grabberScaleRTFCube; + selectedHandle = handleScaleRTFCube; } - pivot = directionVec; offset = Vec3.multiply(directionVec, -1); - var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVec, pivot, offset, null, selectedGrabber); - return addGrabberTool(overlay, tool); + var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVec, directionVec, offset, null, selectedHandle); + return addHandleTool(overlay, tool); } // FUNCTION: UPDATE ROTATION DEGREES OVERLAY function updateRotationDegreesOverlay(angleFromZero, position) { var angle = angleFromZero * (Math.PI / 180); - var cameraPosition = Camera.getPosition(); - var entityToCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); + var toCameraDistance = getDistanceToCamera(position); var overlayProps = { position: position, dimensions: { - x: entityToCameraDistance * ROTATION_DISPLAY_SIZE_X_MULTIPLIER, - y: entityToCameraDistance * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER + x: toCameraDistance * ROTATE_DISPLAY_SIZE_X_MULTIPLIER, + y: toCameraDistance * ROTATE_DISPLAY_SIZE_Y_MULTIPLIER }, - lineHeight: entityToCameraDistance * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, + lineHeight: toCameraDistance * ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER, text: normalizeDegrees(-angleFromZero) + "°" }; Overlays.editOverlay(rotationDegreesDisplay, overlayProps); } // FUNCTION DEF: updateSelectionsRotation - // Helper func used by rotation grabber tools + // Helper func used by rotation handle tools function updateSelectionsRotation(rotationChange) { if (!rotationChange) { print("ERROR: entitySelectionTool.updateSelectionsRotation - Invalid arg specified!!"); @@ -1173,61 +2130,76 @@ SelectionDisplay = (function() { rotation: Quat.multiply(rotationChange, initialProperties.rotation) }; + Quat.print("OldRotation ", initialProperties.rotation); + Quat.print("NewRotation ", newProperties.rotation); + if (reposition) { var dPos = Vec3.subtract(initialProperties.position, SelectionManager.worldPosition); + Vec3.print("SelectionManager.worldPosition ", SelectionManager.worldPosition); + Vec3.print("initialProperties.position ", initialProperties.position); + Vec3.print("dPos 1 ", dPos); dPos = Vec3.multiplyQbyV(rotationChange, dPos); + Vec3.print("dPos 2 ", dPos); newProperties.position = Vec3.sum(SelectionManager.worldPosition, dPos); + Vec3.print("newProperties.position ", newProperties.position); } Entities.editEntity(entityID, newProperties); } } - function addGrabberRotateTool(overlay, mode, direction) { - var selectedGrabber = null; + // TOOL DEFINITION: HANDLE ROTATION TOOL + function addHandleRotateTool(overlay, mode, direction) { + var selectedHandle = null; var worldRotation = null; - addGrabberTool(overlay, { + var rotationCenter = null; + addHandleTool(overlay, { mode: mode, onBegin: function(event, pickRay, pickResult) { + var wantDebug = false; + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onBegin) -> ======================="); + } + SelectionManager.saveProperties(); that.resetPreviousHandleColor(); - 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); - that.setGrabberClonerVisible(false); + that.setHandleTranslateVisible(false); + that.setHandleRotatePitchVisible(direction === ROTATE_DIRECTION.PITCH); + that.setHandleRotateYawVisible(direction === ROTATE_DIRECTION.YAW); + that.setHandleRotateRollVisible(direction === ROTATE_DIRECTION.ROLL); + that.setHandleStretchVisible(false); + that.setHandleScaleCubeVisible(false); + that.setHandleClonerVisible(false); if (direction === ROTATE_DIRECTION.PITCH) { rotationNormal = { x: 1, y: 0, z: 0 }; worldRotation = worldRotationY; - selectedGrabber = grabberRotatePitchRing; + selectedHandle = handleRotatePitchRing; } else if (direction === ROTATE_DIRECTION.YAW) { rotationNormal = { x: 0, y: 1, z: 0 }; worldRotation = worldRotationZ; - selectedGrabber = grabberRotateYawRing; + selectedHandle = handleRotateYawRing; } else if (direction === ROTATE_DIRECTION.ROLL) { rotationNormal = { x: 0, y: 0, z: 1 }; worldRotation = worldRotationX; - selectedGrabber = grabberRotateRollRing; + selectedHandle = handleRotateRollRing; } - Overlays.editOverlay(selectedGrabber, { + Overlays.editOverlay(selectedHandle, { hasTickMarks: true, solid: false, - innerRadius: ROTATION_RING_SELECTED_INNER_RADIUS + innerRadius: ROTATE_RING_SELECTED_INNER_RADIUS }); var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; rotationNormal = Vec3.multiplyQbyV(rotation, rotationNormal); - var rotCenter = SelectionManager.worldPosition; + rotationCenter = SelectionManager.worldPosition; Overlays.editOverlay(rotationDegreesDisplay, { visible: true }); - Overlays.editOverlay(grabberRotateCurrentRing, { - position: rotCenter, + Overlays.editOverlay(handleRotateCurrentRing, { + position: rotationCenter, rotation: worldRotation, startAt: 0, endAt: 0, @@ -1236,47 +2208,78 @@ SelectionDisplay = (function() { // editOverlays may not have committed rotation changes. // Compute zero position based on where the overlay will be eventually. - var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); + var result = rayPlaneIntersection(pickRay, rotationCenter, rotationNormal); // In case of a parallel ray, this will be null, which will cause early-out // in the onMove helper. - rotZero = result; + rotationZero = result; - var rotCenterToZero = Vec3.subtract(rotZero, rotCenter); - var rotCenterToZeroLength = Vec3.length(rotCenterToZero); - rotDegreePos = Vec3.sum(rotCenter, Vec3.multiply(Vec3.normalize(rotCenterToZero), rotCenterToZeroLength * 1.75)); - updateRotationDegreesOverlay(0, rotDegreePos); + var rotationCenterToZero = Vec3.subtract(rotationZero, rotationCenter); + var rotationCenterToZeroLength = Vec3.length(rotationCenterToZero); + rotationDegreesPosition = Vec3.sum(rotationCenter, Vec3.multiply(Vec3.normalize(rotationCenterToZero), rotationCenterToZeroLength * 1.75)); + updateRotationDegreesOverlay(0, rotationDegreesPosition); + + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onBegin) <- ======================="); + } }, onEnd: function(event, reason) { + var wantDebug = false; + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onEnd) -> ======================="); + } Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); - Overlays.editOverlay(selectedGrabber, { + Overlays.editOverlay(selectedHandle, { hasTickMarks: false, solid: true, - innerRadius: ROTATION_RING_IDLE_INNER_RADIUS + innerRadius: ROTATE_RING_IDLE_INNER_RADIUS }); - Overlays.editOverlay(grabberRotateCurrentRing, { visible: false }); + Overlays.editOverlay(handleRotateCurrentRing, { visible: false }); pushCommandForSelections(); + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onEnd) <- ======================="); + } }, onMove: function(event) { - if (!rotZero) { - print("ERROR: entitySelectionTool.handleRotationHandleOnMove - Invalid RotationZero Specified (missed rotation target plane?)"); - + if (!rotationZero) { + print("ERROR: entitySelectionTool.addHandleRotateTool.onMove - Invalid RotationZero Specified (missed rotation target plane?)"); + // EARLY EXIT return; } + + var wantDebug = true; + if (wantDebug) { + print("================== "+ getMode() + "(addHandleRotateTool onMove) -> ======================="); + Vec3.print(" rotationZero: ", rotationZero); + } + var pickRay = generalComputePickRay(event.x, event.y); - var rotCenter = SelectionManager.worldPosition; - var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); + var result = rayPlaneIntersection(pickRay, rotationCenter, rotationNormal); if (result) { - var centerToZero = Vec3.subtract(rotZero, rotCenter); - var centerToIntersect = Vec3.subtract(result, rotCenter); + var centerToZero = Vec3.subtract(rotationZero, rotationCenter); + var centerToIntersect = Vec3.subtract(result, rotationCenter); + + if (wantDebug) { + Vec3.print(" RotationNormal: ", rotationNormal); + Vec3.print(" rotationZero: ", rotationZero); + Vec3.print(" rotationCenter: ", rotationCenter); + 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 snapAngle = ctrlPressed ? ROTATION_CTRL_SNAP_ANGLE : ROTATION_DEFAULT_SNAP_ANGLE; + var snapAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_SNAP_ANGLE; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); + if (wantDebug) { + Quat.print(" rotChange: ", rotChange) + print(" angleFromZero: ", angleFromZero); + } updateSelectionsRotation(rotChange); - updateRotationDegreesOverlay(-angleFromZero, rotDegreePos); + updateRotationDegreesOverlay(-angleFromZero, rotationDegreesPosition); var startAtCurrent = 0; var endAtCurrent = angleFromZero; @@ -1284,692 +2287,26 @@ SelectionDisplay = (function() { startAtCurrent = 360 + angleFromZero; endAtCurrent = 360; } - Overlays.editOverlay(grabberRotateCurrentRing, { + Overlays.editOverlay(handleRotateCurrentRing, { 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 }); + Overlays.editOverlay(handleRotateCurrentRing, { rotation: worldRotationZ }); } + } + if (wantDebug) { + print("================== "+ getMode() + "(addHandleRotateTool onMove) <- ======================="); } } }); } - // @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) { - }; - - 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); - } - } - - that.updateGrabbers(); - }; - - // 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.updateGrabbers(); - } else if (wantDebug) { - print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + spaceMode + " DesiredMode: " + newSpaceMode); - } - if (wantDebug) { - print("====== SetSpaceMode called. <========"); - } - }; - - // FUNCTION: UPDATE GRABBERS - that.updateGrabbers = function() { - var wantDebug = false; - if (wantDebug) { - print("======> Update Grabbers ======="); - print(" Selections Count: " + SelectionManager.selections.length); - print(" SpaceMode: " + spaceMode); - print(" DisplayMode: " + getMode()); - } - - if (SelectionManager.selections.length === 0) { - that.setOverlaysVisible(false); - return; - } - - if (SelectionManager.hasSelection()) { - var position = SelectionManager.worldPosition; - var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; - var dimensions = spaceMode === SPACE_LOCAL ? SelectionManager.localDimensions : SelectionManager.worldDimensions; - var rotationInverse = Quat.inverse(rotation); - - var cameraPosition = Camera.getPosition(); - var entityToCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); - - var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); - rotationX = Quat.multiply(rotation, localRotationX); - worldRotationX = rotationX; - var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); - rotationY = Quat.multiply(rotation, localRotationY); - worldRotationY = rotationY; - var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); - rotationZ = Quat.multiply(rotation, localRotationZ); - worldRotationZ = rotationZ; - - var arrowCylinderDimension = entityToCameraDistance * GRABBER_TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE; - var arrowCylinderDimensions = { x:arrowCylinderDimension, y:arrowCylinderDimension * GRABBER_TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE, z:arrowCylinderDimension }; - var arrowConeDimension = entityToCameraDistance * GRABBER_TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE; - var arrowConeDimensions = { x:arrowConeDimension, y:arrowConeDimension, z:arrowConeDimension }; - var cylinderXPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * entityToCameraDistance, y:0, z:0 })); - Overlays.editOverlay(grabberTranslateXCylinder, { - position: cylinderXPos, - rotation: rotationX, - dimensions: arrowCylinderDimensions - }); - var cylinderXDiff = Vec3.subtract(cylinderXPos, position); - var coneXPos = Vec3.sum(cylinderXPos, Vec3.multiply(Vec3.normalize(cylinderXDiff), arrowCylinderDimensions.y * 0.83)); - Overlays.editOverlay(grabberTranslateXCone, { - position: coneXPos, - rotation: rotationX, - dimensions: arrowConeDimensions - }); - var cylinderYPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * entityToCameraDistance, z:0 })); - Overlays.editOverlay(grabberTranslateYCylinder, { - position: cylinderYPos, - rotation: rotationY, - dimensions: arrowCylinderDimensions - }); - var cylinderYDiff = Vec3.subtract(cylinderYPos, position); - var coneYPos = Vec3.sum(cylinderYPos, Vec3.multiply(Vec3.normalize(cylinderYDiff), arrowCylinderDimensions.y * 0.83)); - Overlays.editOverlay(grabberTranslateYCone, { - position: coneYPos, - rotation: rotationY, - dimensions: arrowConeDimensions - }); - var cylinderZPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:GRABBER_TRANSLATE_ARROW_CYLINDER_OFFSET * entityToCameraDistance })); - Overlays.editOverlay(grabberTranslateZCylinder, { - position: cylinderZPos, - rotation: rotationZ, - dimensions: arrowCylinderDimensions - }); - var cylinderZDiff = Vec3.subtract(cylinderZPos, position); - var coneZPos = Vec3.sum(cylinderZPos, Vec3.multiply(Vec3.normalize(cylinderZDiff), arrowCylinderDimensions.y * 0.83)); - Overlays.editOverlay(grabberTranslateZCone, { - position: coneZPos, - rotation: rotationZ, - dimensions: arrowConeDimensions - }); - - var grabberScaleCubeOffsetX = GRABBER_SCALE_CUBE_OFFSET * dimensions.x; - var grabberScaleCubeOffsetY = GRABBER_SCALE_CUBE_OFFSET * dimensions.y; - var grabberScaleCubeOffsetZ = GRABBER_SCALE_CUBE_OFFSET * dimensions.z; - var scaleDimension = entityToCameraDistance * GRABBER_SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE; - var scaleDimensions = { x:scaleDimension, y:scaleDimension, z:scaleDimension }; - var grabberScaleLBNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleLBNCube, { - position: grabberScaleLBNCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleRBNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleRBNCube, { - position: grabberScaleRBNCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleLBFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleLBFCube, { - position: grabberScaleLBFCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleRBFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:-grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleRBFCube, { - position: grabberScaleRBFCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleLTNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleLTNCube, { - position: grabberScaleLTNCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleRTNCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:-grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleRTNCube, { - position: grabberScaleRTNCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleLTFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:-grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleLTFCube, { - position: grabberScaleLTFCubePos, - rotation: rotation, - dimensions: scaleDimensions - }); - var grabberScaleRTFCubePos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:grabberScaleCubeOffsetX, y:grabberScaleCubeOffsetY, z:grabberScaleCubeOffsetZ })); - Overlays.editOverlay(grabberScaleRTFCube, { - position: grabberScaleRTFCubePos, - rotation: rotation, - 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 = entityToCameraDistance * GRABBER_STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE; - var stretchSphereDimensions = { x:stretchSphereDimension, y:stretchSphereDimension, z:stretchSphereDimension }; - var stretchXPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:GRABBER_STRETCH_SPHERE_OFFSET * entityToCameraDistance, y:0, z:0 })); - Overlays.editOverlay(grabberStretchXSphere, { - position: stretchXPos, - dimensions: stretchSphereDimensions - }); - var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleLTFCubePos); - var grabberScaleRBFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRBFCubePos); - var stretchPanelXDimensions = Vec3.subtract(grabberScaleLTFCubePosRot, grabberScaleRBFCubePosRot); - var tempY = Math.abs(stretchPanelXDimensions.y); - stretchPanelXDimensions.x = 0.01; - stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); - stretchPanelXDimensions.z = tempY; - var stretchPanelXPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:dimensions.x / 2, y:0, z:0 })); - Overlays.editOverlay(grabberStretchXPanel, { - position: stretchPanelXPos, - rotation: rotationZ, - dimensions: stretchPanelXDimensions - }); - var stretchYPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:GRABBER_STRETCH_SPHERE_OFFSET * entityToCameraDistance, z:0 })); - Overlays.editOverlay(grabberStretchYSphere, { - position: stretchYPos, - dimensions: stretchSphereDimensions - }); - var grabberScaleLTFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleLTNCubePos); - var grabberScaleRTNCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRTFCubePos); - var stretchPanelYDimensions = Vec3.subtract(grabberScaleLTFCubePosRot, grabberScaleRTNCubePosRot); - var tempX = Math.abs(stretchPanelYDimensions.x); - stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); - stretchPanelYDimensions.y = 0.01; - stretchPanelYDimensions.z = tempX; - var stretchPanelYPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:dimensions.y / 2, z:0 })); - Overlays.editOverlay(grabberStretchYPanel, { - position: stretchPanelYPos, - rotation: rotationY, - dimensions: stretchPanelYDimensions - }); - var stretchZPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:GRABBER_STRETCH_SPHERE_OFFSET * entityToCameraDistance })); - Overlays.editOverlay(grabberStretchZSphere, { - position: stretchZPos, - dimensions: stretchSphereDimensions - }); - var grabberScaleRTFCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRTFCubePos); - var grabberScaleRBNCubePosRot = Vec3.multiplyQbyV(rotationInverse, grabberScaleRBNCubePos); - var stretchPanelZDimensions = Vec3.subtract(grabberScaleRTFCubePosRot, grabberScaleRBNCubePosRot); - var tempX = Math.abs(stretchPanelZDimensions.x); - stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); - stretchPanelZDimensions.y = tempX; - stretchPanelZDimensions.z = 0.01; - var stretchPanelZPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:dimensions.z / 2 })); - Overlays.editOverlay(grabberStretchZPanel, { - position: stretchPanelZPos, - rotation: rotationX, - dimensions: stretchPanelZDimensions - }); - - var rotateDimension = entityToCameraDistance * GRABBER_ROTATE_RINGS_CAMERA_DISTANCE_MULTIPLE; - var rotateDimensions = { x:rotateDimension, y:rotateDimension, z:rotateDimension }; - if (!isActiveTool(grabberRotatePitchRing)) { - Overlays.editOverlay(grabberRotatePitchRing, { - position: position, - rotation: rotationY, - dimensions: rotateDimensions, - majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE - }); - } - if (!isActiveTool(grabberRotateYawRing)) { - Overlays.editOverlay(grabberRotateYawRing, { - position: position, - rotation: rotationZ, - dimensions: rotateDimensions, - majorTickMarksAngle: ROTATION_DEFAULT_TICK_MARKS_ANGLE - }); - } - if (!isActiveTool(grabberRotateRollRing)) { - Overlays.editOverlay(grabberRotateRollRing, { - position: position, - rotation: rotationX, - 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) || - isActiveTool(grabberTranslateYCone) || isActiveTool(grabberTranslateYCylinder) || - isActiveTool(grabberTranslateZCone) || isActiveTool(grabberTranslateZCylinder) || - isActiveTool(grabberCloner) || isActiveTool(selectionBox); - - Overlays.editOverlay(selectionBox, { - position: position, - rotation: rotation, - dimensions: dimensions, - visible: !inModeRotate - }); - - var grabberClonerOffset = { x:GRABBER_CLONER_OFFSET.x * dimensions.x, y:GRABBER_CLONER_OFFSET.y * dimensions.y, z:GRABBER_CLONER_OFFSET.z * dimensions.z }; - var grabberClonerPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, grabberClonerOffset)); - Overlays.editOverlay(grabberCloner, { - position: grabberClonerPos, - rotation: rotation, - dimensions: scaleDimensions - }); - } - - 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) - || isActiveTool(grabberStretchXSphere) || isActiveTool(grabberStretchYSphere) || isActiveTool(grabberStretchZSphere)); - //keep cloner always hidden for now since you can hold Alt to clone while dragging to translate - we may bring cloner back for HMD only later - //that.setGrabberClonerVisible(!activeTool || isActiveTool(grabberCloner)); - - if (wantDebug) { - print("====== Update Grabbers <======="); - } - }; - Script.update.connect(that.updateGrabbers); - - 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++) { - Overlays.editOverlay(allOverlays[i], { visible: isVisible }); - } - }; - - // FUNCTION: SET GRABBER TRANSLATE VISIBLE - that.setGrabberTranslateVisible = function(isVisible) { - that.setGrabberTranslateXVisible(isVisible); - that.setGrabberTranslateYVisible(isVisible); - that.setGrabberTranslateZVisible(isVisible); - }; - - 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 }); - }; - - that.setGrabberTranslateZVisible = function(isVisible) { - Overlays.editOverlay(grabberTranslateZCone, { visible: isVisible }); - Overlays.editOverlay(grabberTranslateZCylinder, { visible: isVisible }); - }; - - // FUNCTION: SET GRABBER ROTATE VISIBLE - that.setGrabberRotateVisible = function(isVisible) { - that.setGrabberRotatePitchVisible(isVisible); - that.setGrabberRotateYawVisible(isVisible); - that.setGrabberRotateRollVisible(isVisible); - }; - - that.setGrabberRotatePitchVisible = function(isVisible) { - Overlays.editOverlay(grabberRotatePitchRing, { visible: isVisible }); - }; - - that.setGrabberRotateYawVisible = function(isVisible) { - Overlays.editOverlay(grabberRotateYawRing, { visible: isVisible }); - }; - - 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 }); - }; - - // 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.resetPreviousHandleColor(); - - 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); - 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", 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); - 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); - - // GRABBER TOOL: GRABBER CLONER - addGrabberTool(grabberCloner, { + // TOOL DEFINITION: HANDLE CLONER + addHandleTool(handleCloner, { mode: "CLONE", onBegin: function(event, pickRay, pickResult) { var doClone = true; @@ -1988,267 +2325,29 @@ SelectionDisplay = (function() { } }); - // FUNCTION: CHECK MOVE - that.checkMove = function() { - }; + addHandleTranslateTool(handleTranslateXCone, "TRANSLATE_X", TRANSLATE_DIRECTION.X); + addHandleTranslateTool(handleTranslateXCylinder, "TRANSLATE_X", TRANSLATE_DIRECTION.X); + addHandleTranslateTool(handleTranslateYCone, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); + addHandleTranslateTool(handleTranslateYCylinder, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); + addHandleTranslateTool(handleTranslateZCone, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); + addHandleTranslateTool(handleTranslateZCylinder, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); - that.checkControllerMove = function() { - if (SelectionManager.hasSelection()) { - var controllerPose = getControllerWorldLocation(activeHand, true); - var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1; - if (controllerPose.valid && lastControllerPoses[hand].valid) { - if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) || - !Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) { - that.mouseMoveEvent({}); - } - } - lastControllerPoses[hand] = controllerPose; - } - }; + addHandleRotateTool(handleRotatePitchRing, "ROTATE_PITCH", ROTATE_DIRECTION.PITCH); + addHandleRotateTool(handleRotateYawRing, "ROTATE_YAW", ROTATE_DIRECTION.YAW); + addHandleRotateTool(handleRotateRollRing, "ROTATE_ROLL", ROTATE_DIRECTION.ROLL); - // FUNCTION DEF(s): Intersection Check Helpers - function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { - var wantDebug = false; - if ((queryRay === undefined) || (queryRay === null)) { - if (wantDebug) { - print("testRayIntersect - EARLY EXIT -> queryRay is undefined OR null!"); - } - return null; - } + addHandleStretchTool(handleStretchXSphere, "STRETCH_X", STRETCH_DIRECTION.X); + addHandleStretchTool(handleStretchYSphere, "STRETCH_Y", STRETCH_DIRECTION.Y); + addHandleStretchTool(handleStretchZSphere, "STRETCH_Z", STRETCH_DIRECTION.Z); - var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludes, overlayExcludes); - - if (wantDebug) { - if (!overlayIncludes) { - print("testRayIntersect - no overlayIncludes provided."); - } - if (!overlayExcludes) { - print("testRayIntersect - no overlayExcludes provided."); - } - print("testRayIntersect - Hit: " + intersectObj.intersects); - print(" intersectObj.overlayID:" + intersectObj.overlayID + "[" + overlayNames[intersectObj.overlayID] + "]"); - print(" OverlayName: " + overlayNames[intersectObj.overlayID]); - print(" intersectObj.distance:" + intersectObj.distance); - print(" intersectObj.face:" + intersectObj.face); - Vec3.print(" intersectObj.intersection:", intersectObj.intersection); - } - - return intersectObj; - } - - // FUNCTION: MOUSE PRESS EVENT - that.mousePressEvent = function (event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MousePressEvent BEG ======================="); - } - if (!event.isLeftButton && !that.triggered) { - // EARLY EXIT-(if another mouse button than left is pressed ignore it) - return false; - } - - 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]; - for (var key in grabberTools) { - if (grabberTools.hasOwnProperty(key)) { - interactiveOverlays.push(key); - } - } - - // Start with unknown mode, in case no tool can handle this. - activeTool = null; - - var results = testRayIntersect(pickRay, interactiveOverlays); - if (results.intersects) { - var hitOverlayID = results.overlayID; - if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || (hitOverlayID === HMD.homeButtonID)) { - // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) - return false; - } - - entityIconOverlayManager.setIconsSelectable(SelectionManager.selections, true); - - var hitTool = grabberTools[ hitOverlayID ]; - if (hitTool) { - activeTool = hitTool; - if (activeTool.onBegin) { - activeTool.onBegin(event, pickRay, results); - } else { - print("ERROR: entitySelectionTool.mousePressEvent - ActiveTool(" + activeTool.mode + ") missing onBegin"); - } - } else { - print("ERROR: entitySelectionTool.mousePressEvent - Hit unexpected object, check interactiveOverlays"); - }// End_if (hitTool) - }// End_If(results.intersects) - - if (wantDebug) { - print(" DisplayMode: " + getMode()); - print("=============== eST::MousePressEvent END ======================="); - } - - // If mode is known then we successfully handled this; - // otherwise, we're missing a tool. - return activeTool; - }; - - that.resetPreviousHandleColor = function() { - if (previousHandle != null) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor }); - previousHandle = null; - } - if (previousHandleHelper != null) { - Overlays.editOverlay(previousHandleHelper, { color: previousHandleColor }); - previousHandleHelper = null; - } - }; - - that.getHandleHelper = function(overlay) { - if (overlay === grabberTranslateXCone) { - return grabberTranslateXCylinder; - } else if (overlay === grabberTranslateXCylinder) { - return grabberTranslateXCone; - } else if (overlay === grabberTranslateYCone) { - return grabberTranslateYCylinder; - } else if (overlay === grabberTranslateYCylinder) { - return grabberTranslateYCone; - } else if (overlay === grabberTranslateZCone) { - return grabberTranslateZCylinder; - } else if (overlay === grabberTranslateZCylinder) { - return grabberTranslateZCone; - } - }; - - // FUNCTION: MOUSE MOVE EVENT - that.mouseMoveEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseMoveEvent BEG ======================="); - } - if (activeTool) { - if (wantDebug) { - print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); - } - activeTool.onMove(event); - - if (wantDebug) { - print(" Trigger SelectionManager::update"); - } - SelectionManager._update(); - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - // EARLY EXIT--(Move handled via active tool) - 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 highlightNeeded = false; - - if (result.intersects) { - switch (result.overlayID) { - case grabberTranslateXCone: - case grabberTranslateXCylinder: - case grabberRotatePitchRing: - case grabberStretchXSphere: - pickedColor = COLOR_RED; - highlightNeeded = true; - break; - case grabberTranslateYCone: - case grabberTranslateYCylinder: - case grabberRotateYawRing: - case grabberStretchYSphere: - pickedColor = COLOR_GREEN; - highlightNeeded = true; - break; - case grabberTranslateZCone: - case grabberTranslateZCylinder: - case grabberRotateRollRing: - case grabberStretchZSphere: - pickedColor = COLOR_BLUE; - highlightNeeded = true; - break; - case grabberScaleLBNCube: - case grabberScaleRBNCube: - case grabberScaleLBFCube: - case grabberScaleRBFCube: - case grabberScaleLTNCube: - case grabberScaleRTNCube: - case grabberScaleLTFCube: - case grabberScaleRTFCube: - pickedColor = GRABBER_SCALE_CUBE_IDLE_COLOR; - highlightNeeded = true; - break; - default: - that.resetPreviousHandleColor(); - break; - } - - if (highlightNeeded) { - that.resetPreviousHandleColor(); - Overlays.editOverlay(result.overlayID, { color: GRABBER_HOVER_COLOR }); - previousHandle = result.overlayID; - previousHandleHelper = that.getHandleHelper(result.overlayID); - if (previousHandleHelper != null) { - Overlays.editOverlay(previousHandleHelper, { color: GRABBER_HOVER_COLOR }); - } - previousHandleColor = pickedColor; - } - - } else { - that.resetPreviousHandleColor(); - } - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - return false; - }; - - // FUNCTION: MOUSE RELEASE EVENT - that.mouseReleaseEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseReleaseEvent BEG ======================="); - } - var showHandles = false; - if (activeTool) { - if (activeTool.onEnd) { - if (wantDebug) { - print(" Triggering ActiveTool(" + activeTool.mode + ")'s onEnd"); - } - activeTool.onEnd(event); - } else if (wantDebug) { - print(" ActiveTool(" + activeTool.mode + ")'s missing onEnd"); - } - } - - showHandles = activeTool; // base on prior tool value - activeTool = null; - - // if something is selected, then reset the "original" properties for any potential next click+move operation - if (SelectionManager.hasSelection()) { - if (showHandles) { - if (wantDebug) { - print(" Triggering that.select"); - } - that.select(SelectionManager.selections[0], event); - } - } - - if (wantDebug) { - print("=============== eST::MouseReleaseEvent END ======================="); - } - }; - - // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: - // Controller.mousePressEvent.connect(that.mousePressEvent); - // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); - Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); + addHandleScaleTool(handleScaleLBNCube, "SCALE_LBN", SCALE_DIRECTION.LBN); + addHandleScaleTool(handleScaleRBNCube, "SCALE_RBN", SCALE_DIRECTION.RBN); + addHandleScaleTool(handleScaleLBFCube, "SCALE_LBF", SCALE_DIRECTION.LBF); + addHandleScaleTool(handleScaleRBFCube, "SCALE_RBF", SCALE_DIRECTION.RBF); + addHandleScaleTool(handleScaleLTNCube, "SCALE_LTN", SCALE_DIRECTION.LTN); + addHandleScaleTool(handleScaleRTNCube, "SCALE_RTN", SCALE_DIRECTION.RTN); + addHandleScaleTool(handleScaleLTFCube, "SCALE_LTF", SCALE_DIRECTION.LTF); + addHandleScaleTool(handleScaleRTFCube, "SCALE_RTF", SCALE_DIRECTION.RTF); return that; }()); \ No newline at end of file From 55f55cd78bdc52c5c2529d27b26dccb0e19f13eb Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 1 Feb 2018 17:59:38 -0800 Subject: [PATCH 032/121] few more updates --- .../system/libraries/entitySelectionTool.js | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index c68fb6e71c..d294fecf21 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -248,6 +248,7 @@ SelectionDisplay = (function() { var TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005; var TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE = 7.5; var TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE = 0.025; + var TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE = 0.83; var ROTATE_RING_CAMERA_DISTANCE_MULTIPLE = 0.15; var ROTATE_CTRL_SNAP_ANGLE = 22.5; @@ -257,7 +258,7 @@ SelectionDisplay = (function() { var ROTATE_RING_SELECTED_INNER_RADIUS = 0.9; // These are multipliers for sizing the rotation degrees display while rotating an entity - var ROTATE_DISPLAY_DISTANCE_MULTIPLIER = 1.0; + var ROTATE_DISPLAY_DISTANCE_MULTIPLIER = 1.75; var ROTATE_DISPLAY_SIZE_X_MULTIPLIER = 0.3; var ROTATE_DISPLAY_SIZE_Y_MULTIPLIER = 0.09; var ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.07; @@ -674,7 +675,8 @@ SelectionDisplay = (function() { var results = testRayIntersect(pickRay, interactiveOverlays); if (results.intersects) { var hitOverlayID = results.overlayID; - if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || (hitOverlayID === HMD.homeButtonID)) { + if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || + (hitOverlayID === HMD.homeButtonID)) { // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) return false; } @@ -1036,6 +1038,7 @@ SelectionDisplay = (function() { }; var arrowConeDimension = toCameraDistance * TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE; var arrowConeDimensions = { x:arrowConeDimension, y:arrowConeDimension, z:arrowConeDimension }; + var arrowConeOffset = arrowCylinderDimensions.y * TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE; var cylinderXPosition = { x:TRANSLATE_ARROW_CYLINDER_OFFSET * toCameraDistance, y:0, z:0 }; cylinderXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderXPosition)); Overlays.editOverlay(handleTranslateXCylinder, { @@ -1044,7 +1047,7 @@ SelectionDisplay = (function() { dimensions: arrowCylinderDimensions }); var cylinderXDiff = Vec3.subtract(cylinderXPosition, position); - var coneXPosition = Vec3.sum(cylinderXPosition, Vec3.multiply(Vec3.normalize(cylinderXDiff), arrowCylinderDimensions.y * 0.83)); + var coneXPosition = Vec3.sum(cylinderXPosition, Vec3.multiply(Vec3.normalize(cylinderXDiff), arrowConeOffset)); Overlays.editOverlay(handleTranslateXCone, { position: coneXPosition, rotation: rotationX, @@ -1058,7 +1061,7 @@ SelectionDisplay = (function() { dimensions: arrowCylinderDimensions }); var cylinderYDiff = Vec3.subtract(cylinderYPosition, position); - var coneYPosition = Vec3.sum(cylinderYPosition, Vec3.multiply(Vec3.normalize(cylinderYDiff), arrowCylinderDimensions.y * 0.83)); + var coneYPosition = Vec3.sum(cylinderYPosition, Vec3.multiply(Vec3.normalize(cylinderYDiff), arrowConeOffset)); Overlays.editOverlay(handleTranslateYCone, { position: coneYPosition, rotation: rotationY, @@ -1072,7 +1075,7 @@ SelectionDisplay = (function() { dimensions: arrowCylinderDimensions }); var cylinderZDiff = Vec3.subtract(cylinderZPosition, position); - var coneZPosition = Vec3.sum(cylinderZPosition, Vec3.multiply(Vec3.normalize(cylinderZDiff), arrowCylinderDimensions.y * 0.83)); + var coneZPosition = Vec3.sum(cylinderZPosition, Vec3.multiply(Vec3.normalize(cylinderZDiff), arrowConeOffset)); Overlays.editOverlay(handleTranslateZCone, { position: coneZPosition, rotation: rotationZ, @@ -1275,9 +1278,12 @@ SelectionDisplay = (function() { }); } - that.setHandleTranslateXVisible(!activeTool || isActiveTool(handleTranslateXCone) || isActiveTool(handleTranslateXCylinder)); - that.setHandleTranslateYVisible(!activeTool || isActiveTool(handleTranslateYCone) || isActiveTool(handleTranslateYCylinder)); - that.setHandleTranslateZVisible(!activeTool || isActiveTool(handleTranslateZCone) || isActiveTool(handleTranslateZCylinder)); + that.setHandleTranslateXVisible(!activeTool || isActiveTool(handleTranslateXCone) || + isActiveTool(handleTranslateXCylinder)); + that.setHandleTranslateYVisible(!activeTool || isActiveTool(handleTranslateYCone) || + isActiveTool(handleTranslateYCylinder)); + that.setHandleTranslateZVisible(!activeTool || isActiveTool(handleTranslateZCone) || + isActiveTool(handleTranslateZCylinder)); that.setHandleRotatePitchVisible(!activeTool || isActiveTool(handleRotatePitchRing)); that.setHandleRotateYawVisible(!activeTool || isActiveTool(handleRotateYawRing)); that.setHandleRotateRollVisible(!activeTool || isActiveTool(handleRotateRollRing)); @@ -1286,10 +1292,14 @@ SelectionDisplay = (function() { that.setHandleStretchXVisible(showScaleStretch || isActiveTool(handleStretchXSphere)); that.setHandleStretchYVisible(showScaleStretch || isActiveTool(handleStretchYSphere)); that.setHandleStretchZVisible(showScaleStretch || isActiveTool(handleStretchZSphere)); - that.setHandleScaleCubeVisible(showScaleStretch || isActiveTool(handleScaleLBNCube) || isActiveTool(handleScaleRBNCube) || isActiveTool(handleScaleLBFCube) || isActiveTool(handleScaleRBFCube) - || isActiveTool(handleScaleLTNCube) || isActiveTool(handleScaleRTNCube) || isActiveTool(handleScaleLTFCube) || isActiveTool(handleScaleRTFCube) - || isActiveTool(handleStretchXSphere) || isActiveTool(handleStretchYSphere) || isActiveTool(handleStretchZSphere)); - that.setHandleScaleEdgeVisible(!isActiveTool(handleRotatePitchRing) && !isActiveTool(handleRotateYawRing) && !isActiveTool(handleRotateRollRing)); + that.setHandleScaleCubeVisible(showScaleStretch || isActiveTool(handleScaleLBNCube) || + isActiveTool(handleScaleRBNCube) || isActiveTool(handleScaleLBFCube) || + isActiveTool(handleScaleRBFCube) || isActiveTool(handleScaleLTNCube) || + isActiveTool(handleScaleRTNCube) || isActiveTool(handleScaleLTFCube) || + isActiveTool(handleScaleRTFCube) || isActiveTool(handleStretchXSphere) || + isActiveTool(handleStretchYSphere) || isActiveTool(handleStretchZSphere)); + that.setHandleScaleEdgeVisible(!isActiveTool(handleRotatePitchRing) && !isActiveTool(handleRotateYawRing) && + !isActiveTool(handleRotateRollRing)); //keep cloner always hidden for now since you can hold Alt to clone while dragging to translate - we may bring cloner back for HMD only later //that.setHandleClonerVisible(!activeTool || isActiveTool(handleCloner)); @@ -1453,7 +1463,9 @@ SelectionDisplay = (function() { startPosition = SelectionManager.worldPosition; translateXZTool.pickPlanePosition = pickResult.intersection; - translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, SelectionManager.worldDimensions.y), SelectionManager.worldDimensions.z); + 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) { @@ -2215,7 +2227,8 @@ SelectionDisplay = (function() { var rotationCenterToZero = Vec3.subtract(rotationZero, rotationCenter); var rotationCenterToZeroLength = Vec3.length(rotationCenterToZero); - rotationDegreesPosition = Vec3.sum(rotationCenter, Vec3.multiply(Vec3.normalize(rotationCenterToZero), rotationCenterToZeroLength * 1.75)); + rotationDegreesPosition = Vec3.sum(rotationCenter, Vec3.multiply(Vec3.normalize(rotationCenterToZero), + rotationCenterToZeroLength * ROTATE_DISPLAY_DISTANCE_MULTIPLIER)); updateRotationDegreesOverlay(0, rotationDegreesPosition); if (wantDebug) { @@ -2350,4 +2363,4 @@ SelectionDisplay = (function() { addHandleScaleTool(handleScaleRTFCube, "SCALE_RTF", SCALE_DIRECTION.RTF); return that; -}()); \ No newline at end of file +}()); From 3804917cf46abb3f9919e252d68ad41a0aa5b6c3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 2 Feb 2018 09:40:57 +0100 Subject: [PATCH 033/121] Orthographic octree selection seems to be working --- libraries/gpu/src/gpu/Framebuffer.cpp | 2 +- libraries/octree/src/OctreeUtils.cpp | 8 +- libraries/octree/src/OctreeUtils.h | 3 +- .../render-utils/src/RenderShadowTask.cpp | 50 +++++++++---- libraries/render-utils/src/RenderShadowTask.h | 10 +-- libraries/render-utils/src/Shadow.slh | 5 -- libraries/render-utils/src/ShadowCore.slh | 4 +- libraries/render/src/render/CullTask.cpp | 30 +++++--- libraries/render/src/render/CullTask.h | 3 +- .../render/src/render/DrawSceneOctree.cpp | 2 +- .../src/render/RenderFetchCullSortTask.cpp | 2 +- libraries/render/src/render/SpatialTree.cpp | 74 +++++++++++++------ libraries/render/src/render/SpatialTree.h | 31 ++++---- libraries/shared/src/ViewFrustum.cpp | 4 + libraries/shared/src/ViewFrustum.h | 1 + 15 files changed, 152 insertions(+), 77 deletions(-) diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 4fcc9ab0f9..8bb9be4a76 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -62,7 +62,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) { samplerDesc._wrapModeU = Sampler::WRAP_BORDER; samplerDesc._wrapModeV = Sampler::WRAP_BORDER; samplerDesc._filter = Sampler::FILTER_MIN_MAG_LINEAR; - samplerDesc._comparisonFunc = LESS_EQUAL; + samplerDesc._comparisonFunc = LESS; depthTexture->setSampler(Sampler(samplerDesc)); framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); diff --git a/libraries/octree/src/OctreeUtils.cpp b/libraries/octree/src/OctreeUtils.cpp index c55016d8e2..ca15324d4e 100644 --- a/libraries/octree/src/OctreeUtils.cpp +++ b/libraries/octree/src/OctreeUtils.cpp @@ -64,8 +64,14 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc return voxelSizeScale / powf(2.0f, renderLevel); } -float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) { +float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) { const float maxScale = (float)TREE_SCALE; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; return atan(maxScale / visibleDistanceAtMaxScale); } + +float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) { + // Smallest visible element is 1cm + const float smallestSize = 0.01f; + return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale); +} diff --git a/libraries/octree/src/OctreeUtils.h b/libraries/octree/src/OctreeUtils.h index c257bcd5f1..0f87bb6f68 100644 --- a/libraries/octree/src/OctreeUtils.h +++ b/libraries/octree/src/OctreeUtils.h @@ -25,7 +25,8 @@ float calculateRenderAccuracy(const glm::vec3& position, float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale); -float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust); +float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust); +float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust); // MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index c9df67dad1..b576bf774c 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -214,9 +214,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende } const auto setupOutput = task.addJob("ShadowSetup"); + const auto queryResolution = setupOutput.getN(2); // Fetch and cull the items from the scene static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto fetchInput = render::Varying(shadowCasterFilter); + const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying(); const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying(); const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs); @@ -281,14 +282,15 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O output.edit0() = args->_renderMode; output.edit1() = args->_sizeScale; + output.edit2() = glm::ivec2(0, 0); const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); - auto& firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); + + auto& firstCascade = globalShadow->getCascade(0); + auto& firstCascadeFrustum = firstCascade.getFrustum(); unsigned int cascadeIndex; - _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); - _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); // Adjust each cascade frustum for (cascadeIndex = 0; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { @@ -297,19 +299,29 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, bias._constant, bias._slope); } + // Now adjust coarse frustum bounds - auto left = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight()); - auto right = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight()); - auto top = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp()); - auto bottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp()); + auto frustumPosition = firstCascadeFrustum->getPosition(); + auto farTopLeft = firstCascadeFrustum->getFarTopLeft() - frustumPosition; + auto farBottomRight = firstCascadeFrustum->getFarBottomRight() - frustumPosition; + + auto left = glm::dot(farTopLeft, firstCascadeFrustum->getRight()); + auto right = glm::dot(farBottomRight, firstCascadeFrustum->getRight()); + auto top = glm::dot(farTopLeft, firstCascadeFrustum->getUp()); + auto bottom = glm::dot(farBottomRight, firstCascadeFrustum->getUp()); auto near = firstCascadeFrustum->getNearClip(); auto far = firstCascadeFrustum->getFarClip(); + for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum(); - auto cascadeLeft = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getRight()); - auto cascadeRight = glm::dot(cascadeFrustum->getFarTopRight(), cascadeFrustum->getRight()); - auto cascadeTop = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getUp()); - auto cascadeBottom = glm::dot(cascadeFrustum->getFarBottomRight(), cascadeFrustum->getUp()); + + farTopLeft = cascadeFrustum->getFarTopLeft() - frustumPosition; + farBottomRight = cascadeFrustum->getFarBottomRight() - frustumPosition; + + auto cascadeLeft = glm::dot(farTopLeft, cascadeFrustum->getRight()); + auto cascadeRight = glm::dot(farBottomRight, cascadeFrustum->getRight()); + auto cascadeTop = glm::dot(farTopLeft, cascadeFrustum->getUp()); + auto cascadeBottom = glm::dot(farBottomRight, cascadeFrustum->getUp()); auto cascadeNear = cascadeFrustum->getNearClip(); auto cascadeFar = cascadeFrustum->getFarClip(); left = glm::min(left, cascadeLeft); @@ -319,6 +331,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O near = glm::min(near, cascadeNear); far = glm::max(far, cascadeFar); } + + _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); + _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); _coarseShadowFrustum->setProjection(glm::ortho(left, right, bottom, top, near, far)); _coarseShadowFrustum->calculate(); @@ -326,10 +341,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O args->pushViewFrustum(*_coarseShadowFrustum); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; - if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { - // Set to ridiculously high amount to prevent solid angle culling in octree selection - args->_sizeScale = 1e16f; - } + + // We want for the octree query enough resolution to catch the details in the lowest cascade. So compute + // the desired resolution for the first cascade frustum and extrapolate it to the coarse frustum. + glm::ivec2 queryResolution = firstCascade.framebuffer->getSize(); + queryResolution.x = int(queryResolution.x * _coarseShadowFrustum->getWidth() / firstCascadeFrustum->getWidth()); + queryResolution.y = int(queryResolution.y * _coarseShadowFrustum->getHeight() / firstCascadeFrustum->getHeight()); + output.edit2() = queryResolution; } } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 1736d07fd5..ce4c3047d8 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -71,10 +71,10 @@ public: float constantBias1{ 0.15f }; float constantBias2{ 0.15f }; float constantBias3{ 0.15f }; - float slopeBias0{ 0.55f }; - float slopeBias1{ 0.55f }; - float slopeBias2{ 0.55f }; - float slopeBias3{ 0.55f }; + float slopeBias0{ 0.6f }; + float slopeBias1{ 0.6f }; + float slopeBias2{ 0.6f }; + float slopeBias3{ 0.6f }; signals: void dirty(); @@ -82,7 +82,7 @@ signals: class RenderShadowSetup { public: - using Outputs = render::VaryingSet2; + using Outputs = render::VaryingSet3; using Config = RenderShadowSetupConfig; using JobModel = render::Job::ModelO; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 6575e68090..e87519b5f4 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -79,15 +79,10 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3]) ); - return shadowAttenuation; } float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) { - if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { - // If a point is not in the map, do not attenuate - return 1.0; - } float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL; return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 782e2bc2b8..e49e8d638f 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -53,8 +53,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { - bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0)); - bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1)); + bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0)); + bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1)); return all(greaterThanZero) && all(lessThanOne); } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index c6ff224560..b3efc4f1a8 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -14,8 +14,8 @@ #include #include -#include #include +#include using namespace render; @@ -33,7 +33,7 @@ struct Test { _renderDetails(renderDetails) { // FIXME: Keep this code here even though we don't use it yet /*_eyePos = _args->getViewFrustum().getPosition(); - float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); + float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree auto tanAlpha = tan(angle); @@ -130,7 +130,8 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu // start fresh outSelection.clear(); - auto& filter = inputs; + auto& filter = inputs.get0(); + auto frustumResolution = inputs.get1(); if (!filter.selectsNothing()) { assert(renderContext->args); @@ -149,8 +150,19 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu } // Octree selection! - float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); - scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle); + float threshold = 0.0f; + if (queryFrustum.isPerspective()) { + threshold = getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust); + if (frustumResolution.y > 0) { + threshold = glm::max(queryFrustum.getFieldOfView() / frustumResolution.y, threshold); + } + } else { + threshold = getOrthographicAccuracySize(args->_sizeScale, args->_boundaryLevelAdjust); + glm::vec2 frustumSize = glm::vec2(queryFrustum.getWidth(), queryFrustum.getHeight()); + const auto pixelResolution = frustumResolution.x > 0 ? frustumResolution : glm::ivec2(2048, 2048); + threshold = glm::max(threshold, glm::min(frustumSize.x / pixelResolution.x, frustumSize.y / pixelResolution.y)); + } + scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, threshold); } } @@ -371,7 +383,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, // Now get the bound, and // filter individually against the _filter - // inside & fit items: filter only, culling is disabled + // inside & fit items: filter only { PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { @@ -383,7 +395,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, } } - // inside & subcell items: filter only, culling is disabled + // inside & subcell items: filter only { PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { @@ -395,7 +407,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, } } - // partial & fit items: filter only, culling is disabled + // partial & fit items: filter only { PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { @@ -407,7 +419,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, } } - // partial & subcell items: filter only, culling is disabled + // partial & subcell items: filter only { PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index a140a86aee..53d46d11b4 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -53,9 +53,10 @@ namespace render { bool _justFrozeFrustum{ false }; ViewFrustum _frozenFrustum; float _lodAngle; + public: using Config = FetchSpatialTreeConfig; - using Inputs = ItemFilter; + using Inputs = render::VaryingSet2; using JobModel = Job::ModelIO; FetchSpatialTree() {} diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 36663a454a..08d6340e43 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -148,7 +148,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS } // Draw the LOD Reticle { - float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); Transform crosshairModel; crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0)); crosshairModel.setScale(1000.0f * tanf(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index a1b4f079e7..23935851b3 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -23,7 +23,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // CPU jobs: // Fetch and cull the items from the scene const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto fetchInput = render::Varying(filter); + const auto fetchInput = FetchSpatialTree::Inputs(filter, glm::ivec2(0,0)).asVarying(); const auto spatialSelection = task.addJob("FetchSceneSelection", fetchInput); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, render::Varying(filter)).asVarying(); const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); diff --git a/libraries/render/src/render/SpatialTree.cpp b/libraries/render/src/render/SpatialTree.cpp index 1bb3538521..c9f810ebc5 100644 --- a/libraries/render/src/render/SpatialTree.cpp +++ b/libraries/render/src/render/SpatialTree.cpp @@ -12,9 +12,26 @@ #include - using namespace render; +void Octree::PerspectiveSelector::setAngle(float a) { + const float MAX_LOD_ANGLE = glm::radians(45.0f); + const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f); + + angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a)); + auto tanAlpha = tan(angle); + squareTanAlpha = (float)(tanAlpha * tanAlpha); +} + +float Octree::PerspectiveSelector::testThreshold(const Coord3f& point, float size) const { + auto eyeToPoint = point - eyePos; + return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; +} + +float Octree::OrthographicSelector::testThreshold(const Coord3f& point, float size) const { + return (size * size) - squareMinSize; +} + const float Octree::INV_DEPTH_DIM[] = { 1.0f, @@ -520,10 +537,9 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum // Test for lod auto cellLocation = cell.getlocation(); - float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); - if (lod < 0.0f) { + float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); + if (test < 0.0f) { return 0; - break; } // Select this cell partially in frustum @@ -543,13 +559,13 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum } -int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { +int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { int numSelectedsIn = (int) selection.size(); auto cell = getConcreteCell(cellID); auto cellLocation = cell.getlocation(); - float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); - if (lod < 0.0f) { + float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); + if (test < 0.0f) { return 0; } @@ -580,24 +596,40 @@ int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) return (int) selection.size() - numSelectedsIn; } - -int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const { +int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const { auto worldPlanes = frustum.getPlanes(); - FrustumSelector selector; - for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { - ::Plane octPlane; - octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); - selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + if (frustum.isPerspective()) { + PerspectiveSelector selector; + for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { + ::Plane octPlane; + octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); + selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + } + + selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH); + selector.setAngle(threshold); + + return Octree::select(selection, selector); + } else { + OrthographicSelector selector; + for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { + ::Plane octPlane; + octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); + selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + } + + // Divide the threshold (which is in world distance units) by the dimension of the octree + // as all further computations will be done in normalized octree units + threshold *= getInvCellWidth(ROOT_DEPTH); + selector.setSize(threshold); + + return Octree::select(selection, selector); } - - selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH); - selector.setAngle(glm::radians(lodAngle)); - - return Octree::select(selection, selector); } -int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const { - selectCells(selection.cellSelection, frustum, lodAngle); +int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, + float threshold) const { + selectCells(selection.cellSelection, frustum, threshold); // Just grab the items in every selected bricks for (auto brickId : selection.cellSelection.insideBricks) { diff --git a/libraries/render/src/render/SpatialTree.h b/libraries/render/src/render/SpatialTree.h index a89b9847e6..b06053344d 100644 --- a/libraries/render/src/render/SpatialTree.h +++ b/libraries/render/src/render/SpatialTree.h @@ -312,23 +312,27 @@ namespace render { class FrustumSelector { public: Coord4f frustum[6]; + + virtual ~FrustumSelector() {} + virtual float testThreshold(const Coord3f& point, float size) const = 0; + }; + + class PerspectiveSelector : public FrustumSelector { + public: Coord3f eyePos; float angle; float squareTanAlpha; - const float MAX_LOD_ANGLE = glm::radians(45.0f); - const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f); + void setAngle(float a); + float testThreshold(const Coord3f& point, float size) const override; + }; - void setAngle(float a) { - angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a)); - auto tanAlpha = tan(angle); - squareTanAlpha = (float)(tanAlpha * tanAlpha); - } + class OrthographicSelector : public FrustumSelector { + public: + float squareMinSize; - float testSolidAngle(const Coord3f& point, float size) const { - auto eyeToPoint = point - eyePos; - return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; - } + void setSize(float a) { squareMinSize = a * a; } + float testThreshold(const Coord3f& point, float size) const override; }; int select(CellSelection& selection, const FrustumSelector& selector) const; @@ -443,7 +447,7 @@ namespace render { Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey); // Selection and traverse - int selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const; + int selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const; class ItemSelection { public: @@ -469,7 +473,8 @@ namespace render { } }; - int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const; + int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, + float threshold) const; }; } diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 5b016d4e91..0f98e8020c 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -847,3 +847,7 @@ void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8 triangle.v2 = points[vertexIndices[2]]; } } + +bool ViewFrustum::isPerspective() const { + return _projection[3][2] != 0.0f && _projection[2][3] != 0.0f && _projection[3][3] == 0.0f; +} diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index b55fe8b327..859b5c49c5 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -49,6 +49,7 @@ public: // setters for lens attributes void setProjection(const glm::mat4 & projection); void setFocalLength(float focalLength) { _focalLength = focalLength; } + bool isPerspective() const; // getters for lens attributes const glm::mat4& getProjection() const { return _projection; } From 1f4671ba175547f00ccce78b30627bba9de321b6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 2 Feb 2018 11:09:28 +0100 Subject: [PATCH 034/121] Cleaned up orthographic shadow culling functor --- interface/src/SecondaryCamera.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 25 ++++++++++--------- libraries/render-utils/src/RenderShadowTask.h | 20 ++++++++++++--- libraries/render-utils/src/RenderViewTask.cpp | 8 +----- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 5db34c9441..c4199f15b2 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -19,7 +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); + task.addJob("RenderShadowTask"); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); if (!isDeferred) { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b576bf774c..da7f6d97fa 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -200,8 +200,10 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con }); } -void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) { - cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; }; +void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { + ::CullFunctor cullFunctor = [this](const RenderArgs* args, const AABox& bounds) { + return _cullFunctor(args, bounds); + }; // Prepare the ShapePipeline ShapePlumberPointer shapePlumber = std::make_shared(); @@ -229,7 +231,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); - const auto shadowFilter = task.addJob(jobName, i); + const auto shadowFilter = task.addJob(jobName, i, _cullFunctor); // CPU jobs: finer grained culling const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter).asVarying(); @@ -281,8 +283,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O RenderArgs* args = renderContext->args; output.edit0() = args->_renderMode; - output.edit1() = args->_sizeScale; - output.edit2() = glm::ivec2(0, 0); + output.edit1() = glm::ivec2(0, 0); const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { @@ -347,7 +348,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O glm::ivec2 queryResolution = firstCascade.framebuffer->getSize(); queryResolution.x = int(queryResolution.x * _coarseShadowFrustum->getWidth() / firstCascadeFrustum->getWidth()); queryResolution.y = int(queryResolution.y * _coarseShadowFrustum->getHeight() / firstCascadeFrustum->getHeight()); - output.edit2() = queryResolution; + output.edit1() = queryResolution; } } @@ -365,11 +366,12 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon auto& cascade = globalShadow->getCascade(_cascadeIndex); auto& cascadeFrustum = cascade.getFrustum(); args->pushViewFrustum(*cascadeFrustum); - // Set the cull threshold to 2 shadow texels. - auto texelSize = glm::max(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x; - texelSize *= 2.0f; - // SizeScale is used in the shadow cull function defined ine RenderViewTask - args->_sizeScale = texelSize * texelSize; + auto texelSize = glm::min(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x; + // Set the cull threshold to 16 shadow texels. + const auto minTexelCount = 16.0f; + // TODO : maybe adapt that with LOD management system? + texelSize *= minTexelCount; + _cullFunctor._minSquareSize = texelSize * texelSize; } else { output = ItemFilter::Builder::nothing(); } @@ -393,5 +395,4 @@ void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext assert(args->hasViewFrustum()); // Reset the render args args->_renderMode = input.get0(); - args->_sizeScale = input.get1(); } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index ce4c3047d8..15651354f1 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -50,9 +50,22 @@ 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); void configure(const Config& configuration); + + struct CullFunctor { + float _minSquareSize{ 0.0f }; + + bool operator()(const RenderArgs* args, const AABox& bounds) const { + // Cull only objects that are too small relatively to shadow frustum + const auto boundsSquareRadius = glm::dot(bounds.getDimensions(), bounds.getDimensions()); + return boundsSquareRadius > _minSquareSize; + } + }; + + CullFunctor _cullFunctor; + }; class RenderShadowSetupConfig : public render::Job::Config { @@ -82,7 +95,7 @@ signals: class RenderShadowSetup { public: - using Outputs = render::VaryingSet3; + using Outputs = render::VaryingSet2; using Config = RenderShadowSetupConfig; using JobModel = render::Job::ModelO; @@ -107,12 +120,13 @@ public: using Outputs = render::ItemFilter; using JobModel = render::Job::ModelO; - RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor) : _cascadeIndex{ cascadeIndex }, _cullFunctor{ cullFunctor } {} void run(const render::RenderContextPointer& renderContext, Outputs& output); private: unsigned int _cascadeIndex; + RenderShadowTask::CullFunctor& _cullFunctor; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index c2e43582cd..68585ac437 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -17,13 +17,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) { // auto items = input.get(); - // Shadows use an orthographic projection because they are linked to sunlights - // but the cullFunctor passed is probably tailored for perspective projection and culls too much. - task.addJob("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) { - // Cull only objects that are too small relatively to shadow frustum - const auto boundsSquareRadius = glm::dot(bounds.getDimensions(), bounds.getDimensions()); - return boundsSquareRadius > args->_sizeScale; - }); + task.addJob("RenderShadowTask"); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); From 234cb1e3e612a1fe4b19ac516840b771a020f0da Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 2 Feb 2018 12:15:43 +0100 Subject: [PATCH 035/121] Added anti frustum test to remove lower cascade objects from higher shadow cascades --- .../render-utils/src/RenderShadowTask.cpp | 24 ++++++++++--- libraries/render-utils/src/RenderShadowTask.h | 2 +- libraries/render/src/render/CullTask.cpp | 36 ++++++++++++++----- libraries/render/src/render/CullTask.h | 2 +- libraries/shared/src/ViewFrustum.cpp | 12 +++++++ libraries/shared/src/ViewFrustum.h | 1 + 6 files changed, 63 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index da7f6d97fa..829c0fbcf2 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -228,13 +228,26 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende const auto sortedPipelines = task.addJob("PipelineSortShadow", shadowItems); const auto sortedShapes = task.addJob("DepthSortShadow", sortedPipelines, true); + render::Varying cascadeFrustums[SHADOW_CASCADE_MAX_COUNT] = { + ViewFrustumPointer(), + ViewFrustumPointer(), + ViewFrustumPointer(), + ViewFrustumPointer() + }; + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); - const auto shadowFilter = task.addJob(jobName, i, _cullFunctor); + const auto cascadeSetupOutput = task.addJob(jobName, i, _cullFunctor); + const auto shadowFilter = cascadeSetupOutput.getN(0); + auto antiFrustum = render::Varying(ViewFrustumPointer()); + cascadeFrustums[i] = cascadeSetupOutput.getN(1); + if (i > 1) { + antiFrustum = cascadeFrustums[i - 2]; + } // CPU jobs: finer grained culling - const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter).asVarying(); + const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying(); const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, cullFunctor, RenderDetails::SHADOW); // GPU jobs: Render to shadow map @@ -360,7 +373,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); // Set the keylight render args auto& cascade = globalShadow->getCascade(_cascadeIndex); @@ -372,8 +385,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // TODO : maybe adapt that with LOD management system? texelSize *= minTexelCount; _cullFunctor._minSquareSize = texelSize * texelSize; + + output.edit1() = cascadeFrustum; } else { - output = ItemFilter::Builder::nothing(); + output.edit0() = ItemFilter::Builder::nothing(); + output.edit1() = ViewFrustumPointer(); } } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 15651354f1..975f755a48 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -117,7 +117,7 @@ private: class RenderShadowCascadeSetup { public: - using Outputs = render::ItemFilter; + using Outputs = render::VaryingSet2; using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor) : _cascadeIndex{ cascadeIndex }, _cullFunctor{ cullFunctor } {} diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index b3efc4f1a8..633465dba3 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -24,13 +24,15 @@ struct Test { CullFunctor _functor; RenderArgs* _args; RenderDetails::Item& _renderDetails; + ViewFrustumPointer _antiFrustum; glm::vec3 _eyePos; float _squareTanAlpha; - Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : + Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum = nullptr) : _functor(functor), _args(pargs), - _renderDetails(renderDetails) { + _renderDetails(renderDetails), + _antiFrustum(antiFrustum) { // FIXME: Keep this code here even though we don't use it yet /*_eyePos = _args->getViewFrustum().getPosition(); float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); @@ -49,6 +51,15 @@ struct Test { return true; } + bool antiFrustumTest(const AABox& bound) { + assert(_antiFrustum); + if (_antiFrustum->boxInsideFrustum(bound)) { + _renderDetails._outOfView++; + return false; + } + return true; + } + bool solidAngleTest(const AABox& bound) { // FIXME: Keep this code here even though we don't use it yet //auto eyeToPoint = bound.calcCenter() - _eyePos; @@ -331,6 +342,7 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input const auto& inShapes = inputs.get0(); const auto& filter = inputs.get1(); + const auto& antiFrustum = inputs.get2(); auto& outShapes = outputs.edit0(); auto& outBounds = outputs.edit1(); @@ -339,7 +351,7 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input if (!filter.selectsNothing()) { auto& details = args->_details.edit(_detailType); - Test test(_cullFunctor, args, details); + Test test(_cullFunctor, args, details, antiFrustum); for (auto& inItems : inShapes) { auto key = inItems.first; @@ -351,13 +363,21 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input details._considered += (int)inItems.second.size(); - for (auto& item : inItems.second) { - if (test.frustumTest(item.bound) && test.solidAngleTest(item.bound)) { - outItems->second.emplace_back(item); - outBounds += item.bound; + if (antiFrustum == nullptr) { + for (auto& item : inItems.second) { + if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) { + outItems->second.emplace_back(item); + outBounds += item.bound; + } + } + } else { + for (auto& item : inItems.second) { + if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) { + outItems->second.emplace_back(item); + outBounds += item.bound; + } } } - details._rendered += (int)outItems->second.size(); } diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 53d46d11b4..a9695d6281 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -110,7 +110,7 @@ namespace render { class CullShapeBounds { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using Outputs = render::VaryingSet2; using JobModel = Job::ModelIO; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 0f98e8020c..2a2eebc0a7 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -280,6 +280,18 @@ bool ViewFrustum::boxIntersectsFrustum(const AABox& box) const { return true; } +bool ViewFrustum::boxInsideFrustum(const AABox& box) const { + // only check against frustum + for (int i = 0; i < NUM_FRUSTUM_PLANES; i++) { + const glm::vec3& normal = _planes[i].getNormal(); + // check distance to nearest box point + if (_planes[i].distance(box.getNearestVertex(normal)) < 0.0f) { + return false; + } + } + return true; +} + bool ViewFrustum::sphereIntersectsKeyhole(const glm::vec3& center, float radius) const { // check positive touch against central sphere if (glm::length(center - _position) <= (radius + _centerSphereRadius)) { diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 859b5c49c5..981aabe70c 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -104,6 +104,7 @@ public: bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const; bool cubeIntersectsFrustum(const AACube& box) const; bool boxIntersectsFrustum(const AABox& box) const; + bool boxInsideFrustum(const AABox& box) const; bool sphereIntersectsKeyhole(const glm::vec3& center, float radius) const; bool cubeIntersectsKeyhole(const AACube& cube) const; From e9747e9d85336baa56c12f664aa0dcda54ee2e6b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 2 Feb 2018 15:06:22 +0100 Subject: [PATCH 036/121] Small optimisations in shadow shader --- libraries/render-utils/src/RenderShadowTask.cpp | 4 ++-- libraries/render-utils/src/RenderShadowTask.h | 8 ++++---- libraries/render-utils/src/Shadow.slh | 2 +- libraries/render-utils/src/ShadowCore.slh | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 829c0fbcf2..f3797edc11 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -380,8 +380,8 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon auto& cascadeFrustum = cascade.getFrustum(); args->pushViewFrustum(*cascadeFrustum); auto texelSize = glm::min(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x; - // Set the cull threshold to 16 shadow texels. - const auto minTexelCount = 16.0f; + // Set the cull threshold to 24 shadow texels. This is totally arbitrary + const auto minTexelCount = 24.0f; // TODO : maybe adapt that with LOD management system? texelSize *= minTexelCount; _cullFunctor._minSquareSize = texelSize * texelSize; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 975f755a48..87d78ffe51 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -82,12 +82,12 @@ public: float constantBias0{ 0.15f }; float constantBias1{ 0.15f }; - float constantBias2{ 0.15f }; - float constantBias3{ 0.15f }; + float constantBias2{ 0.175f }; + float constantBias3{ 0.2f }; float slopeBias0{ 0.6f }; float slopeBias1{ 0.6f }; - float slopeBias2{ 0.6f }; - float slopeBias3{ 0.6f }; + float slopeBias2{ 0.7f }; + float slopeBias3{ 0.82f }; signals: void dirty(); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index e87519b5f4..abb04a4498 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -89,7 +89,7 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); - vec4 cascadeShadowCoords[2]; + vec4 cascadeShadowCoords[2] = { vec4(0), vec4(0) }; ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index e49e8d638f..a787c54ca0 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -82,10 +82,10 @@ float evalShadowCascadeWeight(vec4 cascadeTexCoords) { float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) { cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]); cascadeIndices.y = cascadeIndices.x+1; - if (cascadeIndices.x < (getShadowCascadeCount()-1)) { + float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]); + if (firstCascadeWeight<1.0 && cascadeIndices.x < (getShadowCascadeCount()-1)) { cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]); - float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]); float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]); // Returns the mix amount between first and second cascade. return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight); From 8a011036ef4f9be5b5412936a3f0cfe6a0de208b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 2 Feb 2018 18:07:35 +0100 Subject: [PATCH 037/121] Removed warnings on mac and ubuntu --- libraries/render-utils/src/LightStage.cpp | 2 -- libraries/render/src/render/CullTask.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 259d0dd665..854ff71e20 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -208,8 +208,6 @@ 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(); for (auto& cascade : _cascades) { cascade._frustum->setOrientation(orientation); cascade._frustum->setPosition(position); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 633465dba3..c745220ab8 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -390,7 +390,6 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input void FetchSpatialSelection::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems) { assert(renderContext->args); - RenderArgs* args = renderContext->args; auto& scene = renderContext->_scene; auto& inSelection = inputs.get0(); From 3a7290c3ede88e7e402c1547d6133621c19c62fa Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Mon, 8 Jan 2018 12:15:13 -0800 Subject: [PATCH 038/121] starting 2d image entity type --- libraries/entities/src/EntityTypes.h | 1 + libraries/entities/src/FlatImageEntity.cpp | 74 ++++++++++++++++++++++ libraries/entities/src/FlatImageEntity.h | 41 ++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 libraries/entities/src/FlatImageEntity.cpp create mode 100644 libraries/entities/src/FlatImageEntity.h diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 316bf2b813..dac36f28dd 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -49,6 +49,7 @@ public: PolyVox, PolyLine, Shape, + Image, LAST = Shape } EntityType; diff --git a/libraries/entities/src/FlatImageEntity.cpp b/libraries/entities/src/FlatImageEntity.cpp new file mode 100644 index 0000000000..9eb3f64bcb --- /dev/null +++ b/libraries/entities/src/FlatImageEntity.cpp @@ -0,0 +1,74 @@ +// +// FlatImageEntity.cpp +// libraries/entities/src +// +// Created by Elisa Lupin-Jimenez on 1/3/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 + +#include +#include +#include +#include + +#include "EntitiesLogging.h" +#include "EntityItemProperties.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" +#include "ResourceCache.h" +#include "ShapeEntityItem.h" +#include "FlatImageEntity.h" + +const QString FlatImageEntity::DEFAULT_IMAGE_URL = QString(""); + +EntityItemPointer FlatImageEntity::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer entity(new FlatImageEntity(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +FlatImageEntity::FlatImageEntity(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Image; +} + +EntityItemProperties FlatImageEntity::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class + properties.setShape("Image"); + return properties; +} + +bool FlatImageEntity::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "FlatImageEntity::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties.getLastEdited()); + } + return somethingChanged; +} + +void FlatImageEntity::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_SHAPE, entity::stringFromShape(getShape())); +} + diff --git a/libraries/entities/src/FlatImageEntity.h b/libraries/entities/src/FlatImageEntity.h new file mode 100644 index 0000000000..08c13f8f9f --- /dev/null +++ b/libraries/entities/src/FlatImageEntity.h @@ -0,0 +1,41 @@ +// +// FlatImageEntity.h +// libraries/entities/src +// +// Created by Elisa Lupin-Jimenez on 1/3/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_FlatImageEntity_h +#define hifi_FlatImageEntity_h + +#include "EntityItem.h" + +class FlatImageEntity : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + FlatImageEntity(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; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + static const QString DEFAULT_IMAGE_URL; + +}; + +#endif // hifi_FlatImageEntity_h From fdca8ab93eef09e21af8883e20b5072441300b80 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 9 Jan 2018 17:46:24 -0800 Subject: [PATCH 039/121] added image button to edit.js, working on connecting to cpp --- .../resources/qml/hifi/tablet/EditTabView.qml | 12 ++++++ .../entities/src/EntityItemProperties.cpp | 4 ++ libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 3 ++ libraries/entities/src/EntityTypes.cpp | 1 + libraries/entities/src/EntityTypes.h | 2 +- libraries/entities/src/FlatImageEntity.cpp | 43 +++++++++++++++++-- libraries/entities/src/FlatImageEntity.h | 12 ++++++ scripts/system/edit.js | 14 +++++- 9 files changed, 87 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index e94325f399..482469d355 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -101,6 +101,18 @@ TabView { } } + // for image + NewEntityButton { + icon: "icons/create-icons/25-web-1-01.svg" + text: "IMAGE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newImageButton" } + }); + editTabView.currentIndex = 2 + } + } + NewEntityButton { icon: "icons/create-icons/25-web-1-01.svg" text: "WEB" diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index e2a5ddf8b5..a1c51b650f 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1369,6 +1369,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); } + if (properties.getType() == EntityTypes::Image) { + APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); + } + if (properties.getType() == EntityTypes::Text) { APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 3e0770f386..5bd6836336 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -194,6 +194,7 @@ public: DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); + DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 90438ab01c..4768ebed86 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -42,6 +42,9 @@ enum EntityPropertyList { PROP_ANIMATION_ALLOW_TRANSLATION, PROP_RELAY_PARENT_JOINTS, + // for image + PROP_IMAGE_URL, + // these properties are supported by the EntityItem base class PROP_REGISTRATION_POINT, PROP_ANGULAR_VELOCITY, diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index cb17c28fd7..e53b9d02f6 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -89,6 +89,7 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const EntityItemPointer newEntityItem = NULL; EntityTypeFactory factory = NULL; if (entityType >= 0 && entityType <= LAST) { + qCDebug(entities) << "type: " << entityType; factory = _factories[entityType]; } if (factory) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index dac36f28dd..8d986c8090 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,8 +48,8 @@ public: Line, PolyVox, PolyLine, - Shape, Image, + Shape, LAST = Shape } EntityType; diff --git a/libraries/entities/src/FlatImageEntity.cpp b/libraries/entities/src/FlatImageEntity.cpp index 9eb3f64bcb..66a5702747 100644 --- a/libraries/entities/src/FlatImageEntity.cpp +++ b/libraries/entities/src/FlatImageEntity.cpp @@ -38,14 +38,14 @@ FlatImageEntity::FlatImageEntity(const EntityItemID& entityItemID) : EntityItem( EntityItemProperties FlatImageEntity::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - properties.setShape("Image"); + properties.setShape("Quad"); return properties; } bool FlatImageEntity::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); + //SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); if (somethingChanged) { bool wantDebug = false; @@ -60,6 +60,13 @@ bool FlatImageEntity::setProperties(const EntityItemProperties& properties) { return somethingChanged; } +// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time +EntityPropertyFlags FlatImageEntity::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + + return requestedProperties; +} + void FlatImageEntity::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, @@ -69,6 +76,36 @@ void FlatImageEntity::appendSubclassData(OctreePacketData* packetData, EncodeBit OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_SHAPE, entity::stringFromShape(getShape())); + // Using "Quad" shape as defined in ShapeEntityItem.cpp + APPEND_ENTITY_PROPERTY(PROP_SHAPE, "Quad"); +} + +int FlatImageEntity::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + return bytesRead; +} + +void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { + const float MAX_FLAT_DIMENSION = 0.0001f; + if (value.y > MAX_FLAT_DIMENSION) { + // enforce flatness in Y + glm::vec3 newDimensions = value; + newDimensions.y = MAX_FLAT_DIMENSION; + EntityItem::setUnscaledDimensions(newDimensions); + } else { + EntityItem::setUnscaledDimensions(value); + } +} + +QString FlatImageEntity::getImageURL() const { + return resultWithReadLock([&] { + return _imageURL; + }); } diff --git a/libraries/entities/src/FlatImageEntity.h b/libraries/entities/src/FlatImageEntity.h index 08c13f8f9f..4dd91b1215 100644 --- a/libraries/entities/src/FlatImageEntity.h +++ b/libraries/entities/src/FlatImageEntity.h @@ -26,6 +26,8 @@ public: virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; virtual bool setProperties(const EntityItemProperties& properties) override; + EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, @@ -34,7 +36,17 @@ public: int& propertyCount, OctreeElement::AppendState& appendState) const override; + int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + static const QString DEFAULT_IMAGE_URL; + QString getImageURL() const; + +protected: + QString _imageURL; }; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 863c185cb4..4241468de1 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -251,7 +251,8 @@ var toolBar = (function () { // Align entity with Avatar orientation. properties.rotation = MyAvatar.orientation; - var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"]; + // added image here + var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Image", "Web"]; if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box prior to creating it. @@ -286,6 +287,7 @@ var toolBar = (function () { properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); } + print("properties.type: " + properties.type); entityID = Entities.addEntity(properties); if (properties.type === "ParticleEffect") { @@ -538,6 +540,16 @@ var toolBar = (function () { }); }); + // for image button + addButton("newImageButton", "web-01.svg", function () { + print("new image message is received"); + createNewEntity({ + type: "Image", + dimensions: DEFAULT_DIMENSIONS, + sourceUrl: "https://highfidelity.com/" + }); + }); + addButton("newWebButton", "web-01.svg", function () { createNewEntity({ type: "Web", From fc0e87d5eaa13cf7c97a1ab2ab8a8eac264576d5 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 10 Jan 2018 17:08:55 -0800 Subject: [PATCH 040/121] more infrastructure links --- .../entities/src/EntityItemProperties.cpp | 9 ++++ .../entities/src/EntityScriptingInterface.cpp | 2 + libraries/entities/src/EntityTree.cpp | 1 + libraries/entities/src/EntityTypes.cpp | 4 +- ...latImageEntity.cpp => ImageEntityItem.cpp} | 42 ++++++++++++------- .../{FlatImageEntity.h => ImageEntityItem.h} | 13 +++--- scripts/system/edit.js | 2 +- 7 files changed, 51 insertions(+), 22 deletions(-) rename libraries/entities/src/{FlatImageEntity.cpp => ImageEntityItem.cpp} (70%) rename libraries/entities/src/{FlatImageEntity.h => ImageEntityItem.h} (85%) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a1c51b650f..fc8ca18fb6 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -590,6 +590,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); } + // Image only + if (_type == EntityTypes::Image) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); + } + // Web only if (_type == EntityTypes::Web) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); @@ -1734,6 +1739,10 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); } + if (properties.getType() == EntityTypes::Image) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); + } + if (properties.getType() == EntityTypes::Text) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4342f0e683..cab4251d97 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -266,6 +266,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties bool success = true; if (_entityTree) { _entityTree->withWriteLock([&] { + propertiesWithSimID.getType(); + qCDebug(entities) << "check 2 type: " << propertiesWithSimID.getType(); EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); if (entity) { if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bf29f3bec9..56c3aa216c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -514,6 +514,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti // construct the instance of the entity EntityTypes::EntityType type = props.getType(); + qCDebug(entities) << "check 3 type: " << type; result = EntityTypes::constructEntityItem(type, entityID, props); if (result) { diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index e53b9d02f6..2e4a0e89a9 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -28,6 +28,7 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" +#include "ImageEntityItem.h" #include "ShapeEntityItem.h" QMap EntityTypes::_typeToNameMap; @@ -47,6 +48,7 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) +REGISTER_ENTITY_TYPE(Image) REGISTER_ENTITY_TYPE(Shape) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory) @@ -89,7 +91,7 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const EntityItemPointer newEntityItem = NULL; EntityTypeFactory factory = NULL; if (entityType >= 0 && entityType <= LAST) { - qCDebug(entities) << "type: " << entityType; + qCDebug(entities) << "check 4 type: " << entityType; factory = _factories[entityType]; } if (factory) { diff --git a/libraries/entities/src/FlatImageEntity.cpp b/libraries/entities/src/ImageEntityItem.cpp similarity index 70% rename from libraries/entities/src/FlatImageEntity.cpp rename to libraries/entities/src/ImageEntityItem.cpp index 66a5702747..a94374f7b5 100644 --- a/libraries/entities/src/FlatImageEntity.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -1,5 +1,5 @@ // -// FlatImageEntity.cpp +// ImageEntityItem.cpp // libraries/entities/src // // Created by Elisa Lupin-Jimenez on 1/3/18. @@ -22,27 +22,27 @@ #include "EntityTreeElement.h" #include "ResourceCache.h" #include "ShapeEntityItem.h" -#include "FlatImageEntity.h" +#include "ImageEntityItem.h" -const QString FlatImageEntity::DEFAULT_IMAGE_URL = QString(""); +const QString ImageEntityItem::DEFAULT_IMAGE_URL = QString(""); -EntityItemPointer FlatImageEntity::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity(new FlatImageEntity(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); +EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer entity(new ImageEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } -FlatImageEntity::FlatImageEntity(const EntityItemID& entityItemID) : EntityItem(entityItemID) { +ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Image; } -EntityItemProperties FlatImageEntity::getProperties(EntityPropertyFlags desiredProperties) const { +EntityItemProperties ImageEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class properties.setShape("Quad"); return properties; } -bool FlatImageEntity::setProperties(const EntityItemProperties& properties) { +bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class //SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); @@ -52,7 +52,7 @@ bool FlatImageEntity::setProperties(const EntityItemProperties& properties) { if (wantDebug) { uint64_t now = usecTimestampNow(); int elapsed = now - getLastEdited(); - qCDebug(entities) << "FlatImageEntity::setProperties() AFTER update... edited AGO=" << elapsed << + qCDebug(entities) << "ImageEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties.getLastEdited()); @@ -61,13 +61,13 @@ bool FlatImageEntity::setProperties(const EntityItemProperties& properties) { } // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time -EntityPropertyFlags FlatImageEntity::getEntityProperties(EncodeBitstreamParams& params) const { +EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); return requestedProperties; } -void FlatImageEntity::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, +void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, @@ -80,7 +80,7 @@ void FlatImageEntity::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_SHAPE, "Quad"); } -int FlatImageEntity::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { @@ -91,7 +91,7 @@ int FlatImageEntity::readEntitySubclassDataFromBuffer(const unsigned char* data, return bytesRead; } -void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { +/*void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { const float MAX_FLAT_DIMENSION = 0.0001f; if (value.y > MAX_FLAT_DIMENSION) { // enforce flatness in Y @@ -101,9 +101,23 @@ void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { } else { EntityItem::setUnscaledDimensions(value); } +}*/ + +void ImageEntityItem::setImageURL(const QString& value) { + withWriteLock([&] { + if (_imageURL != value) { + auto newURL = QUrl::fromUserInput(value); + + if (newURL.isValid()) { + _imageURL = newURL.toDisplayString(); + } else { + qCDebug(entities) << "Clearing image entity source URL since" << value << "cannot be parsed to a valid URL."; + } + } + }); } -QString FlatImageEntity::getImageURL() const { +QString ImageEntityItem::getImageURL() const { return resultWithReadLock([&] { return _imageURL; }); diff --git a/libraries/entities/src/FlatImageEntity.h b/libraries/entities/src/ImageEntityItem.h similarity index 85% rename from libraries/entities/src/FlatImageEntity.h rename to libraries/entities/src/ImageEntityItem.h index 4dd91b1215..55e8fc7b36 100644 --- a/libraries/entities/src/FlatImageEntity.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -1,5 +1,5 @@ // -// FlatImageEntity.h +// ImageEntityItem.h // libraries/entities/src // // Created by Elisa Lupin-Jimenez on 1/3/18. @@ -9,16 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_FlatImageEntity_h -#define hifi_FlatImageEntity_h +#ifndef hifi_ImageEntityItem_h +#define hifi_ImageEntityItem_h #include "EntityItem.h" -class FlatImageEntity : public EntityItem { +class ImageEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - FlatImageEntity(const EntityItemID& entityItemID); + ImageEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated @@ -43,6 +43,7 @@ public: static const QString DEFAULT_IMAGE_URL; + virtual void setImageURL(const QString& value); QString getImageURL() const; protected: @@ -50,4 +51,4 @@ protected: }; -#endif // hifi_FlatImageEntity_h +#endif // hifi_ImageEntityItem_h diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 4241468de1..bcd39ab0a5 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -287,7 +287,7 @@ var toolBar = (function () { properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); } - print("properties.type: " + properties.type); + print("check 1 type: " + properties.type); entityID = Entities.addEntity(properties); if (properties.type === "ParticleEffect") { From 72d8f90ec1b8950dc06bacff76ff84c114df07f7 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 11 Jan 2018 14:43:19 -0800 Subject: [PATCH 041/121] not sure why entities don't render with these changes --- libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityTypes.cpp | 2 +- libraries/entities/src/ImageEntityItem.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5bd6836336..e121332374 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -67,6 +67,7 @@ class EntityItemProperties { 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 ImageEntityItem; 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 diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 2e4a0e89a9..47ec408374 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -91,7 +91,7 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const EntityItemPointer newEntityItem = NULL; EntityTypeFactory factory = NULL; if (entityType >= 0 && entityType <= LAST) { - qCDebug(entities) << "check 4 type: " << entityType; + qCDebug(entities) << "check 4 type: " << entityType << ", name: " << properties.getName(); factory = _factories[entityType]; } if (factory) { diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index a94374f7b5..2e6432ba19 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -24,7 +24,7 @@ #include "ShapeEntityItem.h" #include "ImageEntityItem.h" -const QString ImageEntityItem::DEFAULT_IMAGE_URL = QString(""); +const QString ImageEntityItem::DEFAULT_IMAGE_URL(""); EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new ImageEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); From 3d000d3d011396aee7ae664d8c64f7f2ffaf77d8 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 11 Jan 2018 16:11:01 -0800 Subject: [PATCH 042/121] changed packet number --- libraries/networking/src/udt/PacketHeaders.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index c48c6bfc0b..1ceb9b8e73 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -31,7 +31,6 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityData: case PacketType::EntityPhysics: return static_cast(EntityVersion::SoftEntities); - case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::RemovedJurisdictions); case PacketType::AvatarIdentity: From 6f76650789ba9454e8d8634098e7519d1a3daa43 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 16 Jan 2018 12:17:39 -0800 Subject: [PATCH 043/121] updated with master --- libraries/entities/src/ImageEntityItem.cpp | 9 +++++++-- libraries/entities/src/ImageEntityItem.h | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index 2e6432ba19..41bd55ee85 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -38,6 +38,8 @@ ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem( EntityItemProperties ImageEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class + COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); + // Using "Quad" shape as defined in ShapeEntityItem.cpp properties.setShape("Quad"); return properties; } @@ -45,7 +47,7 @@ EntityItemProperties ImageEntityItem::getProperties(EntityPropertyFlags desiredP bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - //SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL); if (somethingChanged) { bool wantDebug = false; @@ -63,7 +65,7 @@ bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - + requestedProperties += PROP_IMAGE_URL; return requestedProperties; } @@ -78,6 +80,7 @@ void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit bool successPropertyFits = true; // Using "Quad" shape as defined in ShapeEntityItem.cpp APPEND_ENTITY_PROPERTY(PROP_SHAPE, "Quad"); + APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, _imageURL); } int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -88,6 +91,8 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; + READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL); + return bytesRead; } diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index 55e8fc7b36..229b6f190f 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -13,6 +13,7 @@ #define hifi_ImageEntityItem_h #include "EntityItem.h" +#include "ShapeEntityItem.h" class ImageEntityItem : public EntityItem { public: @@ -49,6 +50,9 @@ public: protected: QString _imageURL; + entity::Shape _shape{ entity::Shape::Quad }; + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_BOX }; + }; #endif // hifi_ImageEntityItem_h From c9c55af66153882bc6fec49cbfd736b2d3308bf8 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 16 Jan 2018 17:37:49 -0800 Subject: [PATCH 044/121] setting up properties page (not complete) --- libraries/entities/src/ImageEntityItem.cpp | 16 +++++++++++++--- libraries/entities/src/ImageEntityItem.h | 4 ++++ scripts/system/html/entityProperties.html | 9 +++++++++ scripts/system/html/js/entityProperties.js | 11 +++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index 41bd55ee85..1a6ab1e146 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -79,7 +79,6 @@ void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit bool successPropertyFits = true; // Using "Quad" shape as defined in ShapeEntityItem.cpp - APPEND_ENTITY_PROPERTY(PROP_SHAPE, "Quad"); APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, _imageURL); } @@ -96,7 +95,7 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, return bytesRead; } -/*void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { +void ImageEntityItem::setUnscaledDimensions(const glm::vec3& value) { const float MAX_FLAT_DIMENSION = 0.0001f; if (value.y > MAX_FLAT_DIMENSION) { // enforce flatness in Y @@ -106,7 +105,7 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, } else { EntityItem::setUnscaledDimensions(value); } -}*/ +} void ImageEntityItem::setImageURL(const QString& value) { withWriteLock([&] { @@ -128,3 +127,14 @@ QString ImageEntityItem::getImageURL() const { }); } +/*void ImageEntityItem::computeShapeInfo(ShapeInfo& info) { + // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc) + // is set. + + EntityItem::computeShapeInfo(info); +}*/ + +// This value specifies how the shape should be treated by physics calculations. +ShapeType ImageEntityItem::getShapeType() const { + return _collisionShapeType; +} diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index 229b6f190f..f3f4fbe6c7 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -42,11 +42,15 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + void setUnscaledDimensions(const glm::vec3& value) override; static const QString DEFAULT_IMAGE_URL; virtual void setImageURL(const QString& value); QString getImageURL() const; + //virtual void computeShapeInfo(ShapeInfo& info) override; + virtual ShapeType getShapeType() const override; + protected: QString _imageURL; diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index b93974ee77..6e8178d408 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -714,6 +714,15 @@ +
+ + ImageM + +
+ + +
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 7008d0df66..05a1b2576b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -133,6 +133,11 @@ function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { }; } +function createImageURLUpdateFunction(propertyName) { + return function() { + updateProperty(propertyName, this.value); + } +} function createEmitTextPropertyUpdateFunction(propertyName) { return function() { @@ -621,6 +626,8 @@ function loaded() { var elModelTextures = document.getElementById("property-model-textures"); var elModelOriginalTextures = document.getElementById("property-model-original-textures"); + var elImageURL = document.getElementById("property-image-url"); + var elWebSourceURL = document.getElementById("property-web-source-url"); var elWebDPI = document.getElementById("property-web-dpi"); @@ -985,6 +992,8 @@ function loaded() { } else if (properties.type === "Web") { elWebSourceURL.value = properties.sourceUrl; elWebDPI.value = properties.dpi; + } else if (properties.type === "Image") { + elImageURL.value = properties.imageURL; } else if (properties.type === "Text") { elTextText.value = properties.text; elTextLineHeight.value = properties.lineHeight.toFixed(4); @@ -1352,6 +1361,8 @@ function loaded() { elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); + elImageURL.addEventListener('change', createImageURLUpdateFunction('imageURL')); + elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi', 0)); From ecb53192add37a7bb70c88d2c00b6568aab0db7d Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 17 Jan 2018 11:57:40 -0800 Subject: [PATCH 045/121] edit properties reflect image members --- scripts/system/html/css/edit-style.css | 49 +++++++++++++++++++++- scripts/system/html/js/entityProperties.js | 7 ++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 736d42d593..58acc317bd 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -448,7 +448,7 @@ input[type=checkbox]:checked + label:hover { border: 1.5pt solid black; } -.shape-section, .light-section, .model-section, .web-section, .hyperlink-section, .text-section, .zone-section { +.shape-section, .light-section, .model-section, .web-section, .image-section, .hyperlink-section, .text-section, .zone-section { display: table; } @@ -564,6 +564,7 @@ hr { .text-group[collapsed="true"] ~ .text-group, .zone-group[collapsed="true"] ~ .zone-group, +.image-group[collapsed="true"] ~ .image-group, .web-group[collapsed="true"] ~ .web-group, .hyperlink-group[collapsed="true"] ~ .hyperlink-group, .spatial-group[collapsed="true"] ~ .spatial-group, @@ -1466,6 +1467,9 @@ input#reset-to-natural-dimensions { #properties-list.ShapeMenu #text, #properties-list.BoxMenu #text, #properties-list.SphereMenu #text, +#properties-list.ShapeMenu #image, +#properties-list.BoxMenu #image, +#properties-list.SphereMenu #image, #properties-list.ShapeMenu #web, #properties-list.BoxMenu #web, #properties-list.SphereMenu #web { @@ -1497,6 +1501,7 @@ input#reset-to-natural-dimensions { #properties-list.ParticleEffectMenu #shape-list, #properties-list.ParticleEffectMenu #text, #properties-list.ParticleEffectMenu #web, +#properties-list.ParticleEffectMenu #image, #properties-list.ParticleEffectMenu #zone { display: none; } @@ -1527,6 +1532,7 @@ input#reset-to-natural-dimensions { #properties-list.LightMenu #model, #properties-list.LightMenu #zone, #properties-list.LightMenu #text, +#properties-list.LightMenu #image, #properties-list.LightMenu #web { display: none; } @@ -1563,6 +1569,7 @@ input#reset-to-natural-dimensions { #properties-list.ModelMenu #light, #properties-list.ModelMenu #zone, #properties-list.ModelMenu #text, +#properties-list.ModelMenu #image, #properties-list.ModelMenu #web { display: none; } @@ -1599,6 +1606,7 @@ input#reset-to-natural-dimensions { #properties-list.ZoneMenu #light, #properties-list.ZoneMenu #model, #properties-list.ZoneMenu #text, +#properties-list.ZoneMenu #image, #properties-list.ZoneMenu #web { display: none; } @@ -1609,6 +1617,43 @@ input#reset-to-natural-dimensions { } +/* ----- Order of Menu items for Image ----- */ +#properties-list.ImageMenu #general { + order: 1; +} +#properties-list.ImageMenu #image { + order: 2; +} +#properties-list.ImageMenu #collision-info { + order: 3; +} +#properties-list.ImageMenu #physical { + order: 4; +} +#properties-list.ImageMenu #spatial { + order: 5; +} +#properties-list.ImageMenu #behavior { + order: 6; +} +#properties-list.ImageMenu #hyperlink { + order: 7; +} +/* sections to hide */ +#properties-list.ImageMenu #light, +#properties-list.ImageMenu #model, +#properties-list.ImageMenu #zone, +#properties-list.ImageMenu #web, +#properties-list.ImageMenu #text { + display: none; +} +/* items to hide */ +#properties-list.ImageMenu #shape-list, +#properties-list.ImageMenu #base-color-section { + display: none; +} + + /* ----- Order of Menu items for Web ----- */ #properties-list.WebMenu #general { order: 1; @@ -1635,6 +1680,7 @@ input#reset-to-natural-dimensions { #properties-list.WebMenu #light, #properties-list.WebMenu #model, #properties-list.WebMenu #zone, +#properties-list.WebMenu #image, #properties-list.WebMenu #text { display: none; } @@ -1672,6 +1718,7 @@ input#reset-to-natural-dimensions { #properties-list.TextMenu #light, #properties-list.TextMenu #model, #properties-list.TextMenu #zone, +#properties-list.TextMenu #image, #properties-list.TextMenu #web { display: none; } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 05a1b2576b..54b50de106 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -19,6 +19,7 @@ var ICON_FOR_TYPE = { ParticleEffect: "", Model: "", Web: "q", + Image: "q", // what do? Text: "l", Light: "p", Zone: "o", @@ -134,9 +135,9 @@ function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { } function createImageURLUpdateFunction(propertyName) { - return function() { + return function () { updateProperty(propertyName, this.value); - } + }; } function createEmitTextPropertyUpdateFunction(propertyName) { @@ -1735,7 +1736,7 @@ function loaded() { } // Dropdowns - // For each dropdown the following replacement is created in place of the oriringal dropdown... + // For each dropdown the following replacement is created in place of the original dropdown... // Structure created: //
//
display textcarat
From dc5f29aa58268a9abdc9b82f6a9bc41828c28832 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 17 Jan 2018 17:57:09 -0800 Subject: [PATCH 046/121] entity item properties hooked up for image --- libraries/entities/src/EntityItemProperties.cpp | 9 +++++++++ scripts/system/edit.js | 2 +- scripts/system/html/js/entityProperties.js | 3 ++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index fc8ca18fb6..3729c75f3d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -350,6 +350,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); + CHECK_PROPERTY_CHANGE(PROP_IMAGE_URL, imageURL); + CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); CHECK_PROPERTY_CHANGE(PROP_VOXEL_DATA, voxelData); @@ -785,6 +787,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE(imageURL, QString, setImageURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelData, QByteArray, setVoxelData); @@ -945,6 +949,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); + COPY_PROPERTY_IF_CHANGED(imageURL); + COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); COPY_PROPERTY_IF_CHANGED(voxelData); @@ -1138,6 +1144,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); + ADD_PROPERTY_TO_MAP(PROP_IMAGE_URL, ImageURL, imageURL, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); @@ -2060,6 +2067,8 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _haze.markAllChanged(); + _imageURLChanged = true; + _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; _voxelDataChanged = true; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index bcd39ab0a5..5f976e07d3 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -546,7 +546,7 @@ var toolBar = (function () { createNewEntity({ type: "Image", dimensions: DEFAULT_DIMENSIONS, - sourceUrl: "https://highfidelity.com/" + imageURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }); }); diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 54b50de106..7c3af548ba 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -19,7 +19,7 @@ var ICON_FOR_TYPE = { ParticleEffect: "", Model: "", Web: "q", - Image: "q", // what do? + Image: "q", // change this when image type icon added Text: "l", Light: "p", Zone: "o", @@ -995,6 +995,7 @@ function loaded() { elWebDPI.value = properties.dpi; } else if (properties.type === "Image") { elImageURL.value = properties.imageURL; + //elImageURL.value = properties.sourceURL; } else if (properties.type === "Text") { elTextText.value = properties.text; elTextLineHeight.value = properties.lineHeight.toFixed(4); From 576d683d3bb62baef14c4ddc2f3696ca71679258 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 18 Jan 2018 14:40:33 -0800 Subject: [PATCH 047/121] initial changes to incorporate snap model --- libraries/entities/src/EntityScriptingInterface.cpp | 2 -- libraries/entities/src/EntityTree.cpp | 1 - libraries/entities/src/EntityTypes.cpp | 4 +++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index cab4251d97..4342f0e683 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -266,8 +266,6 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties bool success = true; if (_entityTree) { _entityTree->withWriteLock([&] { - propertiesWithSimID.getType(); - qCDebug(entities) << "check 2 type: " << propertiesWithSimID.getType(); EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); if (entity) { if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 56c3aa216c..bf29f3bec9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -514,7 +514,6 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti // construct the instance of the entity EntityTypes::EntityType type = props.getType(); - qCDebug(entities) << "check 3 type: " << type; result = EntityTypes::constructEntityItem(type, entityID, props); if (result) { diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 47ec408374..6326df3cc1 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -91,7 +91,9 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const EntityItemPointer newEntityItem = NULL; EntityTypeFactory factory = NULL; if (entityType >= 0 && entityType <= LAST) { - qCDebug(entities) << "check 4 type: " << entityType << ", name: " << properties.getName(); + if (getEntityTypeName(entityType) == "Image") { + entityType = getEntityTypeFromName("Web"); + } factory = _factories[entityType]; } if (factory) { From ceb621a52151d20bfa66dd4eb09779c45f003901 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Mon, 22 Jan 2018 11:36:58 -0800 Subject: [PATCH 048/121] reverted protocol change for images --- libraries/entities/src/EntityItemProperties.cpp | 16 ++++++++-------- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityPropertyFlags.h | 4 ++-- libraries/entities/src/EntityTypes.cpp | 7 ++----- libraries/entities/src/EntityTypes.h | 2 +- libraries/entities/src/ImageEntityItem.cpp | 8 ++++++-- libraries/entities/src/ImageEntityItem.h | 4 ++++ scripts/system/edit.js | 10 +++++++++- 8 files changed, 33 insertions(+), 20 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 3729c75f3d..48ad9601ba 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -350,7 +350,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); - CHECK_PROPERTY_CHANGE(PROP_IMAGE_URL, imageURL); + //CHECK_PROPERTY_CHANGE(PROP_IMAGE_URL, imageURL); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -593,9 +593,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool } // Image only - if (_type == EntityTypes::Image) { + /*if (_type == EntityTypes::Image) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); - } + }*/ // Web only if (_type == EntityTypes::Web) { @@ -1144,7 +1144,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); - ADD_PROPERTY_TO_MAP(PROP_IMAGE_URL, ImageURL, imageURL, QString); + //ADD_PROPERTY_TO_MAP(PROP_IMAGE_URL, ImageURL, imageURL, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); @@ -1381,9 +1381,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); } - if (properties.getType() == EntityTypes::Image) { + /*if (properties.getType() == EntityTypes::Image) { APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); - } + }*/ if (properties.getType() == EntityTypes::Text) { APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); @@ -1746,9 +1746,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); } - if (properties.getType() == EntityTypes::Image) { + /*if (properties.getType() == EntityTypes::Image) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); - } + }*/ if (properties.getType() == EntityTypes::Text) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index e121332374..14544cd4d1 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -67,7 +67,7 @@ class EntityItemProperties { 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 ImageEntityItem; + //friend class ImageEntityItem; 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 diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 4768ebed86..ec8f4e08c8 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -42,8 +42,8 @@ enum EntityPropertyList { PROP_ANIMATION_ALLOW_TRANSLATION, PROP_RELAY_PARENT_JOINTS, - // for image - PROP_IMAGE_URL, + /*// for image + PROP_IMAGE_URL,*/ // these properties are supported by the EntityItem base class PROP_REGISTRATION_POINT, diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 6326df3cc1..6bf5a176a7 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -28,7 +28,7 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -#include "ImageEntityItem.h" +//#include "ImageEntityItem.h" #include "ShapeEntityItem.h" QMap EntityTypes::_typeToNameMap; @@ -48,7 +48,7 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) -REGISTER_ENTITY_TYPE(Image) +//REGISTER_ENTITY_TYPE(Image) REGISTER_ENTITY_TYPE(Shape) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory) @@ -91,9 +91,6 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const EntityItemPointer newEntityItem = NULL; EntityTypeFactory factory = NULL; if (entityType >= 0 && entityType <= LAST) { - if (getEntityTypeName(entityType) == "Image") { - entityType = getEntityTypeFromName("Web"); - } factory = _factories[entityType]; } if (factory) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 8d986c8090..6fe4274820 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,7 +48,7 @@ public: Line, PolyVox, PolyLine, - Image, + //Image, Shape, LAST = Shape } EntityType; diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index 1a6ab1e146..e30d85257e 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* NOT IN USE + #include #include @@ -127,14 +129,16 @@ QString ImageEntityItem::getImageURL() const { }); } -/*void ImageEntityItem::computeShapeInfo(ShapeInfo& info) { +void ImageEntityItem::computeShapeInfo(ShapeInfo& info) { // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc) // is set. EntityItem::computeShapeInfo(info); -}*/ +} // This value specifies how the shape should be treated by physics calculations. ShapeType ImageEntityItem::getShapeType() const { return _collisionShapeType; } + +*/ diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index f3f4fbe6c7..276d21e6f0 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* NOT IN USE + #ifndef hifi_ImageEntityItem_h #define hifi_ImageEntityItem_h @@ -60,3 +62,5 @@ protected: }; #endif // hifi_ImageEntityItem_h + +*/ diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 5f976e07d3..1d02fed787 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -540,7 +540,7 @@ var toolBar = (function () { }); }); - // for image button + /*// for image button addButton("newImageButton", "web-01.svg", function () { print("new image message is received"); createNewEntity({ @@ -548,6 +548,14 @@ var toolBar = (function () { dimensions: DEFAULT_DIMENSIONS, imageURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }); + });*/ + addButton("newImageButton", "web-01.svg", function () { + createNewEntity({ + type: "Model", + dimensions: DEFAULT_DIMENSIONS, + modelURL: "http://hifi-content.s3.amazonaws.com/alan/dev/Test/snapshot.fbx", + textures: JSON.stringify({ "tex.picture": "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }) + }); }); addButton("newWebButton", "web-01.svg", function () { From 308e481e63f5d19db4f8548c3a8689d60a22958f Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Mon, 22 Jan 2018 17:46:12 -0800 Subject: [PATCH 049/121] switch to image property list not working --- scripts/system/edit.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 1d02fed787..6f48e1167a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -252,7 +252,7 @@ var toolBar = (function () { properties.rotation = MyAvatar.orientation; // added image here - var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Image", "Web"]; + var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"]; if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box prior to creating it. @@ -287,7 +287,6 @@ var toolBar = (function () { properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); } - print("check 1 type: " + properties.type); entityID = Entities.addEntity(properties); if (properties.type === "ParticleEffect") { @@ -540,20 +539,21 @@ var toolBar = (function () { }); }); - /*// for image button - addButton("newImageButton", "web-01.svg", function () { - print("new image message is received"); - createNewEntity({ - type: "Image", - dimensions: DEFAULT_DIMENSIONS, - imageURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" - }); - });*/ addButton("newImageButton", "web-01.svg", function () { createNewEntity({ type: "Model", - dimensions: DEFAULT_DIMENSIONS, + // make constant for this later + dimensions: { + x: 4.16, + y: 0.02, + z: 2.58 + }, modelURL: "http://hifi-content.s3.amazonaws.com/alan/dev/Test/snapshot.fbx", + imageURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg", + // will this work? + /*get textures() { + return JSON.stringify({ "tex.picture": this.imageURL }); + }*/ textures: JSON.stringify({ "tex.picture": "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }) }); }); From 42151b8fd47f6b32d3662d9d599096ec4d290408 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 23 Jan 2018 14:50:41 -0800 Subject: [PATCH 050/121] creating new image entity opens image property options --- .../entities/src/EntityItemProperties.cpp | 12 +- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/ImageEntityItem.cpp | 2 +- scripts/system/edit.js | 6 +- scripts/system/html/js/entityProperties.js | 123 ++++++++++++++++++ 5 files changed, 136 insertions(+), 9 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 48ad9601ba..7fcfc00ee8 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -592,8 +592,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); } - // Image only - /*if (_type == EntityTypes::Image) { + + + /*// Image only + if (_type == EntityTypes::Image) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); }*/ @@ -787,7 +789,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE(imageURL, QString, setImageURL); + //COPY_PROPERTY_FROM_QSCRIPTVALUE(imageURL, QString, setImageURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); @@ -949,7 +951,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); - COPY_PROPERTY_IF_CHANGED(imageURL); + //COPY_PROPERTY_IF_CHANGED(imageURL); COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -2067,7 +2069,7 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _haze.markAllChanged(); - _imageURLChanged = true; + //_imageURLChanged = true; _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 14544cd4d1..5ca28a12c5 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -195,7 +195,7 @@ public: DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); - DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); + //DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index e30d85257e..9969a39ed0 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -35,7 +35,7 @@ EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const E } ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Image; + //_type = EntityTypes::Image; } EntityItemProperties ImageEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6f48e1167a..a5c236a6c9 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -253,6 +253,8 @@ var toolBar = (function () { // added image here var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"]; + //var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Image", "Web"]; + if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box prior to creating it. @@ -539,6 +541,7 @@ var toolBar = (function () { }); }); + // for image button addButton("newImageButton", "web-01.svg", function () { createNewEntity({ type: "Model", @@ -548,8 +551,7 @@ var toolBar = (function () { y: 0.02, z: 2.58 }, - modelURL: "http://hifi-content.s3.amazonaws.com/alan/dev/Test/snapshot.fbx", - imageURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg", + modelURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx", // will this work? /*get textures() { return JSON.stringify({ "tex.picture": this.imageURL }); diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 7c3af548ba..2b90f8ddfa 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -511,6 +511,10 @@ function loaded() { var elPropertiesList = document.getElementById("properties-list"); var elID = document.getElementById("property-id"); var elType = document.getElementById("property-type"); +<<<<<<< HEAD +======= + debugPrint("the type is: " + JSON.stringify(elType)); +>>>>>>> creating new image entity opens image property options var elTypeIcon = document.getElementById("type-icon"); var elName = document.getElementById("property-name"); var elLocked = document.getElementById("property-locked"); @@ -661,16 +665,28 @@ function loaded() { var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); // Skybox +<<<<<<< HEAD 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 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"); +>>>>>>> creating new image entity opens image property options // Ambient light var elCopySkyboxURLToAmbientURL = document.getElementById("copy-skybox-url-to-ambient-url"); +<<<<<<< HEAD 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 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"); +>>>>>>> creating new image entity opens image property options var elZoneAmbientLightIntensity = document.getElementById("property-zone-key-ambient-intensity"); var elZoneAmbientLightURL = document.getElementById("property-zone-key-ambient-url"); @@ -787,7 +803,11 @@ function loaded() { } else { properties = data.selections[0].properties; +<<<<<<< HEAD +======= + debugPrint("props: " + JSON.stringify(properties)); +>>>>>>> creating new image entity opens image property options if (lastEntityID !== '"' + properties.id + '"' && lastEntityID !== null && editor !== null) { saveJSONUserData(true); } @@ -796,6 +816,14 @@ function loaded() { lastEntityID = '"' + properties.id + '"'; elID.value = properties.id; +<<<<<<< HEAD +======= + // image is not yet a separate entity type + if (properties.type === "Model" && properties.modelURL === "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx") { + properties.type = "Image"; + } + +>>>>>>> creating new image entity opens image property options // Create class name for css ruleset filtering elPropertiesList.className = properties.type + 'Menu'; @@ -1024,9 +1052,15 @@ function loaded() { } else if (properties.type === "Zone") { // Key light +<<<<<<< HEAD elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); elZoneKeyLightModeDisabled.checked = (properties.keyLightMode === 'disabled'); elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); +======= + elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); + elZoneKeyLightModeDisabled.checked = (properties.keyLightMode === 'disabled'); + elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); +>>>>>>> creating new image entity opens image property options elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; @@ -1038,6 +1072,7 @@ function loaded() { elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); // Skybox +<<<<<<< HEAD elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); @@ -1046,14 +1081,30 @@ function loaded() { elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); elZoneAmbientLightModeDisabled.checked = (properties.ambientLightMode === 'disabled'); elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); +======= + elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); + elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); + elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); + + // Ambient light + elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); + elZoneAmbientLightModeDisabled.checked = (properties.ambientLightMode === 'disabled'); + elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); +>>>>>>> creating new image entity opens image property options elZoneAmbientLightIntensity.value = properties.ambientLight.ambientIntensity.toFixed(2); elZoneAmbientLightURL.value = properties.ambientLight.ambientURL; // Haze +<<<<<<< HEAD elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled'); elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); +======= + elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); + elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled'); + elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); +>>>>>>> creating new image entity opens image property options elZoneHazeRange.value = properties.haze.hazeRange.toFixed(0); elZoneHazeColor.style.backgroundColor = "rgb(" + @@ -1319,15 +1370,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-color-control2').attr('active', 'true'); }, onHide: function(colpick) { $('#property-color-control2').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } })); @@ -1343,15 +1403,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-light-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-light-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } })); @@ -1400,15 +1469,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-text-text-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-text-text-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options $(el).attr('active', 'false'); emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); } @@ -1424,15 +1502,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-text-background-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-text-background-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); } })); @@ -1449,15 +1536,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-key-light-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-key-light-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); } })); @@ -1518,15 +1614,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-haze-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-haze-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('hazeColor', rgb.r, rgb.g, rgb.b, 'haze'); } })); @@ -1543,15 +1648,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('hazeGlareColor', rgb.r, rgb.g, rgb.b, 'haze'); } })); @@ -1585,15 +1699,24 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', +<<<<<<< HEAD submit: false, // We don't want to have a submission button +======= +>>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-skybox-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-skybox-color').attr('active', 'false'); }, +<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); +======= + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); +>>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); } })); From fe3bc00baa0cb1bcaaa3090e5ca3e438ba47874a Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 23 Jan 2018 16:45:54 -0800 Subject: [PATCH 051/121] loading and rendering atp and http image files --- scripts/system/html/js/entityProperties.js | 131 ++------------------- 1 file changed, 7 insertions(+), 124 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 2b90f8ddfa..e7526fa2e0 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -136,7 +136,8 @@ function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { function createImageURLUpdateFunction(propertyName) { return function () { - updateProperty(propertyName, this.value); + var newTextures = JSON.stringify({ "tex.picture": this.value }); + updateProperty(propertyName, newTextures); }; } @@ -511,10 +512,6 @@ function loaded() { var elPropertiesList = document.getElementById("properties-list"); var elID = document.getElementById("property-id"); var elType = document.getElementById("property-type"); -<<<<<<< HEAD -======= - debugPrint("the type is: " + JSON.stringify(elType)); ->>>>>>> creating new image entity opens image property options var elTypeIcon = document.getElementById("type-icon"); var elName = document.getElementById("property-name"); var elLocked = document.getElementById("property-locked"); @@ -665,28 +662,16 @@ function loaded() { var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); // Skybox -<<<<<<< HEAD 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 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"); ->>>>>>> creating new image entity opens image property options // Ambient light var elCopySkyboxURLToAmbientURL = document.getElementById("copy-skybox-url-to-ambient-url"); -<<<<<<< HEAD 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 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"); ->>>>>>> creating new image entity opens image property options var elZoneAmbientLightIntensity = document.getElementById("property-zone-key-ambient-intensity"); var elZoneAmbientLightURL = document.getElementById("property-zone-key-ambient-url"); @@ -803,11 +788,6 @@ function loaded() { } else { properties = data.selections[0].properties; -<<<<<<< HEAD - -======= - debugPrint("props: " + JSON.stringify(properties)); ->>>>>>> creating new image entity opens image property options if (lastEntityID !== '"' + properties.id + '"' && lastEntityID !== null && editor !== null) { saveJSONUserData(true); } @@ -816,14 +796,11 @@ function loaded() { lastEntityID = '"' + properties.id + '"'; elID.value = properties.id; -<<<<<<< HEAD -======= - // image is not yet a separate entity type + // HTML workaround since image is not yet a separate entity type if (properties.type === "Model" && properties.modelURL === "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx") { properties.type = "Image"; } ->>>>>>> creating new image entity opens image property options // Create class name for css ruleset filtering elPropertiesList.className = properties.type + 'Menu'; @@ -1022,8 +999,9 @@ function loaded() { elWebSourceURL.value = properties.sourceUrl; elWebDPI.value = properties.dpi; } else if (properties.type === "Image") { - elImageURL.value = properties.imageURL; - //elImageURL.value = properties.sourceURL; + var imageLink = JSON.parse(properties.textures)["tex.picture"]; + debugPrint("image url is: " + imageLink); + elImageURL.value = imageLink; } else if (properties.type === "Text") { elTextText.value = properties.text; elTextLineHeight.value = properties.lineHeight.toFixed(4); @@ -1052,15 +1030,9 @@ function loaded() { } else if (properties.type === "Zone") { // Key light -<<<<<<< HEAD elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); elZoneKeyLightModeDisabled.checked = (properties.keyLightMode === 'disabled'); elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); -======= - elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); - elZoneKeyLightModeDisabled.checked = (properties.keyLightMode === 'disabled'); - elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); ->>>>>>> creating new image entity opens image property options elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; @@ -1072,7 +1044,6 @@ function loaded() { elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); // Skybox -<<<<<<< HEAD elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); @@ -1081,30 +1052,14 @@ function loaded() { elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); elZoneAmbientLightModeDisabled.checked = (properties.ambientLightMode === 'disabled'); elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); -======= - elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); - elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); - elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); - - // Ambient light - elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); - elZoneAmbientLightModeDisabled.checked = (properties.ambientLightMode === 'disabled'); - elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); ->>>>>>> creating new image entity opens image property options elZoneAmbientLightIntensity.value = properties.ambientLight.ambientIntensity.toFixed(2); elZoneAmbientLightURL.value = properties.ambientLight.ambientURL; // Haze -<<<<<<< HEAD elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled'); elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); -======= - elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); - elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled'); - elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); ->>>>>>> creating new image entity opens image property options elZoneHazeRange.value = properties.haze.hazeRange.toFixed(0); elZoneHazeColor.style.backgroundColor = "rgb(" + @@ -1370,24 +1325,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-color-control2').attr('active', 'true'); }, onHide: function(colpick) { $('#property-color-control2').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } })); @@ -1403,24 +1349,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-light-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-light-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } })); @@ -1432,7 +1369,7 @@ function loaded() { elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); - elImageURL.addEventListener('change', createImageURLUpdateFunction('imageURL')); + elImageURL.addEventListener('change', createImageURLUpdateFunction('textures')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi', 0)); @@ -1469,24 +1406,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-text-text-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-text-text-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options $(el).attr('active', 'false'); emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); } @@ -1502,24 +1430,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-text-background-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-text-background-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); } })); @@ -1536,24 +1455,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-key-light-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-key-light-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); } })); @@ -1614,24 +1524,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-haze-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-haze-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('hazeColor', rgb.r, rgb.g, rgb.b, 'haze'); } })); @@ -1648,24 +1549,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('hazeGlareColor', rgb.r, rgb.g, rgb.b, 'haze'); } })); @@ -1699,24 +1591,15 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', -<<<<<<< HEAD submit: false, // We don't want to have a submission button -======= ->>>>>>> creating new image entity opens image property options onShow: function(colpick) { $('#property-zone-skybox-color').attr('active', 'true'); }, onHide: function(colpick) { $('#property-zone-skybox-color').attr('active', 'false'); }, -<<<<<<< HEAD onChange: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); -======= - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); ->>>>>>> creating new image entity opens image property options emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); } })); From d390e20139efdd55dea99abe44959bbf2e203a46 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 24 Jan 2018 11:31:47 -0800 Subject: [PATCH 052/121] removed extraneous commenting and image class --- .../resources/qml/hifi/tablet/EditTabView.qml | 1 - .../entities/src/EntityItemProperties.cpp | 24 --- libraries/entities/src/EntityItemProperties.h | 2 - libraries/entities/src/EntityPropertyFlags.h | 3 - libraries/entities/src/EntityTypes.cpp | 2 - libraries/entities/src/EntityTypes.h | 1 - libraries/entities/src/ImageEntityItem.cpp | 144 ------------------ libraries/entities/src/ImageEntityItem.h | 66 -------- scripts/system/edit.js | 9 +- scripts/system/html/js/entityProperties.js | 1 - 10 files changed, 1 insertion(+), 252 deletions(-) delete mode 100644 libraries/entities/src/ImageEntityItem.cpp delete mode 100644 libraries/entities/src/ImageEntityItem.h diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index 482469d355..ea5762d00a 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -101,7 +101,6 @@ TabView { } } - // for image NewEntityButton { icon: "icons/create-icons/25-web-1-01.svg" text: "IMAGE" diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7fcfc00ee8..e2a5ddf8b5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -350,8 +350,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); - //CHECK_PROPERTY_CHANGE(PROP_IMAGE_URL, imageURL); - CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); CHECK_PROPERTY_CHANGE(PROP_VOXEL_DATA, voxelData); @@ -592,13 +590,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); } - - - /*// Image only - if (_type == EntityTypes::Image) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); - }*/ - // Web only if (_type == EntityTypes::Web) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); @@ -789,8 +780,6 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); - //COPY_PROPERTY_FROM_QSCRIPTVALUE(imageURL, QString, setImageURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelData, QByteArray, setVoxelData); @@ -951,8 +940,6 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); - //COPY_PROPERTY_IF_CHANGED(imageURL); - COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); COPY_PROPERTY_IF_CHANGED(voxelData); @@ -1146,7 +1133,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); - //ADD_PROPERTY_TO_MAP(PROP_IMAGE_URL, ImageURL, imageURL, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); @@ -1383,10 +1369,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); } - /*if (properties.getType() == EntityTypes::Image) { - APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); - }*/ - if (properties.getType() == EntityTypes::Text) { APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); @@ -1748,10 +1730,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); } - /*if (properties.getType() == EntityTypes::Image) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); - }*/ - if (properties.getType() == EntityTypes::Text) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); @@ -2069,8 +2047,6 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _haze.markAllChanged(); - //_imageURLChanged = true; - _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; _voxelDataChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5ca28a12c5..3e0770f386 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -67,7 +67,6 @@ class EntityItemProperties { 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 ImageEntityItem; 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 @@ -195,7 +194,6 @@ public: DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); - //DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ec8f4e08c8..90438ab01c 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -42,9 +42,6 @@ enum EntityPropertyList { PROP_ANIMATION_ALLOW_TRANSLATION, PROP_RELAY_PARENT_JOINTS, - /*// for image - PROP_IMAGE_URL,*/ - // these properties are supported by the EntityItem base class PROP_REGISTRATION_POINT, PROP_ANGULAR_VELOCITY, diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 6bf5a176a7..cb17c28fd7 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -28,7 +28,6 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -//#include "ImageEntityItem.h" #include "ShapeEntityItem.h" QMap EntityTypes::_typeToNameMap; @@ -48,7 +47,6 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) -//REGISTER_ENTITY_TYPE(Image) REGISTER_ENTITY_TYPE(Shape) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory) diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 6fe4274820..316bf2b813 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,7 +48,6 @@ public: Line, PolyVox, PolyLine, - //Image, Shape, LAST = Shape } EntityType; diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp deleted file mode 100644 index 9969a39ed0..0000000000 --- a/libraries/entities/src/ImageEntityItem.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// -// ImageEntityItem.cpp -// libraries/entities/src -// -// Created by Elisa Lupin-Jimenez on 1/3/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 -// - -/* NOT IN USE - -#include - -#include -#include -#include -#include - -#include "EntitiesLogging.h" -#include "EntityItemProperties.h" -#include "EntityTree.h" -#include "EntityTreeElement.h" -#include "ResourceCache.h" -#include "ShapeEntityItem.h" -#include "ImageEntityItem.h" - -const QString ImageEntityItem::DEFAULT_IMAGE_URL(""); - -EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity(new ImageEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - //_type = EntityTypes::Image; -} - -EntityItemProperties ImageEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); - // Using "Quad" shape as defined in ShapeEntityItem.cpp - properties.setShape("Quad"); - return properties; -} - -bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL); - - if (somethingChanged) { - bool wantDebug = false; - if (wantDebug) { - uint64_t now = usecTimestampNow(); - int elapsed = now - getLastEdited(); - qCDebug(entities) << "ImageEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " getLastEdited()=" << getLastEdited(); - } - setLastEdited(properties.getLastEdited()); - } - return somethingChanged; -} - -// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time -EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_IMAGE_URL; - return requestedProperties; -} - -void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - // Using "Quad" shape as defined in ShapeEntityItem.cpp - APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, _imageURL); -} - -int ImageEntityItem::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_IMAGE_URL, QString, setImageURL); - - return bytesRead; -} - -void ImageEntityItem::setUnscaledDimensions(const glm::vec3& value) { - const float MAX_FLAT_DIMENSION = 0.0001f; - if (value.y > MAX_FLAT_DIMENSION) { - // enforce flatness in Y - glm::vec3 newDimensions = value; - newDimensions.y = MAX_FLAT_DIMENSION; - EntityItem::setUnscaledDimensions(newDimensions); - } else { - EntityItem::setUnscaledDimensions(value); - } -} - -void ImageEntityItem::setImageURL(const QString& value) { - withWriteLock([&] { - if (_imageURL != value) { - auto newURL = QUrl::fromUserInput(value); - - if (newURL.isValid()) { - _imageURL = newURL.toDisplayString(); - } else { - qCDebug(entities) << "Clearing image entity source URL since" << value << "cannot be parsed to a valid URL."; - } - } - }); -} - -QString ImageEntityItem::getImageURL() const { - return resultWithReadLock([&] { - return _imageURL; - }); -} - -void ImageEntityItem::computeShapeInfo(ShapeInfo& info) { - // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc) - // is set. - - EntityItem::computeShapeInfo(info); -} - -// This value specifies how the shape should be treated by physics calculations. -ShapeType ImageEntityItem::getShapeType() const { - return _collisionShapeType; -} - -*/ diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h deleted file mode 100644 index 276d21e6f0..0000000000 --- a/libraries/entities/src/ImageEntityItem.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// ImageEntityItem.h -// libraries/entities/src -// -// Created by Elisa Lupin-Jimenez on 1/3/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 -// - -/* NOT IN USE - -#ifndef hifi_ImageEntityItem_h -#define hifi_ImageEntityItem_h - -#include "EntityItem.h" -#include "ShapeEntityItem.h" - -class ImageEntityItem : public EntityItem { -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - ImageEntityItem(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; - - 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; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - void setUnscaledDimensions(const glm::vec3& value) override; - - static const QString DEFAULT_IMAGE_URL; - virtual void setImageURL(const QString& value); - QString getImageURL() const; - - //virtual void computeShapeInfo(ShapeInfo& info) override; - virtual ShapeType getShapeType() const override; - -protected: - QString _imageURL; - - entity::Shape _shape{ entity::Shape::Quad }; - ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_BOX }; - -}; - -#endif // hifi_ImageEntityItem_h - -*/ diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a5c236a6c9..20425c7ea6 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -251,9 +251,7 @@ var toolBar = (function () { // Align entity with Avatar orientation. properties.rotation = MyAvatar.orientation; - // added image here var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"]; - //var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Image", "Web"]; if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { @@ -541,21 +539,16 @@ var toolBar = (function () { }); }); - // for image button addButton("newImageButton", "web-01.svg", function () { createNewEntity({ type: "Model", - // make constant for this later dimensions: { x: 4.16, y: 0.02, z: 2.58 }, modelURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx", - // will this work? - /*get textures() { - return JSON.stringify({ "tex.picture": this.imageURL }); - }*/ + // change to another default image textures: JSON.stringify({ "tex.picture": "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }) }); }); diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index e7526fa2e0..59b54b1020 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1000,7 +1000,6 @@ function loaded() { elWebDPI.value = properties.dpi; } else if (properties.type === "Image") { var imageLink = JSON.parse(properties.textures)["tex.picture"]; - debugPrint("image url is: " + imageLink); elImageURL.value = imageLink; } else if (properties.type === "Text") { elTextText.value = properties.text; From bd7204e6efd507be41c4a45d3ec4e21d61d44db2 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 24 Jan 2018 12:01:59 -0800 Subject: [PATCH 053/121] image entity now has grabbable and dynamic options --- scripts/system/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 20425c7ea6..c298da38bc 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -547,6 +547,7 @@ var toolBar = (function () { y: 0.02, z: 2.58 }, + shapeType: "box", modelURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx", // change to another default image textures: JSON.stringify({ "tex.picture": "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }) From 2ff47fa454b494ce64d2afa7e4396f0233e32ea7 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 25 Jan 2018 14:39:45 -0800 Subject: [PATCH 054/121] images auto-add to asset server, started asset browser add link --- interface/resources/qml/hifi/AssetServer.qml | 3 +- interface/src/Application.cpp | 42 ++++++++++++++++---- interface/src/Application.h | 1 + 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 37c3c2adab..6a4e703413 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -142,8 +142,9 @@ Windows.ScrollingWindow { }); } + // Elisa note - need to link this with specific add entity call function canAddToWorld(path) { - var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i]; + var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i]; if (selectedItems > 1) { return false; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 99bd4d5758..b53ba61e61 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -335,15 +335,17 @@ static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; // we will never drop below the 'min' value static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1; -static const QString SNAPSHOT_EXTENSION = ".jpg"; -static const QString SVO_EXTENSION = ".svo"; +static const QString SNAPSHOT_EXTENSION = ".jpg"; +static const QString JPG_EXTENSION = ".jpg"; +static const QString PNG_EXTENSION = ".png"; +static const QString SVO_EXTENSION = ".svo"; static const QString SVO_JSON_EXTENSION = ".svo.json"; static const QString JSON_GZ_EXTENSION = ".json.gz"; static const QString JSON_EXTENSION = ".json"; -static const QString JS_EXTENSION = ".js"; -static const QString FST_EXTENSION = ".fst"; -static const QString FBX_EXTENSION = ".fbx"; -static const QString OBJ_EXTENSION = ".obj"; +static const QString JS_EXTENSION = ".js"; +static const QString FST_EXTENSION = ".fst"; +static const QString FBX_EXTENSION = ".fbx"; +static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; static const QString ZIP_EXTENSION = ".zip"; @@ -382,7 +384,9 @@ const QHash Application::_acceptedExtensi { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, { JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent }, - { ZIP_EXTENSION, &Application::importFromZIP } + { ZIP_EXTENSION, &Application::importFromZIP }, + { JPG_EXTENSION, &Application::importImage }, + { PNG_EXTENSION, &Application::importImage } }; class DeadlockWatchdogThread : public QThread { @@ -2899,6 +2903,27 @@ bool Application::importFromZIP(const QString& filePath) { return true; } +bool Application::importImage(const QString& urlString) { + qCDebug(interfaceapp) << "dragged image"; + QString filepath(urlString); + filepath.remove("file:///"); + //<> + addAssetToWorld(filepath, "", false, false); + //emit uploadRequest(urlString); + /*auto upload = DependencyManager::get()->createUpload(urlString); + QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { + if (upload->getError() != AssetUpload::NoError) { + QString errorInfo = "Could not upload model to the Asset Server."; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; + //addAssetToWorldError(filenameFromPath(urlString), errorInfo); + } else { + //addAssetToWorldSetMapping(urlString, QString("/" + filenameFromPath(urlString)), hash); + } + upload->deleteLater(); + });*/ + return true; +} + // thread-safe void Application::onPresent(quint32 frameCount) { bool expected = false; @@ -6403,6 +6428,7 @@ void Application::addAssetToWorld(QString path, QString zipFile, bool isZip, boo // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). QString mapping; QString filename = filenameFromPath(path); + qCDebug(interfaceapp) << "Filename is: " << filename; if (isZip || isBlocks) { QString assetName = zipFile.section("/", -1).remove(QRegExp("[.]zip(.*)$")); QString assetFolder = path.section("model_repo/", -1); @@ -6425,6 +6451,8 @@ void Application::addAssetToWorld(QString path, QString zipFile, bool isZip, boo } void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { + qCDebug(interfaceapp) << "mapping request is: " << mapping; + qCDebug(interfaceapp) << "file is located: " << filePath; auto request = DependencyManager::get()->createGetMappingRequest(mapping); QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { diff --git a/interface/src/Application.h b/interface/src/Application.h index ddb8ce11e5..0e9bf95081 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -469,6 +469,7 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); bool importFromZIP(const QString& filePath); + bool importImage(const QString& urlString); bool nearbyEntitiesAreReadyForPhysics(); int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode); From 62437dcc2265886c5d0fb7349c4625d4e68d18d7 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 25 Jan 2018 15:26:26 -0800 Subject: [PATCH 055/121] drag and drop works --- interface/src/Application.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b53ba61e61..b203487547 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2904,23 +2904,10 @@ bool Application::importFromZIP(const QString& filePath) { } bool Application::importImage(const QString& urlString) { - qCDebug(interfaceapp) << "dragged image"; + qCDebug(interfaceapp) << "An image file has been dropped in"; QString filepath(urlString); filepath.remove("file:///"); - //<> addAssetToWorld(filepath, "", false, false); - //emit uploadRequest(urlString); - /*auto upload = DependencyManager::get()->createUpload(urlString); - QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { - if (upload->getError() != AssetUpload::NoError) { - QString errorInfo = "Could not upload model to the Asset Server."; - qWarning(interfaceapp) << "Error downloading model: " + errorInfo; - //addAssetToWorldError(filenameFromPath(urlString), errorInfo); - } else { - //addAssetToWorldSetMapping(urlString, QString("/" + filenameFromPath(urlString)), hash); - } - upload->deleteLater(); - });*/ return true; } @@ -6428,7 +6415,6 @@ void Application::addAssetToWorld(QString path, QString zipFile, bool isZip, boo // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). QString mapping; QString filename = filenameFromPath(path); - qCDebug(interfaceapp) << "Filename is: " << filename; if (isZip || isBlocks) { QString assetName = zipFile.section("/", -1).remove(QRegExp("[.]zip(.*)$")); QString assetFolder = path.section("model_repo/", -1); @@ -6451,8 +6437,6 @@ void Application::addAssetToWorld(QString path, QString zipFile, bool isZip, boo } void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { - qCDebug(interfaceapp) << "mapping request is: " << mapping; - qCDebug(interfaceapp) << "file is located: " << filePath; auto request = DependencyManager::get()->createGetMappingRequest(mapping); QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { @@ -6519,7 +6503,8 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else { // to prevent files that aren't models from being loaded into world automatically - if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { + if (filePath.endsWith(OBJ_EXTENSION) || filePath.endsWith(FBX_EXTENSION) || + filePath.endsWith(JPG_EXTENSION) || filePath.endsWith(PNG_EXTENSION)) { addAssetToWorldAddEntity(filePath, mapping); } else { qCDebug(interfaceapp) << "Zipped contents are not supported entity files"; @@ -6536,8 +6521,17 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { EntityItemProperties properties; properties.setType(EntityTypes::Model); properties.setName(mapping.right(mapping.length() - 1)); - properties.setModelURL("atp:" + mapping); - properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); + if (filePath.endsWith(PNG_EXTENSION) || filePath.endsWith(JPG_EXTENSION)) { + QJsonObject textures { + {"tex.picture", QString("atp:" + mapping) } + }; + properties.setModelURL("https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx"); + properties.setTextures(QJsonDocument(textures).toJson(QJsonDocument::Compact)); + properties.setShapeType(SHAPE_TYPE_BOX); + } else { + properties.setModelURL("atp:" + mapping); + properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); + } properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar. properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions. glm::vec3 positionOffset = getMyAvatar()->getWorldOrientation() * (getMyAvatar()->getSensorToWorldScale() * glm::vec3(0.0f, 0.0f, -2.0f)); From 9f8e2017ceab378aec06a75c6f447317f68b1e32 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 26 Jan 2018 14:39:04 -0800 Subject: [PATCH 056/121] can add image directly from asset server --- interface/resources/qml/hifi/AssetServer.qml | 167 ++++++++++-------- .../qml/hifi/dialogs/TabletAssetServer.qml | 167 ++++++++++-------- .../entities/src/EntityScriptingInterface.cpp | 5 +- .../entities/src/EntityScriptingInterface.h | 2 +- 4 files changed, 183 insertions(+), 158 deletions(-) diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 6a4e703413..04fceec1f3 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -182,92 +182,103 @@ Windows.ScrollingWindow { return; } - var SHAPE_TYPE_NONE = 0; - var SHAPE_TYPE_SIMPLE_HULL = 1; - var SHAPE_TYPE_SIMPLE_COMPOUND = 2; - var SHAPE_TYPE_STATIC_MESH = 3; - var SHAPE_TYPE_BOX = 4; - var SHAPE_TYPE_SPHERE = 5; + if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) { + var name = assetProxyModel.data(treeView.selection.currentIndex); + var modelURL = "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx"; + var textures = JSON.stringify({ "tex.picture": defaultURL}); + var shapeType = "box"; + var dynamic = false; + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); + var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, position, gravity); + } else { + var SHAPE_TYPE_NONE = 0; + var SHAPE_TYPE_SIMPLE_HULL = 1; + var SHAPE_TYPE_SIMPLE_COMPOUND = 2; + var SHAPE_TYPE_STATIC_MESH = 3; + var SHAPE_TYPE_BOX = 4; + var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPES = []; - SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; - SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; - SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; - SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; - SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; - SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; + var SHAPE_TYPES = []; + SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; + SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; + SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; + SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; - var DYNAMIC_DEFAULT = false; - var prompt = desktop.customInputDialog({ - textInput: { - label: "Model URL", - text: defaultURL - }, - comboBox: { - label: "Automatic Collisions", - index: SHAPE_TYPE_DEFAULT, - items: SHAPE_TYPES - }, - checkBox: { - label: "Dynamic", - checked: DYNAMIC_DEFAULT, - disableForItems: [ - SHAPE_TYPE_STATIC_MESH - ], - checkStateOnDisable: false, - warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors" - } - }); - - prompt.selected.connect(function (jsonResult) { - if (jsonResult) { - var result = JSON.parse(jsonResult); - var url = result.textInput.trim(); - var shapeType; - switch (result.comboBox) { - case SHAPE_TYPE_SIMPLE_HULL: - shapeType = "simple-hull"; - break; - case SHAPE_TYPE_SIMPLE_COMPOUND: - shapeType = "simple-compound"; - break; - case SHAPE_TYPE_STATIC_MESH: - shapeType = "static-mesh"; - break; - case SHAPE_TYPE_BOX: - shapeType = "box"; - break; - case SHAPE_TYPE_SPHERE: - shapeType = "sphere"; - break; - default: - shapeType = "none"; + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; + var DYNAMIC_DEFAULT = false; + var prompt = desktop.customInputDialog({ + textInput: { + label: "Model URL", + text: defaultURL + }, + comboBox: { + label: "Automatic Collisions", + index: SHAPE_TYPE_DEFAULT, + items: SHAPE_TYPES + }, + checkBox: { + label: "Dynamic", + checked: DYNAMIC_DEFAULT, + disableForItems: [ + SHAPE_TYPE_STATIC_MESH + ], + checkStateOnDisable: false, + warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors" } + }); - var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; - if (shapeType === "static-mesh" && dynamic) { - // The prompt should prevent this case - print("Error: model cannot be both static mesh and dynamic. This should never happen."); - } else if (url) { - var name = assetProxyModel.data(treeView.selection.currentIndex); - var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); - var gravity; - if (dynamic) { - // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a - // different scripting engine from QTScript. - gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); - } else { - gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + prompt.selected.connect(function (jsonResult) { + if (jsonResult) { + var result = JSON.parse(jsonResult); + var url = result.textInput.trim(); + var shapeType; + switch (result.comboBox) { + case SHAPE_TYPE_SIMPLE_HULL: + shapeType = "simple-hull"; + break; + case SHAPE_TYPE_SIMPLE_COMPOUND: + shapeType = "simple-compound"; + break; + case SHAPE_TYPE_STATIC_MESH: + shapeType = "static-mesh"; + break; + case SHAPE_TYPE_BOX: + shapeType = "box"; + break; + case SHAPE_TYPE_SPHERE: + shapeType = "sphere"; + break; + default: + shapeType = "none"; } - print("Asset browser - adding asset " + url + " (" + name + ") to world."); + var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; + if (shapeType === "static-mesh" && dynamic) { + // The prompt should prevent this case + print("Error: model cannot be both static mesh and dynamic. This should never happen."); + } else if (url) { + var name = assetProxyModel.data(treeView.selection.currentIndex); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); + var gravity; + if (dynamic) { + // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a + // different scripting engine from QTScript. + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); + } else { + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + } - // Entities.addEntity doesn't work from QML, so we use this. - Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity); + print("Asset browser - adding asset " + url + " (" + name + ") to world."); + + // Entities.addEntity doesn't work from QML, so we use this. + Entities.addModelEntity(name, url, "", shapeType, dynamic, addPosition, gravity); + } } - } - }); + }); + } } function copyURLToClipboard(index) { diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index a02496a252..ef4fb8d177 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -182,92 +182,103 @@ Rectangle { return; } - var SHAPE_TYPE_NONE = 0; - var SHAPE_TYPE_SIMPLE_HULL = 1; - var SHAPE_TYPE_SIMPLE_COMPOUND = 2; - var SHAPE_TYPE_STATIC_MESH = 3; - var SHAPE_TYPE_BOX = 4; - var SHAPE_TYPE_SPHERE = 5; + if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) { + var name = assetProxyModel.data(treeView.selection.currentIndex); + var modelURL = "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx"; + var textures = JSON.stringify({ "tex.picture": defaultURL}); + var shapeType = "box"; + var dynamic = false; + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); + var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, position, gravity); + } else { + var SHAPE_TYPE_NONE = 0; + var SHAPE_TYPE_SIMPLE_HULL = 1; + var SHAPE_TYPE_SIMPLE_COMPOUND = 2; + var SHAPE_TYPE_STATIC_MESH = 3; + var SHAPE_TYPE_BOX = 4; + var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPES = []; - SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; - SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; - SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; - SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; - SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; - SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; + var SHAPE_TYPES = []; + SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; + SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; + SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; + SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; - var DYNAMIC_DEFAULT = false; - var prompt = tabletRoot.customInputDialog({ - textInput: { - label: "Model URL", - text: defaultURL - }, - comboBox: { - label: "Automatic Collisions", - index: SHAPE_TYPE_DEFAULT, - items: SHAPE_TYPES - }, - checkBox: { - label: "Dynamic", - checked: DYNAMIC_DEFAULT, - disableForItems: [ - SHAPE_TYPE_STATIC_MESH - ], - checkStateOnDisable: false, - warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors" - } - }); - - prompt.selected.connect(function (jsonResult) { - if (jsonResult) { - var result = JSON.parse(jsonResult); - var url = result.textInput.trim(); - var shapeType; - switch (result.comboBox) { - case SHAPE_TYPE_SIMPLE_HULL: - shapeType = "simple-hull"; - break; - case SHAPE_TYPE_SIMPLE_COMPOUND: - shapeType = "simple-compound"; - break; - case SHAPE_TYPE_STATIC_MESH: - shapeType = "static-mesh"; - break; - case SHAPE_TYPE_BOX: - shapeType = "box"; - break; - case SHAPE_TYPE_SPHERE: - shapeType = "sphere"; - break; - default: - shapeType = "none"; + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; + var DYNAMIC_DEFAULT = false; + var prompt = tabletRoot.customInputDialog({ + textInput: { + label: "Model URL", + text: defaultURL + }, + comboBox: { + label: "Automatic Collisions", + index: SHAPE_TYPE_DEFAULT, + items: SHAPE_TYPES + }, + checkBox: { + label: "Dynamic", + checked: DYNAMIC_DEFAULT, + disableForItems: [ + SHAPE_TYPE_STATIC_MESH + ], + checkStateOnDisable: false, + warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors" } + }); - var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; - if (shapeType === "static-mesh" && dynamic) { - // The prompt should prevent this case - print("Error: model cannot be both static mesh and dynamic. This should never happen."); - } else if (url) { - var name = assetProxyModel.data(treeView.selection.currentIndex); - var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); - var gravity; - if (dynamic) { - // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a - // different scripting engine from QTScript. - gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); - } else { - gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + prompt.selected.connect(function (jsonResult) { + if (jsonResult) { + var result = JSON.parse(jsonResult); + var url = result.textInput.trim(); + var shapeType; + switch (result.comboBox) { + case SHAPE_TYPE_SIMPLE_HULL: + shapeType = "simple-hull"; + break; + case SHAPE_TYPE_SIMPLE_COMPOUND: + shapeType = "simple-compound"; + break; + case SHAPE_TYPE_STATIC_MESH: + shapeType = "static-mesh"; + break; + case SHAPE_TYPE_BOX: + shapeType = "box"; + break; + case SHAPE_TYPE_SPHERE: + shapeType = "sphere"; + break; + default: + shapeType = "none"; } - print("Asset browser - adding asset " + url + " (" + name + ") to world."); + var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; + if (shapeType === "static-mesh" && dynamic) { + // The prompt should prevent this case + print("Error: model cannot be both static mesh and dynamic. This should never happen."); + } else if (url) { + var name = assetProxyModel.data(treeView.selection.currentIndex); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); + var gravity; + if (dynamic) { + // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a + // different scripting engine from QTScript. + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); + } else { + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + } - // Entities.addEntity doesn't work from QML, so we use this. - Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity); + print("Asset browser - adding asset " + url + " (" + name + ") to world."); + + // Entities.addEntity doesn't work from QML, so we use this. + Entities.addModelEntity(name, url, "", shapeType, dynamic, addPosition, gravity); + } } - } - }); + }); + } } function copyURLToClipboard(index) { diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4342f0e683..06a141a95e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -299,7 +299,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } } -QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, +QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity) { _activityTracking.addedEntityCount++; @@ -311,6 +311,9 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin properties.setDynamic(dynamic); properties.setPosition(position); properties.setGravity(gravity); + if (!textures.isEmpty()) { + properties.setTextures(textures); + } return addEntity(properties); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d1b321dbca..919d0e3489 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -158,7 +158,7 @@ public slots: /// temporary method until addEntity can be used from QJSEngine /// Deliberately not adding jsdoc, only used internally. - Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, + Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity); /**jsdoc From cc4bafb46fdcd4b599e2d24d2fd09393bec555dd Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 26 Jan 2018 15:40:22 -0800 Subject: [PATCH 057/121] image entities shown as images in entity list --- interface/resources/qml/hifi/AssetServer.qml | 1 - scripts/system/html/js/entityList.js | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 04fceec1f3..b173b3826d 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -142,7 +142,6 @@ Windows.ScrollingWindow { }); } - // Elisa note - need to link this with specific add entity call function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i]; diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 7b25e66c67..dde91dc694 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -156,6 +156,10 @@ function loaded() { var urlParts = url.split('/'); var filename = urlParts[urlParts.length - 1]; + if (url === "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx") { + type = "Image"; + } + if (entities[id] === undefined) { entityList.add([{ id: id, name: name, type: type, url: filename, locked: locked, visible: visible, From f71d9e4d6a4a994cb26187476ac78e86c6f5b998 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 30 Jan 2018 17:49:30 -0800 Subject: [PATCH 058/121] snapshot referenced locally, won't work on OS --- interface/resources/qml/hifi/AssetServer.qml | 2 +- .../qml/hifi/dialogs/TabletAssetServer.qml | 2 +- interface/resources/snapshot/snapshot.fbx | Bin 0 -> 51420 bytes interface/src/Application.cpp | 2 +- scripts/system/edit.js | 4 ++-- scripts/system/html/js/entityList.js | 4 +++- scripts/system/html/js/entityProperties.js | 3 ++- 7 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 interface/resources/snapshot/snapshot.fbx diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index b173b3826d..42b8118115 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -183,7 +183,7 @@ Windows.ScrollingWindow { if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) { var name = assetProxyModel.data(treeView.selection.currentIndex); - var modelURL = "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx"; + var modelURL = "qrc:///snapshot/snapshot.fbx"; var textures = JSON.stringify({ "tex.picture": defaultURL}); var shapeType = "box"; var dynamic = false; diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index ef4fb8d177..315def6f14 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -184,7 +184,7 @@ Rectangle { if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) { var name = assetProxyModel.data(treeView.selection.currentIndex); - var modelURL = "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx"; + var modelURL = "qrc:///snapshot/snapshot.fbx"; var textures = JSON.stringify({ "tex.picture": defaultURL}); var shapeType = "box"; var dynamic = false; diff --git a/interface/resources/snapshot/snapshot.fbx b/interface/resources/snapshot/snapshot.fbx new file mode 100644 index 0000000000000000000000000000000000000000..641a7f82ebd0a67a9364c21867bad4a02d246048 GIT binary patch literal 51420 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

&G zn)U3&NLVlYg3k%9i=l-QG?rwY&lPFRdfN-$!ojKHtN?jwpZweFP!jW1fNRrMz*Wl=AuKoqy55AyU3eH^f^$WO?uQ|Q3)87W! zM%VQ3#OnI)jA6SHckwNm0;qmkMw6tW@TM|j;ZwBA76W$`f zes=!aTM{V(amXl#A5029(ssbCKD7PiNZSEt_|Qf<(st0xb(r3Mr0t-Y_Aq_?NZUab z=wbTyk+uU&>S3s-Z5{UDtKcKM}3o>GeMF{{KcB$g(?AYpNpS!mo7M IwBH~9Z+ICcSO5S3 literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b203487547..53c1e3fad5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6525,7 +6525,7 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { QJsonObject textures { {"tex.picture", QString("atp:" + mapping) } }; - properties.setModelURL("https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx"); + properties.setModelURL("qrc:///snapshot/snapshot.fbx"); properties.setTextures(QJsonDocument(textures).toJson(QJsonDocument::Compact)); properties.setShapeType(SHAPE_TYPE_BOX); } else { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index c298da38bc..ece8516567 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -548,9 +548,9 @@ var toolBar = (function () { z: 2.58 }, shapeType: "box", - modelURL: "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx", + modelURL: 'qrc:///snapshot/snapshot.fbx', // change to another default image - textures: JSON.stringify({ "tex.picture": "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/dog.jpg" }) + textures: JSON.stringify({ "tex.picture": 'qrc:///snapshot/img/no-image.jpg' }) }); }); diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index dde91dc694..85cd77bd28 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -156,7 +156,9 @@ function loaded() { var urlParts = url.split('/'); var filename = urlParts[urlParts.length - 1]; - if (url === "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx") { + var snapURL = 'qrc:///snapshot/snapshot.fbx'; + + if (url === snapURL) { type = "Image"; } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 59b54b1020..acaff333e2 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -797,7 +797,8 @@ function loaded() { elID.value = properties.id; // HTML workaround since image is not yet a separate entity type - if (properties.type === "Model" && properties.modelURL === "https://hifi-content.s3.amazonaws.com/elisalj/image_entity/snapshot.fbx") { + var snapURL = 'qrc:///snapshot/snapshot.fbx'; + if (properties.type === "Model" && properties.modelURL === snapURL) { properties.type = "Image"; } From d7a847930d3f8d88cbbb505a6f6b6bafbbad8ac6 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 31 Jan 2018 11:44:22 -0800 Subject: [PATCH 059/121] added image icon --- interface/resources/fonts/hifi-glyphs.ttf | Bin 31232 -> 31500 bytes .../fonts/hifi-glyphs/fonts/hifi-glyphs.eot | Bin 0 -> 31678 bytes .../fonts/hifi-glyphs/fonts/hifi-glyphs.svg | 148 ++++++++++++++++++ .../fonts/hifi-glyphs/fonts/hifi-glyphs.ttf | Bin 0 -> 31500 bytes .../fonts/hifi-glyphs/fonts/hifi-glyphs.woff | Bin 0 -> 20032 bytes .../fonts/hifi-glyphs/icons-reference.html | 94 ++++++++++- .../resources/fonts/hifi-glyphs/styles.css | 35 ++++- .../resources/icons/create-icons/image.svg | 23 +++ .../resources/qml/hifi/tablet/EditTabView.qml | 2 +- scripts/system/edit.js | 1 - scripts/system/html/js/entityProperties.js | 2 +- 11 files changed, 298 insertions(+), 7 deletions(-) create mode 100644 interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot create mode 100644 interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.svg create mode 100644 interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf create mode 100644 interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.woff create mode 100644 interface/resources/icons/create-icons/image.svg diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 4cc5a0fe4f098a287c1df067c8bff37695f96d34..8907cf7858dd304cdad024bfa1ff4ca5cd94caf9 100644 GIT binary patch delta 724 zcmX|8O=uHg5Ph>h-=@iK`jgG3nn;_(>^4eivq_UGO~Jo*03;0QVN) zX6G`+!tO43VEF|A+uTBVcJti01h6S!VKbM_%s5l^Fo2H$;<;S5k`V@J8z2?H!Cbzy z)bq8w1E5C$1r`>kGZRPhIY1l%$d}J76|h4W0UB;CbTyOD?i_ub28u0Dt*}@u&77$F zfYNh-k#!sa-mUbPOYI{+;NZ}T_jeoG9&-)t4kc;5HcC}WC9NtCSnyeP&}HzJ@u(EYPvN^4f+E^ov|n-DW(Vg0d|0kC*nP%B`gw6E$TNDaYHwBiAl^DrG#$iK__|C zgvNqQH)8RGsp$dcRy}4sMszda|BrZC7X`baPejBPrbc<$R?GqWn-6M7gWo&g@Y| z_0ABp$wt^A$>#-+s4~gUOI;p%;PR4JZB63kHo?uwYRz#?q0T5G=(k{E5D^Sl!^PgV zXrw>VZw^L=tM`g}b*VT{>$OkCZsO=x?RV*^Ad2((%v^P|?5DNrhqBl4|CbHzFM6Dk ACjbBd delta 475 zcmX|-Pe>F27{!0z?6_>Hl;lBV(GkK38QNLUDR4XK+M&==V(DOwy1BW#IkZFUivMnc zu9t>xA`)~ow8=vt6p=S0FG+_EdkD#nVnUZTW~4s&c<=Jw_uf}Nk@;hpLx6hb2{DjN z4&5p){#k?4cOcr=)0^ZiVJLkBwEn>>@&4_bHb@G%JDRubp^+~yLD>UkV%#d&)hZz< zZ3B_ zt$}N19n^G#H0Q0N%}*Hyx#|acY~|BOiC6dFNgar1PZpdz_VZ;ZyoZ|KK{OBq3$*y0~&1KYOszK#GhJ3Ro0XZUASApy0dARxVdMs@1@+ zt-^nf_8?G1_yAr@=z@5iYMY{fmkMuXy@o=qG??fZ80f@_VPLwtqn$Bz!_ea~!*o}j oc6Zu&BxUcwX_D7Z->L4-ly+$eL51KxrwMlxFZ_S{m45ugzdev~Hvj+t diff --git a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot b/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot new file mode 100644 index 0000000000000000000000000000000000000000..d3591e6499c6afaf63e4a2c0f144f4722c2f8d9f GIT binary patch literal 31678 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 + + +Generated by Fontastic.me + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8907cf7858dd304cdad024bfa1ff4ca5cd94caf9 GIT binary patch literal 31500 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 literal 0 HcmV?d00001 diff --git a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.woff new file mode 100644 index 0000000000000000000000000000000000000000..152e5b4cfc7c4c4a93501e1299cd76b08f99f111 GIT binary patch literal 20032 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%0AM|Nb=x81LFNbI8I6Rv$RP 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

HiFi Glyphs

-

This font was created inHigh Fidelity

+

This font was created for use inHigh Fidelity

CSS mapping

  • @@ -520,8 +520,52 @@
  • -
    - +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    +

Character mapping

@@ -1034,6 +1078,50 @@
+
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +