From d9b136cf5d3aa2bfb60058fc42175465bac7c317 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 20 Aug 2018 11:06:51 -0700 Subject: [PATCH 01/42] Read resources from other than the main directory --- libraries/fbx/src/GLTFReader.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 300e6f4846..317342b886 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -935,7 +935,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping } bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) { - QUrl binaryUrl = _url.resolved(QUrl(url).fileName()); + QUrl binaryUrl = _url.resolved(url); qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url; bool success; @@ -948,7 +948,7 @@ bool GLTFReader::doesResourceExist(const QString& url) { if (_url.isEmpty()) { return false; } - QUrl candidateUrl = _url.resolved(QUrl(url).fileName()); + QUrl candidateUrl = _url.resolved(url); return DependencyManager::get()->resourceExists(candidateUrl); } @@ -1001,8 +1001,9 @@ FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { fbxtex.texcoordSet = 0; if (texture.defined["source"]) { - QString fname = QUrl(_file.images[texture.source].uri).fileName(); - QUrl textureUrl = _url.resolved(fname); + QString url = _file.images[texture.source].uri; + QString fname = QUrl(url).fileName(); + QUrl textureUrl = _url.resolved(url); qCDebug(modelformat) << "fname: " << fname; qCDebug(modelformat) << "textureUrl: " << textureUrl; qCDebug(modelformat) << "Url: " << _url; From 05ff99a1d2f2f0aa8387bd505d665de26bc58519 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 9 Aug 2018 18:39:19 -0700 Subject: [PATCH 02/42] working on blendshapes --- interface/src/avatar/AvatarManager.cpp | 4 +- .../render-utils/src/CauterizedModel.cpp | 5 ++ .../render-utils/src/MeshPartPayload.cpp | 5 +- libraries/render-utils/src/Model.cpp | 54 +++++-------------- libraries/render-utils/src/Model.h | 2 +- 5 files changed, 26 insertions(+), 44 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dbafd06611..efd1c103d9 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -353,15 +353,14 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { QReadLocker locker(&_hashLock); QVector::iterator avatarItr = _avatarsToFade.begin(); const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; while (avatarItr != _avatarsToFade.end()) { auto avatar = std::static_pointer_cast(*avatarItr); avatar->updateFadingStatus(scene); if (!avatar->isFading()) { // fading to zero is such a rare event we push a unique transaction for each if (avatar->isInScene()) { - render::Transaction transaction; avatar->removeFromScene(*avatarItr, scene, transaction); - scene->enqueueTransaction(transaction); } avatarItr = _avatarsToFade.erase(avatarItr); } else { @@ -370,6 +369,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { ++avatarItr; } } + scene->enqueueTransaction(transaction); } AvatarSharedPointer AvatarManager::newSharedAvatar() { diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 80a9c5ccae..ba394b8b2f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -75,6 +75,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; + const FBXGeometry& fbxGeometry = getFBXGeometry(); uint32_t numMeshes = (uint32_t)meshes.size(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); @@ -85,6 +86,10 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (!fbxGeometry.meshes[i].blendshapes.empty()) { + _blendedVertexBuffers[i] = std::make_shared(); + _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + } auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); auto material = getGeometry()->getShapeMaterial(shapeID); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ca6e736a65..58f4f170a0 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -208,7 +208,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); - _blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex]; + auto blendedVertexBuffer = model->_blendedVertexBuffers.find(_meshIndex); + if (blendedVertexBuffer != model->_blendedVertexBuffers.end()) { + _blendedVertexBuffer = blendedVertexBuffer->second; + } auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); const Model::MeshState& state = model->getMeshState(_meshIndex); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ba0d714f7a..f92a6809f3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -305,36 +305,6 @@ bool Model::updateGeometry() { state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); - - // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index - // later in ModelMeshPayload, however the vast majority of meshes will not have them. - // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? - auto buffer = std::make_shared(); - if (!mesh.blendshapes.isEmpty()) { - std::vector normalsAndTangents; - normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size()); - - for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin(); - normalIt != mesh.normals.end(); - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - normalsAndTangents.push_back(finalNormal); - normalsAndTangents.push_back(finalTangent); - } - - buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData()); - buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); - } - _blendedVertexBuffers.push_back(buffer); } needFullUpdate = true; emit rigReady(); @@ -1280,9 +1250,9 @@ void Blender::run() { } // post the result to the geometry cache, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", - Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), - Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), - Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), + Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), + Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1463,7 +1433,7 @@ bool Model::maybeStartBlender() { void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, const QVector& vertices, const QVector& normals, const QVector& tangents) { auto geometryRef = geometry.lock(); - if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { + if (!geometryRef || _renderGeometry != geometryRef || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; @@ -1476,13 +1446,13 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo continue; } - gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; + gpu::BufferPointer buffer = _blendedVertexBuffers[i]; const auto vertexCount = mesh.vertices.size(); const auto verticesSize = vertexCount * sizeof(glm::vec3); const auto offset = index * sizeof(glm::vec3); normalsAndTangents.clear(); - normalsAndTangents.resize(normals.size()+tangents.size()); + normalsAndTangents.resize(normals.size() + tangents.size()); // assert(normalsAndTangents.size() == 2 * vertexCount); // Interleave normals and tangents @@ -1510,7 +1480,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo } #else // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(index, index+vertexCount), [&](const tbb::blocked_range& range) { + tbb::parallel_for(tbb::blocked_range(index, index + vertexCount), [&](const tbb::blocked_range& range) { auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end()); auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end()); auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2; @@ -1603,6 +1573,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1612,6 +1583,10 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (!fbxGeometry.meshes[i].blendshapes.empty()) { + _blendedVertexBuffers[i] = std::make_shared(); + _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + } _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1724,8 +1699,8 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, + const QVector& vertices, const QVector& normals, const QVector& tangents) { if (model) { model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents); @@ -1744,4 +1719,3 @@ void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const } } } - diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 627e5fddab..1d0570f087 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -413,7 +413,7 @@ protected: QUrl _url; - gpu::Buffers _blendedVertexBuffers; + std::unordered_map _blendedVertexBuffers; QVector > > _dilatedTextures; From a378435683b2bf9c5130ade161a29caa8f4d21ab Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 21 Aug 2018 10:20:51 -0700 Subject: [PATCH 03/42] Fix MS16003: In PAL/GoTo WebView, 'BACK' closes app when you can't go back --- interface/resources/qml/controls/TabletWebView.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index bc958aa876..db695dbfb2 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -52,12 +52,18 @@ Item { id: back enabledColor: hifi.colors.darkGray disabledColor: hifi.colors.lightGrayText - enabled: historyIndex > 0 + enabled: true text: "BACK" MouseArea { anchors.fill: parent - onClicked: goBack() + onClicked: { + if (historyIndex > 0) { + goBack(); + } else { + closeWebEngine(); + } + } } } From bfb9b679687ae4a0e4a5dac2e167b162d5101cd1 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 11:17:33 -0700 Subject: [PATCH 04/42] more debug statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 6 ++++++ scripts/system/menu.js | 1 + scripts/system/tablet-ui/tabletUI.js | 6 +++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 1922b02f93..10a9f01337 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -117,6 +117,7 @@ Item { id: pageRepeater model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { + console.log("onItemAdded: " + tabletProxy.buttons.rowCount()); item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } @@ -208,6 +209,7 @@ Item { Component.onCompleted: updateProperties() function updateProperties() { + console.log("updateProperties: " + tabletProxy.buttons.rowCount()); var keys = Object.keys(modelData.properties).forEach(function (key) { if (tabletButton[key] !== modelData.properties[key]) { tabletButton[key] = modelData.properties[key]; @@ -269,6 +271,10 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count + + Component.onCompleted: { + console.log("pageIndicator: " + pageIndicator.count); + } } } diff --git a/scripts/system/menu.js b/scripts/system/menu.js index d669d3d918..1db210216a 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -52,6 +52,7 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- if (isHMDMode) { button = tablet.addButton(buttonProperties); button.clicked.connect(onClicked); + print("Adding menu button"); } else if (button) { button.clicked.disconnect(onClicked); tablet.removeButton(button); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index f339475f72..c28040000b 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -22,7 +22,7 @@ var DEFAULT_TABLET_SCALE = 70; var preMakeTime = Date.now(); var validCheckTime = Date.now(); - var debugTablet = false; + var debugTablet = true; var tabletScalePercentage = 70.0; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; @@ -298,7 +298,7 @@ } wantsMenu = clicked; }); - + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -306,7 +306,7 @@ } wantsMenu = clicked; }); - + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From af5c7a7a674618f16d20f69ccf980c9c2476d63c Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 21 Aug 2018 16:35:47 -0300 Subject: [PATCH 05/42] Android - Domains List - cache in memory only on this instance (deleted if app restarts) allows for fast list display. Showing again also asks for update so in some seconds the list gets updated in case of changes --- .../hifiinterface/fragment/HomeFragment.java | 25 ++++++++---- .../hifiinterface/view/DomainAdapter.java | 38 +++++++++++++++---- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java index 7bd373cf1d..86b8625cfe 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java @@ -76,18 +76,22 @@ public class HomeFragment extends Fragment { }); mDomainAdapter.setListener(new DomainAdapter.AdapterListener() { @Override - public void onEmptyAdapter() { + public void onEmptyAdapter(boolean shouldStopRefreshing) { searchNoResultsView.setText(R.string.search_no_results); searchNoResultsView.setVisibility(View.VISIBLE); mDomainsView.setVisibility(View.GONE); - mSwipeRefreshLayout.setRefreshing(false); + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override - public void onNonEmptyAdapter() { + public void onNonEmptyAdapter(boolean shouldStopRefreshing) { searchNoResultsView.setVisibility(View.GONE); mDomainsView.setVisibility(View.VISIBLE); - mSwipeRefreshLayout.setRefreshing(false); + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override @@ -96,11 +100,20 @@ public class HomeFragment extends Fragment { } }); mDomainsView.setAdapter(mDomainAdapter); + mDomainAdapter.startLoad(); mSearchView = rootView.findViewById(R.id.searchView); mSearchIconView = rootView.findViewById(R.id.search_mag_icon); mClearSearch = rootView.findViewById(R.id.search_clear); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); mSearchView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @@ -142,10 +155,6 @@ public class HomeFragment extends Fragment { mDomainAdapter.loadDomains(mSearchView.getText().toString(), true); } }); - - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - - return rootView; } @Override diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 71d634e9ea..6860742a0d 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -12,6 +12,7 @@ import android.widget.TextView; import com.squareup.picasso.Picasso; +import java.util.Arrays; import java.util.List; import io.highfidelity.hifiinterface.R; @@ -36,19 +37,39 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { + mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mDomains.length == 0) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } + } + } + } + public void loadDomains(String filterText, boolean forceRefresh) { domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() { @Override @@ -60,13 +81,16 @@ public class DomainAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 17:47:40 -0300 Subject: [PATCH 06/42] Android - People List - cache in memory only on this instance (deleted if app restarts) allows for fast list display. Showing again also asks for update so in some ms the list gets updated in case of changes --- .../fragment/FriendsFragment.java | 14 +++++--- .../hifiinterface/view/UserListAdapter.java | 35 ++++++++++++++++--- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 2a008d7950..2475c4d887 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -98,13 +98,17 @@ public class FriendsFragment extends Fragment { mUsersAdapter.setListener(new UserListAdapter.AdapterListener() { @Override - public void onEmptyAdapter() { - mSwipeRefreshLayout.setRefreshing(false); + public void onEmptyAdapter(boolean shouldStopRefreshing) { + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override - public void onNonEmptyAdapter() { - mSwipeRefreshLayout.setRefreshing(false); + public void onNonEmptyAdapter(boolean shouldStopRefreshing) { + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override @@ -115,6 +119,8 @@ public class FriendsFragment extends Fragment { mUsersView.setAdapter(mUsersAdapter); + mUsersAdapter.startLoad(); + mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 9f62b21250..a34de5b34e 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -37,28 +37,53 @@ public class UserListAdapter extends RecyclerView.Adapter USERS_TMP_CACHE; + public UserListAdapter(Context c, UsersProvider usersProvider) { mContext = c; mInflater = LayoutInflater.from(mContext); mProvider = usersProvider; - loadUsers(); } public void setListener(AdapterListener adapterListener) { mAdapterListener = adapterListener; } + public void startLoad() { + useTmpCachedUsers(); + loadUsers(); + } + + private void useTmpCachedUsers() { + if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) { + mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); + mUsers.addAll(USERS_TMP_CACHE); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } + } + } + } + public void loadUsers() { mProvider.retrieve(new UsersProvider.UsersCallback() { @Override public void retrieveOk(List users) { mUsers = new ArrayList<>(users); notifyDataSetChanged(); + + USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); + USERS_TMP_CACHE.addAll(mUsers); + if (mAdapterListener != null) { if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(); + mAdapterListener.onEmptyAdapter(true); } else { - mAdapterListener.onNonEmptyAdapter(); + mAdapterListener.onNonEmptyAdapter(true); } } } @@ -240,8 +265,8 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 18:40:10 -0300 Subject: [PATCH 07/42] Android - People and Domain list - synchronize cache and request code --- .../hifiinterface/view/DomainAdapter.java | 40 ++++++++++--------- .../hifiinterface/view/UserListAdapter.java | 37 +++++++++-------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 6860742a0d..78251ac4a4 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -57,14 +57,16 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { - mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); - notifyDataSetChanged(); - if (mAdapterListener != null) { - if (mDomains.length == 0) { - mAdapterListener.onEmptyAdapter(false); - } else { - mAdapterListener.onNonEmptyAdapter(false); + synchronized (this) { + if (DOMAINS_TMP_CACHE != null && DOMAINS_TMP_CACHE.length > 0) { + mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mDomains.length == 0) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } } } } @@ -81,16 +83,18 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { - mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); - mUsers.addAll(USERS_TMP_CACHE); - notifyDataSetChanged(); - if (mAdapterListener != null) { - if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(false); - } else { - mAdapterListener.onNonEmptyAdapter(false); + synchronized (this) { + if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) { + mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); + mUsers.addAll(USERS_TMP_CACHE); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } } } } @@ -76,14 +78,16 @@ public class UserListAdapter extends RecyclerView.Adapter(users); notifyDataSetChanged(); - USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); - USERS_TMP_CACHE.addAll(mUsers); + synchronized (this) { + USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); + USERS_TMP_CACHE.addAll(mUsers); - if (mAdapterListener != null) { - if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(true); - } else { - mAdapterListener.onNonEmptyAdapter(true); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(true); + } else { + mAdapterListener.onNonEmptyAdapter(true); + } } } } @@ -269,4 +273,5 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 15:55:00 -0700 Subject: [PATCH 08/42] adding fix --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 4e920e430b..1081f8c4e7 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -140,8 +140,7 @@ int TabletButtonsProxyModel::buttonIndex(const QString &uuid) { return -1; } -void TabletButtonsProxyModel::setPageIndex(int pageIndex) -{ +void TabletButtonsProxyModel::setPageIndex(int pageIndex) { if (_pageIndex == pageIndex) return; @@ -465,6 +464,9 @@ void TabletProxy::onTabletShown() { _showRunningScripts = false; pushOntoStack("hifi/dialogs/TabletRunningScripts.qml"); } + if (_currentPathLoaded == TABLET_HOME_SOURCE_URL) { + loadHomeScreen(true); + } } } From 81d0e212dd737b34f43f2f0fff845855a06ee0f9 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 15:57:17 -0700 Subject: [PATCH 09/42] removing print statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 10a9f01337..cccd37ad95 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -117,7 +117,6 @@ Item { id: pageRepeater model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { - console.log("onItemAdded: " + tabletProxy.buttons.rowCount()); item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } @@ -209,7 +208,6 @@ Item { Component.onCompleted: updateProperties() function updateProperties() { - console.log("updateProperties: " + tabletProxy.buttons.rowCount()); var keys = Object.keys(modelData.properties).forEach(function (key) { if (tabletButton[key] !== modelData.properties[key]) { tabletButton[key] = modelData.properties[key]; @@ -272,9 +270,6 @@ Item { anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count - Component.onCompleted: { - console.log("pageIndicator: " + pageIndicator.count); - } } } From bb176eb22834b01d6c1e89d38f18533240f5cef7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 16:00:01 -0700 Subject: [PATCH 10/42] trailing debug statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 1 - scripts/system/menu.js | 1 - scripts/system/tablet-ui/tabletUI.js | 4 +--- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index cccd37ad95..1922b02f93 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -269,7 +269,6 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count - } } diff --git a/scripts/system/menu.js b/scripts/system/menu.js index 1db210216a..d669d3d918 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -52,7 +52,6 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- if (isHMDMode) { button = tablet.addButton(buttonProperties); button.clicked.connect(onClicked); - print("Adding menu button"); } else if (button) { button.clicked.disconnect(onClicked); tablet.removeButton(button); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index c28040000b..d78e90b789 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -22,7 +22,7 @@ var DEFAULT_TABLET_SCALE = 70; var preMakeTime = Date.now(); var validCheckTime = Date.now(); - var debugTablet = true; + var debugTablet = false; var tabletScalePercentage = 70.0; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; @@ -298,7 +298,6 @@ } wantsMenu = clicked; }); - clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -306,7 +305,6 @@ } wantsMenu = clicked; }); - clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 4e9a5446187187ffcba87a846ec1b22f36cdbf15 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 16:01:24 -0700 Subject: [PATCH 11/42] redoing newline --- scripts/system/tablet-ui/tabletUI.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index d78e90b789..80ddbeca8b 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -298,6 +298,7 @@ } wantsMenu = clicked; }); + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -305,6 +306,7 @@ } wantsMenu = clicked; }); + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 5c0b12abf6a7d1983dc5a080cf31422c41ee36d3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 22 Aug 2018 12:07:32 -0700 Subject: [PATCH 12/42] ordered ray/parabola intersection code --- interface/src/avatar/AvatarManager.cpp | 8 +- interface/src/raypick/LaserPointer.cpp | 3 + interface/src/raypick/ParabolaPointer.cpp | 3 + .../src/RenderablePolyVoxEntityItem.cpp | 13 +- libraries/entities/src/EntityTree.cpp | 54 +- libraries/entities/src/EntityTreeElement.cpp | 33 +- libraries/entities/src/EntityTreeElement.h | 9 +- libraries/entities/src/ShapeEntityItem.cpp | 28 +- libraries/octree/src/Octree.cpp | 127 ++--- libraries/octree/src/Octree.h | 22 +- libraries/render-utils/src/Model.cpp | 177 ++++-- libraries/render-utils/src/Model.h | 10 + libraries/shared/src/GeometryUtil.cpp | 7 +- libraries/shared/src/TriangleSet.cpp | 518 +++++++++++------- libraries/shared/src/TriangleSet.h | 25 +- 15 files changed, 589 insertions(+), 448 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0d180bc40d..139f44d58b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -553,8 +553,6 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic return result; } - glm::vec3 normDirection = glm::normalize(ray.direction); - auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -587,14 +585,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findRayCapsuleIntersection(ray.origin, normDirection, start, end, radius, distance); + bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); if (!intersects) { // ray doesn't intersect avatar's capsule continue; } QVariantMap extraInfo; - intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, normDirection, + intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true); if (intersects && (!result.intersects || distance < result.distance)) { @@ -608,7 +606,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic } if (result.intersects) { - result.intersection = ray.origin + normDirection * result.distance; + result.intersection = ray.origin + ray.direction * result.distance; } return result; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 2382a95105..40a5e5cb69 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -42,6 +42,9 @@ glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const glm::vec3 LaserPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const { auto rayPickResult = std::static_pointer_cast(pickResult); + if (!rayPickResult) { + return glm::vec3(0.0f); + } if (distance > 0.0f) { PickRay pick = PickRay(rayPickResult->pickVariant); return pick.origin + distance * pick.direction; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 097c98340c..57d57e11c4 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -67,6 +67,9 @@ glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) co glm::vec3 ParabolaPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const { auto parabolaPickResult = std::static_pointer_cast(pickResult); + if (!parabolaPickResult) { + return glm::vec3(0.0f); + } if (distance > 0.0f) { PickParabola pick = PickParabola(parabolaPickResult->pickVariant); return pick.origin + pick.velocity * distance + 0.5f * pick.acceleration * distance * distance; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index c11ccb70a0..63f27dd170 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -584,8 +584,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); - glm::vec4 directionInVoxel = glm::normalize(farInVoxel - originInVoxel); - glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); PolyVox::RaycastResult raycastResult = doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { @@ -599,14 +597,9 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o voxelBox += result3 - Vectors::HALF; voxelBox += result3 + Vectors::HALF; - float voxelDistance; - bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), - voxelDistance, face, surfaceNormal); - - glm::vec4 voxelIntersectionPoint = glm::vec4(glm::vec3(originInVoxel) + glm::vec3(directionInVoxel) * voxelDistance, 1.0); - glm::vec4 intersectionPoint = vtwMatrix * voxelIntersectionPoint; - distance = glm::distance(origin, glm::vec3(intersectionPoint)); - return hit; + glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f); + return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), + distance, face, surfaceNormal); } bool RenderablePolyVoxEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 377e192bb1..1cf6e45d5b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -825,15 +825,38 @@ bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, keepSearching, + EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; + // We recurse OctreeElements in order, so if we hit something, we can stop immediately + keepSearching = false; } return keepSearching; } +float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { + RayArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); + float distance = FLT_MAX; + // If origin is inside the cube, always check this element first + if (entityTreeElementPointer->getAACube().contains(args->origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) { + // Don't add this cell if it's already farther than our best distance so far + if (boundDistance < args->distance) { + distance = boundDistance; + } + } + } + return distance; +} + EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, bool visibleOnly, bool collidableOnly, bool precisionPicking, @@ -846,7 +869,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&]{ - recurseTreeWithOperation(findRayIntersectionOp, &args); + recurseTreeWithOperationSorted(findRayIntersectionOp, findRayIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -860,15 +883,38 @@ bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extra ParabolaArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, keepSearching, + EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; + // We recurse OctreeElements in order, so if we hit something, we can stop immediately + keepSearching = false; } return keepSearching; } +float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { + ParabolaArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); + float distance = FLT_MAX; + // If origin is inside the cube, always check this element first + if (entityTreeElementPointer->getAACube().contains(args->origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (entityTreeElementPointer->getAACube().findParabolaIntersection(args->origin, args->velocity, args->acceleration, boundDistance, face, surfaceNormal)) { + // Don't add this cell if it's already farther than our best distance so far + if (boundDistance < args->parabolicDistance) { + distance = boundDistance; + } + } + } + return distance; +} + EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, bool visibleOnly, bool collidableOnly, bool precisionPicking, @@ -882,7 +928,7 @@ EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&] { - recurseTreeWithOperation(findParabolaIntersectionOp, &args); + recurseTreeWithOperationSorted(findParabolaIntersectionOp, findParabolaIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5974fce6c5..e8e11c0ee1 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -140,27 +140,17 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 } EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking) { + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); + float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; - // if the ray doesn't intersect with our cube OR the distance to element is less than current best distance - // we can stop searching! - bool hit = _cube.findRayIntersection(origin, direction, distanceToElementCube, localFace, localSurfaceNormal); - if (!hit || (!_cube.contains(origin) && distanceToElementCube > distance)) { - keepSearching = false; // no point in continuing to search - return result; // we did not intersect - } - - // by default, we only allow intersections with leaves with content if (!canPickIntersect()) { - return result; // we don't intersect with non-leaves, and we keep searching + return result; } // if the distance to the element cube is not less than the current best distance, then it's not possible @@ -289,7 +279,7 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad } EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { @@ -299,17 +289,8 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin BoxFace localFace; glm::vec3 localSurfaceNormal; - // if the parabola doesn't intersect with our cube OR the distance to element is less than current best distance - // we can stop searching! - bool hit = _cube.findParabolaIntersection(origin, velocity, acceleration, distanceToElementCube, localFace, localSurfaceNormal); - if (!hit || (!_cube.contains(origin) && distanceToElementCube > parabolicDistance)) { - keepSearching = false; // no point in continuing to search - return result; // we did not intersect - } - - // by default, we only allow intersections with leaves with content if (!canPickIntersect()) { - return result; // we don't intersect with non-leaves, and we keep searching + return result; } // if the distance to the element cube is not less than the current best distance, then it's not possible diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d6f9db08d6..793340c9a4 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -136,10 +136,9 @@ public: virtual bool canPickIntersect() const override { return hasEntities(); } virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking = false); + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); virtual EntityItemID findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, @@ -149,7 +148,7 @@ public: glm::vec3& penetration, void** penetratedObject) const override; virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index e4ea1470c1..773a7059dc 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -262,20 +262,18 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix(); glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); - glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); + glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); - float localDistance; // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 - if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) { - // determine where on the unit sphere the hit point occured - glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); - // then translate back to work coordinates - glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); - distance = glm::distance(origin, hitAt); + if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, distance)) { bool success; - // FIXME: this is only correct for uniformly scaled spheres - surfaceNormal = glm::normalize(hitAt - getCenterPosition(success)); - if (!success) { + glm::vec3 center = getCenterPosition(success); + if (success) { + // FIXME: this is only correct for uniformly scaled spheres + // determine where on the unit sphere the hit point occured + glm::vec3 hitAt = origin + (direction * distance); + surfaceNormal = glm::normalize(hitAt - center); + } else { return false; } return true; @@ -297,9 +295,11 @@ bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 if (findParabolaSphereIntersection(entityFrameOrigin, entityFrameVelocity, entityFrameAcceleration, glm::vec3(0.0f), 0.5f, parabolicDistance)) { bool success; - // FIXME: this is only correct for uniformly scaled spheres - surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - getCenterPosition(success)); - if (!success) { + glm::vec3 center = getCenterPosition(success); + if (success) { + // FIXME: this is only correct for uniformly scaled spheres + surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - center); + } else { return false; } return true; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9bb0e25982..c6474c1fe4 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -68,55 +68,12 @@ Octree::~Octree() { eraseAllOctreeElements(false); } - -// Inserts the value and key into three arrays sorted by the key array, the first array is the value, -// the second array is a sorted key for the value, the third array is the index for the value in it original -// non-sorted array -// returns -1 if size exceeded -// originalIndexArray is optional -int insertOctreeElementIntoSortedArrays(const OctreeElementPointer& value, float key, int originalIndex, - OctreeElementPointer* valueArray, float* keyArray, int* originalIndexArray, - int currentCount, int maxCount) { - - if (currentCount < maxCount) { - int i = 0; - if (currentCount > 0) { - while (i < currentCount && key > keyArray[i]) { - i++; - } - // i is our desired location - // shift array elements to the right - if (i < currentCount && i+1 < maxCount) { - for (int j = currentCount - 1; j > i; j--) { - valueArray[j] = valueArray[j - 1]; - keyArray[j] = keyArray[j - 1]; - } - } - } - // place new element at i - valueArray[i] = value; - keyArray[i] = key; - if (originalIndexArray) { - originalIndexArray[i] = originalIndex; - } - return currentCount + 1; - } - return -1; // error case -} - - - // Recurses voxel tree calling the RecurseOctreeOperation function for each element. // stops recursion if operation function returns false. void Octree::recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData) { recurseElementWithOperation(_rootElement, operation, extraData); } -// Recurses voxel tree calling the RecurseOctreePostFixOperation function for each element in post-fix order. -void Octree::recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData) { - recurseElementWithPostOperation(_rootElement, operation, extraData); -} - // Recurses voxel element with an operation function void Octree::recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { @@ -129,71 +86,53 @@ void Octree::recurseElementWithOperation(const OctreeElementPointer& element, co for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer child = element->getChildAtIndex(i); if (child) { - recurseElementWithOperation(child, operation, extraData, recursionCount+1); + recurseElementWithOperation(child, operation, extraData, recursionCount + 1); } } } } -// Recurses voxel element with an operation function -void Octree::recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - void* extraData, int recursionCount) { +void Octree::recurseTreeWithOperationSorted(const RecurseOctreeOperation& operation, const RecurseOctreeSortingOperation& sortingOperation, void* extraData) { + recurseElementWithOperationSorted(_rootElement, operation, sortingOperation, extraData); +} + +// Recurses voxel element with an operation function, calling operation on its children in a specific order +bool Octree::recurseElementWithOperationSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, + const RecurseOctreeSortingOperation& sortingOperation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - HIFI_FCDEBUG(octree(), "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - return; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + // If we go too deep, we want to keep searching other paths + return true; } + bool keepSearching = operation(element, extraData); + + std::vector sortedChildren; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer child = element->getChildAtIndex(i); if (child) { - recurseElementWithPostOperation(child, operation, extraData, recursionCount+1); - } - } - operation(element, extraData); -} - -// Recurses voxel tree calling the RecurseOctreeOperation function for each element. -// stops recursion if operation function returns false. -void Octree::recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData) { - - recurseElementWithOperationDistanceSorted(_rootElement, operation, point, extraData); -} - -// Recurses voxel element with an operation function -void Octree::recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData, int recursionCount) { - - if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - return; - } - - if (operation(element, extraData)) { - // determine the distance sorted order of our children - OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int currentCount = 0; - - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - OctreeElementPointer childElement = element->getChildAtIndex(i); - if (childElement) { - // chance to optimize, doesn't need to be actual distance!! Could be distance squared - float distanceSquared = childElement->distanceSquareToPoint(point); - currentCount = insertOctreeElementIntoSortedArrays(childElement, distanceSquared, i, - sortedChildren, (float*)&distancesToChildren, - (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); - } - } - - for (int i = 0; i < currentCount; i++) { - OctreeElementPointer childElement = sortedChildren[i]; - if (childElement) { - recurseElementWithOperationDistanceSorted(childElement, operation, point, extraData); + float priority = sortingOperation(child, extraData); + if (priority < FLT_MAX) { + sortedChildren.emplace_back(priority, child); } } } + + if (sortedChildren.size() > 1) { + static auto comparator = [](const SortedChild& left, const SortedChild& right) { return left.first > right.first; }; + std::sort(sortedChildren.begin(), sortedChildren.end(), comparator); + } + + for (auto it = sortedChildren.begin(); it != sortedChildren.end(); ++it) { + const SortedChild& sortedChild = *it; + // Our children were sorted, so if one hits something, we don't need to check the others + if (!recurseElementWithOperationSorted(sortedChild.second, operation, sortingOperation, extraData, recursionCount + 1)) { + return false; + } + } + // We checked all our children and didn't find anything. + // Stop if we hit something in this element. Continue if we didn't. + return keepSearching; } void Octree::recurseTreeWithOperator(RecurseOctreeOperator* operatorObject) { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index b827ed7cd0..a2b2f227cb 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -49,6 +49,9 @@ public: // Callback function, for recuseTreeWithOperation using RecurseOctreeOperation = std::function; +// Function for sorting octree children during recursion. If return value == FLT_MAX, child is discarded +using RecurseOctreeSortingOperation = std::function; +using SortedChild = std::pair; typedef QHash CubeList; const bool NO_EXISTS_BITS = false; @@ -163,17 +166,10 @@ public: OctreeElementPointer getOrCreateChildElementContaining(const AACube& box); void recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); - void recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); - - /// \param operation type of operation - /// \param point point in world-frame (meters) - /// \param extraData hook for user data to be interpreted by special context - void recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData = NULL); + void recurseTreeWithOperationSorted(const RecurseOctreeOperation& operation, const RecurseOctreeSortingOperation& sortingOperation, void* extraData = NULL); void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject); - bool isDirty() const { return _isDirty; } void clearDirtyBit() { _isDirty = false; } void setDirtyBit() { _isDirty = true; } @@ -227,14 +223,8 @@ public: void recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount = 0); - - /// Traverse child nodes of node applying operation in post-fix order - /// - void recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - void* extraData, int recursionCount = 0); - - void recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData, int recursionCount = 0); + bool recurseElementWithOperationSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, + const RecurseOctreeSortingOperation& sortingOperation, void* extraData, int recursionCount = 0); bool recurseElementWithOperator(const OctreeElementPointer& element, RecurseOctreeOperator* operatorObject, int recursionCount = 0); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8f4eb57f8e..22bc6b6ca7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -379,14 +379,17 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { QMutexLocker locker(&_mutex); - float bestDistance = std::numeric_limits::max(); + float bestDistance = FLT_MAX; + BoxFace bestFace; Triangle bestModelTriangle; Triangle bestWorldTriangle; - int bestSubMeshIndex = 0; + glm::vec3 bestWorldIntersectionPoint; + glm::vec3 bestMeshIntersectionPoint; + int bestPartIndex; + int bestShapeID; + int bestSubMeshIndex; - int subMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); - if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -399,39 +402,75 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); int shapeID = 0; + int subMeshIndex = 0; + + std::vector sortedTriangleSets; for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { int partIndex = 0; - for (auto &partTriangleSet : meshTriangleSets) { - float triangleSetDistance; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (partTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); - glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); - float worldDistance = glm::distance(origin, worldIntersectionPoint); - - if (worldDistance < bestDistance) { - bestDistance = worldDistance; - intersectedSomething = true; - face = triangleSetFace; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - extraInfo["partIndex"] = partIndex; - extraInfo["shapeID"] = shapeID; - bestSubMeshIndex = subMeshIndex; + for (auto& partTriangleSet : meshTriangleSets) { + float priority = FLT_MAX; + if (partTriangleSet.getBounds().contains(meshFrameOrigin)) { + priority = 0.0f; + } else { + float partBoundDistance = FLT_MAX; + BoxFace partBoundFace; + glm::vec3 partBoundNormal; + if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance, + partBoundFace, partBoundNormal)) { + priority = partBoundDistance; } } + + if (priority < FLT_MAX) { + sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex); + } partIndex++; shapeID++; } subMeshIndex++; } + if (sortedTriangleSets.size() > 1) { + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); + } + + for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) { + const SortedTriangleSet& sortedTriangleSet = *it; + // We can exit once triangleSetDistance > bestDistance + if (sortedTriangleSet.distance > bestDistance) { + break; + } + float triangleSetDistance = FLT_MAX; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, + triangleSetTriangle, pickAgainstTriangles, allowBackface)) { + if (triangleSetDistance < bestDistance) { + bestDistance = triangleSetDistance; + intersectedSomething = true; + bestFace = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); + glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); + bestWorldIntersectionPoint = worldIntersectionPoint; + bestMeshIntersectionPoint = meshIntersectionPoint; + bestPartIndex = sortedTriangleSet.partIndex; + bestShapeID = sortedTriangleSet.shapeID; + bestSubMeshIndex = sortedTriangleSet.subMeshIndex; + } + } + } + if (intersectedSomething) { distance = bestDistance; + face = bestFace; surfaceNormal = bestWorldTriangle.getNormal(); + extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint); + extraInfo["partIndex"] = bestPartIndex; + extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); @@ -483,13 +522,16 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co QMutexLocker locker(&_mutex); float bestDistance = FLT_MAX; + BoxFace bestFace; Triangle bestModelTriangle; Triangle bestWorldTriangle; - int bestSubMeshIndex = 0; + glm::vec3 bestWorldIntersectionPoint; + glm::vec3 bestMeshIntersectionPoint; + int bestPartIndex; + int bestShapeID; + int bestSubMeshIndex; - int subMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); - if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -503,40 +545,79 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co glm::vec3 meshFrameAcceleration = glm::vec3(worldToMeshMatrix * glm::vec4(acceleration, 0.0f)); int shapeID = 0; + int subMeshIndex = 0; + + std::vector sortedTriangleSets; for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { int partIndex = 0; - for (auto &partTriangleSet : meshTriangleSets) { - float triangleSetDistance; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (partTriangleSet.findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, - triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - if (triangleSetDistance < bestDistance) { - bestDistance = triangleSetDistance; - intersectedSomething = true; - face = triangleSetFace; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance + - 0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance; - glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance + - 0.5f * acceleration * triangleSetDistance * triangleSetDistance; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - extraInfo["partIndex"] = partIndex; - extraInfo["shapeID"] = shapeID; - bestSubMeshIndex = subMeshIndex; + for (auto& partTriangleSet : meshTriangleSets) { + float priority = FLT_MAX; + if (partTriangleSet.getBounds().contains(meshFrameOrigin)) { + priority = 0.0f; + } else { + float partBoundDistance = FLT_MAX; + BoxFace partBoundFace; + glm::vec3 partBoundNormal; + if (partTriangleSet.getBounds().findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, + partBoundDistance, partBoundFace, partBoundNormal)) { + priority = partBoundDistance; } } + + if (priority < FLT_MAX) { + sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex); + } partIndex++; shapeID++; } subMeshIndex++; } + if (sortedTriangleSets.size() > 1) { + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); + } + + for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) { + const SortedTriangleSet& sortedTriangleSet = *it; + // We can exit once triangleSetDistance > bestDistance + if (sortedTriangleSet.distance > bestDistance) { + break; + } + float triangleSetDistance = FLT_MAX; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (sortedTriangleSet.triangleSet->findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, + triangleSetDistance, triangleSetFace, triangleSetTriangle, + pickAgainstTriangles, allowBackface)) { + if (triangleSetDistance < bestDistance) { + bestDistance = triangleSetDistance; + intersectedSomething = true; + bestFace = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance + + 0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance; + glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance + + 0.5f * acceleration * triangleSetDistance * triangleSetDistance; + bestWorldIntersectionPoint = worldIntersectionPoint; + bestMeshIntersectionPoint = meshIntersectionPoint; + bestPartIndex = sortedTriangleSet.partIndex; + bestShapeID = sortedTriangleSet.shapeID; + bestSubMeshIndex = sortedTriangleSet.subMeshIndex; + // These sets can overlap, so we can't exit early if we find something + } + } + } + if (intersectedSomething) { parabolicDistance = bestDistance; + face = bestFace; surfaceNormal = bestWorldTriangle.getNormal(); + extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint); + extraInfo["partIndex"] = bestPartIndex; + extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 627e5fddab..adb2fcdd3d 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -64,6 +64,16 @@ class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; +struct SortedTriangleSet { + SortedTriangleSet(float distance, TriangleSet* triangleSet, int partIndex, int shapeID, int subMeshIndex) : + distance(distance), triangleSet(triangleSet), partIndex(partIndex), shapeID(shapeID), subMeshIndex(subMeshIndex) {} + + float distance; + TriangleSet* triangleSet; + int partIndex; + int shapeID; + int subMeshIndex; +}; /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public std::enable_shared_from_this, public scriptable::ModelProvider { diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 6fb06eb624..a93c2ec9f3 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -286,12 +286,13 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi distance = 0.0f; return true; // starts inside the sphere } - float b = glm::dot(direction, relativeOrigin); - float radicand = b * b - c; + float b = 2.0f * glm::dot(direction, relativeOrigin); + float a = glm::dot(direction, direction); + float radicand = b * b - 4.0f * a * c; if (radicand < 0.0f) { return false; // doesn't hit the sphere } - float t = -b - sqrtf(radicand); + float t = 0.5f * (-b - sqrtf(radicand)) / a; if (t < 0.0f) { return false; // doesn't hit the sphere } diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index cde9c20cab..f3c0ef5776 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -13,6 +13,8 @@ #include "GLMHelpers.h" +#include + void TriangleSet::insert(const Triangle& t) { _isBalanced = false; @@ -30,47 +32,6 @@ void TriangleSet::clear() { _triangleOctree.clear(); } -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { - - // reset our distance to be the max possible, lower level tests will store best distance here - distance = std::numeric_limits::max(); - - if (!_isBalanced) { - balanceOctree(); - } - - int trianglesTouched = 0; - auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, triangle, precision, trianglesTouched, allowBackface); - - #if WANT_DEBUGGING - if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); - } - #endif - return result; -} - -bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { - // reset our distance to be the max possible, lower level tests will store best distance here - parabolicDistance = FLT_MAX; - - if (!_isBalanced) { - balanceOctree(); - } - - int trianglesTouched = 0; - auto result = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, parabolicDistance, face, triangle, precision, trianglesTouched, allowBackface); - -#if WANT_DEBUGGING - if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); - } -#endif - return result; -} - bool TriangleSet::convexHullContains(const glm::vec3& point) const { if (!_bounds.contains(point)) { return false; @@ -97,7 +58,7 @@ void TriangleSet::debugDump() { } void TriangleSet::balanceOctree() { - _triangleOctree.reset(_bounds, 0); + _triangleOctree.reset(_bounds); // insert all the triangles for (size_t i = 0; i < _triangles.size(); i++) { @@ -106,79 +67,15 @@ void TriangleSet::balanceOctree() { _isBalanced = true; - #if WANT_DEBUGGING +#if WANT_DEBUGGING debugDump(); - #endif +#endif } - -// Determine of the given ray (origin/direction) in model space intersects with any triangles -// in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, Triangle& triangle, bool precision, - int& trianglesTouched, bool allowBackface) { - bool intersectedSomething = false; - float bestDistance = FLT_MAX; - - if (precision) { - for (const auto& triangleIndex : _triangleIndices) { - const auto& thisTriangle = _allTriangles[triangleIndex]; - float thisTriangleDistance; - trianglesTouched++; - if (findRayTriangleIntersection(origin, direction, thisTriangle, thisTriangleDistance, allowBackface)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - triangle = thisTriangle; - } - } - } - } else { - intersectedSomething = true; - bestDistance = distance; - } - - if (intersectedSomething) { - distance = bestDistance; - } - - return intersectedSomething; -} - -bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, Triangle& triangle, bool precision, - int& trianglesTouched, bool allowBackface) { - bool intersectedSomething = false; - float bestDistance = FLT_MAX; - - if (precision) { - for (const auto& triangleIndex : _triangleIndices) { - const auto& thisTriangle = _allTriangles[triangleIndex]; - float thisTriangleDistance; - trianglesTouched++; - if (findParabolaTriangleIntersection(origin, velocity, acceleration, thisTriangle, thisTriangleDistance, allowBackface)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - triangle = thisTriangle; - } - } - } - } else { - intersectedSomething = true; - bestDistance = parabolicDistance; - } - - if (intersectedSomething) { - parabolicDistance = bestDistance; - } - - return intersectedSomething; -} - -static const int MAX_DEPTH = 4; // for now -static const int MAX_CHILDREN = 8; +// With an octree: 8 ^ MAX_DEPTH = 4096 leaves +//static const int MAX_DEPTH = 4; +// With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves +static const int MAX_DEPTH = 12; TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) @@ -190,7 +87,8 @@ void TriangleSet::TriangleOctreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); - _children.clear(); + _children.first.reset(); + _children.second.reset(); } void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { @@ -201,45 +99,76 @@ void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { void TriangleSet::TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; - qDebug() << "bounds:" << getBounds(); - qDebug() << "depth:" << _depth; - qDebug() << "population:" << _population << "this level or below" + qDebug() << " bounds:" << getBounds(); + qDebug() << " depth:" << _depth; + qDebug() << " population:" << _population << "this level or below" << " ---- triangleIndices:" << _triangleIndices.size() << "in this cell"; - qDebug() << "child cells:" << _children.size(); + int numChildren = 0; + if (_children.first) { + numChildren++; + } else if (_children.second) { + numChildren++; + } + qDebug() << "child cells:" << numChildren; if (_depth < MAX_DEPTH) { - int childNum = 0; - for (auto& child : _children) { - qDebug() << "child:" << childNum; - child.second.debugDump(); - childNum++; + if (_children.first) { + qDebug() << "child: 0"; + _children.first->debugDump(); + } + if (_children.second) { + qDebug() << "child: 1"; + _children.second->debugDump(); } } } +std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { + std::pair toReturn; + int axis = 0; + // find biggest axis + glm::vec3 dimensions = _bounds.getDimensions(); + for (int i = 0; i < 3; i++) { + if (dimensions[i] >= dimensions[(i + 1) % 3] && dimensions[i] >= dimensions[(i + 2) % 3]) { + axis = i; + break; + } + } + + // The new boxes are half the size in the largest dimension + glm::vec3 newDimensions = dimensions; + newDimensions[axis] *= 0.5f; + toReturn.first.setBox(_bounds.getCorner(), newDimensions); + glm::vec3 offset = glm::vec3(0.0f); + offset[axis] = newDimensions[axis]; + toReturn.second.setBox(_bounds.getCorner() + offset, newDimensions); + return toReturn; +} + void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { - const Triangle& triangle = _allTriangles[triangleIndex]; _population++; + // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { + const Triangle& triangle = _allTriangles[triangleIndex]; + auto childBounds = getTriangleOctreeCellChildBounds(); - for (int child = 0; child < MAX_CHILDREN; child++) { - AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); - - + auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { // if the child AABox would contain the triangle... - if (childBounds.contains(triangle)) { + if (childBound.contains(triangle)) { // if the child cell doesn't yet exist, create it... - if (_children.find((AABox::OctreeChild)child) == _children.end()) { - _children.insert( - std::pair - ((AABox::OctreeChild)child, TriangleOctreeCell(_allTriangles, childBounds, _depth + 1))); + if (!child) { + child = std::make_shared(_allTriangles, childBound, _depth + 1); } // insert the triangleIndex in the child cell - _children.at((AABox::OctreeChild)child).insert(triangleIndex); - return; + child->insert(triangleIndex); + return true; } + return false; + }; + if (insertOperator(childBounds.first, _children.first) || insertOperator(childBounds.second, _children.second)) { + return; } } // either we're at max depth, or the triangle doesn't fit in one of our @@ -247,6 +176,62 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { _triangleIndices.push_back(triangleIndex); } +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { + if (!_isBalanced) { + balanceOctree(); + } + + float localDistance = distance; + int trianglesTouched = 0; + bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + if (hit) { + distance = localDistance; + } + +#if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + } +#endif + return hit; +} + +// Determine of the given ray (origin/direction) in model space intersects with any triangles +// in the set. If an intersection occurs, the distance and surface normal will be provided. +bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, Triangle& triangle, bool precision, + int& trianglesTouched, bool allowBackface) { + bool intersectedSomething = false; + float bestDistance = FLT_MAX; + Triangle bestTriangle; + + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& thisTriangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findRayTriangleIntersection(origin, direction, thisTriangle, thisTriangleDistance, allowBackface)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + bestTriangle = thisTriangle; + intersectedSomething = true; + } + } + } + } else { + intersectedSomething = true; + bestDistance = distance; + } + + if (intersectedSomething) { + distance = bestDistance; + triangle = bestTriangle; + } + + return intersectedSomething; +} + bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -257,52 +242,81 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi float bestLocalDistance = FLT_MAX; BoxFace bestLocalFace; Triangle bestLocalTriangle; - glm::vec3 bestLocalNormal; bool intersects = false; - float boxDistance = FLT_MAX; - // if the pick intersects our bounding box, then continue - if (getBounds().findRayIntersection(origin, direction, boxDistance, bestLocalFace, bestLocalNormal)) { - // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) - // then we know that none of our triangles can represent a better intersection and we can return - if (boxDistance > distance) { - return false; - } - - // if we're not yet at the max depth, then check which child the triangle fits in - if (_depth < MAX_DEPTH) { - float bestChildDistance = FLT_MAX; - for (auto& child : _children) { - // check each child, if there's an intersection, it will return some distance that we need - // to compare against the other results, because there might be multiple intersections and - // we will always choose the best (shortest) intersection - float childDistance = bestChildDistance; - BoxFace childFace; - Triangle childTriangle; - if (child.second.findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { - if (childDistance < bestLocalDistance) { - bestLocalDistance = childDistance; - bestChildDistance = childDistance; - bestLocalFace = childFace; - bestLocalTriangle = childTriangle; - intersects = true; - } - } - } - } - // also check our local triangle set - float internalDistance = boxDistance; + // Check our local triangle set first + // The distance passed in here is the distance to our bounding box. If !precision, that distance is used + { + float internalDistance = distance; BoxFace internalFace; Triangle internalTriangle; if (findRayIntersectionInternal(origin, direction, internalDistance, internalFace, internalTriangle, precision, trianglesTouched, allowBackface)) { - if (internalDistance < bestLocalDistance) { - bestLocalDistance = internalDistance; - bestLocalFace = internalFace; - bestLocalTriangle = internalTriangle; - intersects = true; + bestLocalDistance = internalDistance; + bestLocalFace = internalFace; + bestLocalTriangle = internalTriangle; + intersects = true; + } + } + + // if we're not yet at the max depth, then check our children + if (_depth < MAX_DEPTH) { + std::list sortedTriangleCells; + auto sortingOperator = [&](std::shared_ptr& child) { + if (child) { + float priority = FLT_MAX; + if (child->getBounds().contains(origin)) { + priority = 0.0f; + } else { + float childBoundDistance = FLT_MAX; + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) { + // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) + if (childBoundDistance < bestLocalDistance) { + priority = childBoundDistance; + } + } + } + + if (priority < FLT_MAX) { + if (sortedTriangleCells.size() > 0 && priority < sortedTriangleCells.front().first) { + sortedTriangleCells.emplace_front(priority, child); + } else { + sortedTriangleCells.emplace_back(priority, child); + } + } + } + }; + sortingOperator(_children.first); + sortingOperator(_children.second); + + for (auto it = sortedTriangleCells.begin(); it != sortedTriangleCells.end(); ++it) { + const SortedTriangleCell& sortedTriangleCell = *it; + float childDistance = sortedTriangleCell.first; + // We can exit once childDistance > bestLocalDistance + if (childDistance > bestLocalDistance) { + break; + } + // If we're inside the child cell and !precision, we need the actual distance to the cell bounds + if (!precision && childDistance < EPSILON) { + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal); + } + BoxFace childFace; + Triangle childTriangle; + if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalTriangle = childTriangle; + intersects = true; + break; + } } } } + if (intersects) { distance = bestLocalDistance; face = bestLocalFace; @@ -311,6 +325,61 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi return intersects; } +bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, + float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { + if (!_isBalanced) { + balanceOctree(); + } + + float localDistance = parabolicDistance; + int trianglesTouched = 0; + bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + if (hit) { + parabolicDistance = localDistance; + } + +#if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + } +#endif + return hit; +} + +bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, float& parabolicDistance, + BoxFace& face, Triangle& triangle, bool precision, + int& trianglesTouched, bool allowBackface) { + bool intersectedSomething = false; + float bestDistance = FLT_MAX; + Triangle bestTriangle; + + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& thisTriangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findParabolaTriangleIntersection(origin, velocity, acceleration, thisTriangle, thisTriangleDistance, allowBackface)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + bestTriangle = thisTriangle; + intersectedSomething = true; + } + } + } + } else { + intersectedSomething = true; + bestDistance = parabolicDistance; + } + + if (intersectedSomething) { + parabolicDistance = bestDistance; + triangle = bestTriangle; + } + + return intersectedSomething; +} + bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, @@ -322,52 +391,81 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& float bestLocalDistance = FLT_MAX; BoxFace bestLocalFace; Triangle bestLocalTriangle; - glm::vec3 bestLocalNormal; bool intersects = false; - float boxDistance = FLT_MAX; - // if the pick intersects our bounding box, then continue - if (getBounds().findParabolaIntersection(origin, velocity, acceleration, boxDistance, bestLocalFace, bestLocalNormal)) { - // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) - // then we know that none of our triangles can represent a better intersection and we can return - if (boxDistance > parabolicDistance) { - return false; - } - - // if we're not yet at the max depth, then check which child the triangle fits in - if (_depth < MAX_DEPTH) { - float bestChildDistance = FLT_MAX; - for (auto& child : _children) { - // check each child, if there's an intersection, it will return some distance that we need - // to compare against the other results, because there might be multiple intersections and - // we will always choose the best (shortest) intersection - float childDistance = bestChildDistance; - BoxFace childFace; - Triangle childTriangle; - if (child.second.findParabolaIntersection(origin, velocity, acceleration, childDistance, childFace, childTriangle, precision, trianglesTouched)) { - if (childDistance < bestLocalDistance) { - bestLocalDistance = childDistance; - bestChildDistance = childDistance; - bestLocalFace = childFace; - bestLocalTriangle = childTriangle; - intersects = true; - } - } - } - } - // also check our local triangle set - float internalDistance = boxDistance; + // Check our local triangle set first + // The distance passed in here is the distance to our bounding box. If !precision, that distance is used + { + float internalDistance = parabolicDistance; BoxFace internalFace; Triangle internalTriangle; if (findParabolaIntersectionInternal(origin, velocity, acceleration, internalDistance, internalFace, internalTriangle, precision, trianglesTouched, allowBackface)) { - if (internalDistance < bestLocalDistance) { - bestLocalDistance = internalDistance; - bestLocalFace = internalFace; - bestLocalTriangle = internalTriangle; - intersects = true; + bestLocalDistance = internalDistance; + bestLocalFace = internalFace; + bestLocalTriangle = internalTriangle; + intersects = true; + } + } + + // if we're not yet at the max depth, then check our children + if (_depth < MAX_DEPTH) { + std::list sortedTriangleCells; + auto sortingOperator = [&](std::shared_ptr& child) { + if (child) { + float priority = FLT_MAX; + if (child->getBounds().contains(origin)) { + priority = 0.0f; + } else { + float childBoundDistance = FLT_MAX; + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + if (child->getBounds().findParabolaIntersection(origin, velocity, acceleration, childBoundDistance, childBoundFace, childBoundNormal)) { + // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) + if (childBoundDistance < bestLocalDistance) { + priority = childBoundDistance; + } + } + } + + if (priority < FLT_MAX) { + if (sortedTriangleCells.size() > 0 && priority < sortedTriangleCells.front().first) { + sortedTriangleCells.emplace_front(priority, child); + } else { + sortedTriangleCells.emplace_back(priority, child); + } + } + } + }; + sortingOperator(_children.first); + sortingOperator(_children.second); + + for (auto it = sortedTriangleCells.begin(); it != sortedTriangleCells.end(); ++it) { + const SortedTriangleCell& sortedTriangleCell = *it; + float childDistance = sortedTriangleCell.first; + // We can exit once childDistance > bestLocalDistance + if (childDistance > bestLocalDistance) { + break; + } + // If we're inside the child cell and !precision, we need the actual distance to the cell bounds + if (!precision && childDistance < EPSILON) { + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + sortedTriangleCell.second->getBounds().findParabolaIntersection(origin, velocity, acceleration, childDistance, childBoundFace, childBoundNormal); + } + BoxFace childFace; + Triangle childTriangle; + if (sortedTriangleCell.second->findParabolaIntersection(origin, velocity, acceleration, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalTriangle = childTriangle; + intersects = true; + break; + } } } } + if (intersects) { parabolicDistance = bestLocalDistance; face = bestLocalFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 0b0d0a9ac5..a838e2d9e2 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include "AABox.h" #include "GeometryUtil.h" @@ -20,9 +21,8 @@ class TriangleSet { class TriangleOctreeCell { public: - TriangleOctreeCell(std::vector& allTriangles) : - _allTriangles(allTriangles) - { } + TriangleOctreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} + TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); @@ -40,8 +40,6 @@ class TriangleSet { void debugDump(); protected: - TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); - // checks our internal list of triangles bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, @@ -50,20 +48,22 @@ class TriangleSet { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); + std::pair getTriangleOctreeCellChildBounds(); + std::vector& _allTriangles; - std::map _children; - int _depth{ 0 }; - int _population{ 0 }; + std::pair, std::shared_ptr> _children; + int _depth { 0 }; + int _population { 0 }; AABox _bounds; std::vector _triangleIndices; friend class TriangleSet; }; + using SortedTriangleCell = std::pair>; + public: - TriangleSet() : - _triangleOctree(_triangles) - {} + TriangleSet() : _triangleOctree(_triangles) {} void debugDump(); @@ -87,8 +87,7 @@ public: const AABox& getBounds() const { return _bounds; } protected: - - bool _isBalanced{ false }; + bool _isBalanced { false }; std::vector _triangles; TriangleOctreeCell _triangleOctree; AABox _bounds; From c474f38860afef8694d4736016c549dbd6bb3d9e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 22 Aug 2018 17:49:02 -0700 Subject: [PATCH 13/42] force coarse picking, sort avatars --- interface/src/Menu.cpp | 6 + interface/src/Menu.h | 1 + interface/src/avatar/AvatarManager.cpp | 174 +++++++++++++++---------- interface/src/avatar/AvatarManager.h | 2 + interface/src/raypick/ParabolaPick.cpp | 7 +- interface/src/raypick/RayPick.cpp | 7 +- libraries/avatars/src/AvatarData.h | 2 +- libraries/pointers/src/Pick.cpp | 4 + libraries/pointers/src/PickManager.h | 11 +- libraries/shared/src/TriangleSet.cpp | 54 ++++---- libraries/shared/src/TriangleSet.h | 18 +-- 11 files changed, 178 insertions(+), 108 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index fe7083ea30..d4c74127c8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -46,6 +46,7 @@ #include "InterfaceLogging.h" #include "LocationBookmarks.h" #include "DeferredLightingEffect.h" +#include "PickManager.h" #include "AmbientOcclusionEffect.h" #include "RenderShadowTask.h" @@ -688,6 +689,11 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool))); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool))); + // Developer > Picking >>> + MenuWrapper* pickingOptionsMenu = developerMenu->addMenu("Picking"); + addCheckableActionToQMenuAndActionHash(pickingOptionsMenu, MenuOption::ForceCoarsePicking, 0, false, + DependencyManager::get().data(), SLOT(setForceCoarsePicking(bool))); + // Developer > Display Crash Options addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); // Developer > Crash >>> diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c489a3f0b0..4386bec5ae 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -221,6 +221,7 @@ namespace MenuOption { const QString NotificationSounds = "play_notification_sounds"; const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot"; const QString NotificationSoundsTablet = "play_notification_sounds_tablet"; + const QString ForceCoarsePicking = "Force Coarse Picking"; } #endif // hifi_Menu_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 139f44d58b..6fb35ec1da 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -553,6 +553,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic return result; } + // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to + // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code + // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking + // against the avatar is sort-of right, but you likely wont be able to pick against the arms. + + // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. + + std::vector sortedAvatars; auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -561,47 +569,60 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic continue; } - float distance; - BoxFace face; - glm::vec3 surfaceNormal; - - SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); - - // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to - // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code - // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking - // against the avatar is sort-of right, but you likely wont be able to pick against the arms. - - // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. - + float distance = FLT_MAX; +#if 0 // if we weren't picking against the capsule, we would want to pick against the avatarBounds... - // AABox avatarBounds = avatarModel->getRenderableMeshBound(); - // if (!avatarBounds.findRayIntersection(ray.origin, normDirection, distance, face, surfaceNormal)) { - // // ray doesn't intersect avatar's bounding-box - // continue; - // } - + SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); + AABox avatarBounds = avatarModel->getRenderableMeshBound(); + if (avatarBounds.contains(ray.origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (avatarBounds.findRayIntersection(ray.origin, ray.direction, boundDistance, face, surfaceNormal)) { + distance = boundDistance; + } + } +#else glm::vec3 start; glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); - if (!intersects) { - // ray doesn't intersect avatar's capsule - continue; + findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); +#endif + + if (distance < FLT_MAX) { + sortedAvatars.emplace_back(distance, avatar); + } + } + + if (sortedAvatars.size() > 1) { + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); + } + + for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) { + const SortedAvatar& sortedAvatar = *it; + // We can exit once avatarCapsuleDistance > bestDistance + if (sortedAvatar.first > result.distance) { + break; } + float distance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; QVariantMap extraInfo; - intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, - distance, face, surfaceNormal, extraInfo, true); - - if (intersects && (!result.intersects || distance < result.distance)) { - result.intersects = true; - result.avatarID = avatar->getID(); - result.distance = distance; - result.face = face; - result.surfaceNormal = surfaceNormal; - result.extraInfo = extraInfo; + SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel(); + if (avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true)) { + if (distance < result.distance) { + result.intersects = true; + result.avatarID = sortedAvatar.second->getID(); + result.distance = distance; + result.face = face; + result.surfaceNormal = surfaceNormal; + result.extraInfo = extraInfo; + } } } @@ -625,6 +646,14 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector return result; } + // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to + // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code + // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking + // against the avatar is sort-of right, but you likely wont be able to pick against the arms. + + // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. + + std::vector sortedAvatars; auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -633,47 +662,60 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector continue; } - float parabolicDistance; - BoxFace face; - glm::vec3 surfaceNormal; - - SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); - - // It's better to intersect the parabola against the avatar's actual mesh, but this is currently difficult to - // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code - // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking - // against the avatar is sort-of right, but you likely wont be able to pick against the arms. - - // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. - + float distance = FLT_MAX; +#if 0 // if we weren't picking against the capsule, we would want to pick against the avatarBounds... - // AABox avatarBounds = avatarModel->getRenderableMeshBound(); - // if (!avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal)) { - // // parabola doesn't intersect avatar's bounding-box - // continue; - // } - + SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); + AABox avatarBounds = avatarModel->getRenderableMeshBound(); + if (avatarBounds.contains(pick.origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, boundDistance, face, surfaceNormal)) { + distance = boundDistance; + } + } +#else glm::vec3 start; glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), parabolicDistance); - if (!intersects) { - // ray doesn't intersect avatar's capsule - continue; + findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), distance); +#endif + + if (distance < FLT_MAX) { + sortedAvatars.emplace_back(distance, avatar); + } + } + + if (sortedAvatars.size() > 1) { + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); + } + + for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) { + const SortedAvatar& sortedAvatar = *it; + // We can exit once avatarCapsuleDistance > bestDistance + if (sortedAvatar.first > result.parabolicDistance) { + break; } + float parabolicDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; QVariantMap extraInfo; - intersects = avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, - parabolicDistance, face, surfaceNormal, extraInfo, true); - - if (intersects && (!result.intersects || parabolicDistance < result.parabolicDistance)) { - result.intersects = true; - result.avatarID = avatar->getID(); - result.parabolicDistance = parabolicDistance; - result.face = face; - result.surfaceNormal = surfaceNormal; - result.extraInfo = extraInfo; + SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel(); + if (avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal, extraInfo, true)) { + if (parabolicDistance < result.parabolicDistance) { + result.intersects = true; + result.avatarID = sortedAvatar.second->getID(); + result.parabolicDistance = parabolicDistance; + result.face = face; + result.surfaceNormal = surfaceNormal; + result.extraInfo = extraInfo; + } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ecf9a2d735..a9c04e8473 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -27,6 +27,8 @@ #include "AvatarMotionState.h" #include "MyAvatar.h" +using SortedAvatar = std::pair>; + /**jsdoc * The AvatarManager API has properties and methods which manage Avatars within the same domain. * diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index b3e3f16345..f4db1df25a 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -13,11 +13,13 @@ #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" +#include "PickManager.h" PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToEntityIntersectionResult entityRes = - DependencyManager::get()->findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), + DependencyManager::get()->findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); @@ -28,8 +30,9 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), + qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 96b41dcc72..fde1da3f87 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -13,10 +13,12 @@ #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" +#include "PickManager.h" PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToEntityIntersectionResult entityRes = - DependencyManager::get()->findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), + DependencyManager::get()->findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); @@ -26,8 +28,9 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { } PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), + qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 53a2a69119..9d4ba3902d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1559,7 +1559,7 @@ class RayToAvatarIntersectionResult { public: bool intersects { false }; QUuid avatarID; - float distance { 0.0f }; + float distance { FLT_MAX }; BoxFace face; glm::vec3 intersection; glm::vec3 surfaceNormal; diff --git a/libraries/pointers/src/Pick.cpp b/libraries/pointers/src/Pick.cpp index 4315409bdb..7ac53a2643 100644 --- a/libraries/pointers/src/Pick.cpp +++ b/libraries/pointers/src/Pick.cpp @@ -72,11 +72,15 @@ PickResultPointer PickQuery::getPrevPickResult() const { void PickQuery::setIgnoreItems(const QVector& ignoreItems) { withWriteLock([&] { _ignoreItems = ignoreItems; + // We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same ignoreItems in a different order + std::sort(_ignoreItems.begin(), _ignoreItems.end(), std::less()); }); } void PickQuery::setIncludeItems(const QVector& includeItems) { withWriteLock([&] { _includeItems = includeItems; + // We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same includeItems in a different order + std::sort(_includeItems.begin(), _includeItems.end(), std::less()); }); } \ No newline at end of file diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 242550d837..595c43e71d 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -16,7 +16,10 @@ #include -class PickManager : public Dependency, protected ReadWriteLockable { +#include + +class PickManager : public QObject, public Dependency, protected ReadWriteLockable { + Q_OBJECT SINGLETON_DEPENDENCY public: @@ -53,7 +56,13 @@ public: unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; } void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; } + bool getForceCoarsePicking() { return _forceCoarsePicking; } + +public slots: + void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } + protected: + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index f3c0ef5776..1dc8c5657a 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -29,7 +29,7 @@ void TriangleSet::clear() { _bounds.clear(); _isBalanced = false; - _triangleOctree.clear(); + _triangleTree.clear(); } bool TriangleSet::convexHullContains(const glm::vec3& point) const { @@ -53,16 +53,16 @@ void TriangleSet::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "triangles:" << size() << "at top level...."; - qDebug() << "----- _triangleOctree -----"; - _triangleOctree.debugDump(); + qDebug() << "----- _triangleTree -----"; + _triangleTree.debugDump(); } -void TriangleSet::balanceOctree() { - _triangleOctree.reset(_bounds); +void TriangleSet::balanceTree() { + _triangleTree.reset(_bounds); // insert all the triangles for (size_t i = 0; i < _triangles.size(); i++) { - _triangleOctree.insert(i); + _triangleTree.insert(i); } _isBalanced = true; @@ -77,13 +77,13 @@ void TriangleSet::balanceOctree() { // With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves static const int MAX_DEPTH = 12; -TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : +TriangleSet::TriangleTreeCell::TriangleTreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) { reset(bounds, depth); } -void TriangleSet::TriangleOctreeCell::clear() { +void TriangleSet::TriangleTreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); @@ -91,13 +91,13 @@ void TriangleSet::TriangleOctreeCell::clear() { _children.second.reset(); } -void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { +void TriangleSet::TriangleTreeCell::reset(const AABox& bounds, int depth) { clear(); _bounds = bounds; _depth = depth; } -void TriangleSet::TriangleOctreeCell::debugDump() { +void TriangleSet::TriangleTreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << " bounds:" << getBounds(); qDebug() << " depth:" << _depth; @@ -123,7 +123,7 @@ void TriangleSet::TriangleOctreeCell::debugDump() { } } -std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { +std::pair TriangleSet::TriangleTreeCell::getTriangleTreeCellChildBounds() { std::pair toReturn; int axis = 0; // find biggest axis @@ -145,20 +145,20 @@ std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellCh return toReturn; } -void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { +void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) { _population++; // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { const Triangle& triangle = _allTriangles[triangleIndex]; - auto childBounds = getTriangleOctreeCellChildBounds(); + auto childBounds = getTriangleTreeCellChildBounds(); - auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { + auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { // if the child AABox would contain the triangle... if (childBound.contains(triangle)) { // if the child cell doesn't yet exist, create it... if (!child) { - child = std::make_shared(_allTriangles, childBound, _depth + 1); + child = std::make_shared(_allTriangles, childBound, _depth + 1); } // insert the triangleIndex in the child cell @@ -179,19 +179,19 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { - balanceOctree(); + balanceTree(); } float localDistance = distance; int trianglesTouched = 0; - bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { distance = localDistance; } #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size(); } #endif return hit; @@ -199,7 +199,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& // Determine of the given ray (origin/direction) in model space intersects with any triangles // in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, +bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { bool intersectedSomething = false; @@ -232,7 +232,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec return intersectedSomething; } -bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { if (_population < 1) { @@ -261,7 +261,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi // if we're not yet at the max depth, then check our children if (_depth < MAX_DEPTH) { std::list sortedTriangleCells; - auto sortingOperator = [&](std::shared_ptr& child) { + auto sortingOperator = [&](std::shared_ptr& child) { if (child) { float priority = FLT_MAX; if (child->getBounds().contains(origin)) { @@ -328,25 +328,25 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { - balanceOctree(); + balanceTree(); } float localDistance = parabolicDistance; int trianglesTouched = 0; - bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { parabolicDistance = localDistance; } #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size(); } #endif return hit; } -bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, +bool TriangleSet::TriangleTreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -380,7 +380,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm return intersectedSomething; } -bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, +bool TriangleSet::TriangleTreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -410,7 +410,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& // if we're not yet at the max depth, then check our children if (_depth < MAX_DEPTH) { std::list sortedTriangleCells; - auto sortingOperator = [&](std::shared_ptr& child) { + auto sortingOperator = [&](std::shared_ptr& child) { if (child) { float priority = FLT_MAX; if (child->getBounds().contains(origin)) { diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index a838e2d9e2..84e362f0d0 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -19,10 +19,10 @@ class TriangleSet { - class TriangleOctreeCell { + class TriangleTreeCell { public: - TriangleOctreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} - TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); + TriangleTreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} + TriangleTreeCell(std::vector& allTriangles, const AABox& bounds, int depth); void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); @@ -48,10 +48,10 @@ class TriangleSet { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); - std::pair getTriangleOctreeCellChildBounds(); + std::pair getTriangleTreeCellChildBounds(); std::vector& _allTriangles; - std::pair, std::shared_ptr> _children; + std::pair, std::shared_ptr> _children; int _depth { 0 }; int _population { 0 }; AABox _bounds; @@ -60,10 +60,10 @@ class TriangleSet { friend class TriangleSet; }; - using SortedTriangleCell = std::pair>; + using SortedTriangleCell = std::pair>; public: - TriangleSet() : _triangleOctree(_triangles) {} + TriangleSet() : _triangleTree(_triangles) {} void debugDump(); @@ -74,7 +74,7 @@ public: bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); - void balanceOctree(); + void balanceTree(); void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles size_t size() const { return _triangles.size(); } @@ -89,6 +89,6 @@ public: protected: bool _isBalanced { false }; std::vector _triangles; - TriangleOctreeCell _triangleOctree; + TriangleTreeCell _triangleTree; AABox _bounds; }; From e3ad5adcaec07108360e340302cd643c61e4af31 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 06:49:35 -0700 Subject: [PATCH 14/42] first approach vertical alignment --- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/avatars/src/AvatarData.h | 4 ++ libraries/avatars/src/AvatarHashMap.cpp | 72 +++++++++++++++++++++++-- libraries/avatars/src/AvatarHashMap.h | 2 + 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c1ec19b307..63a905aa6e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -918,7 +918,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]); + auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + glm::vec3(0, 2 * _surrogateIndex, 0); if (_globalPosition != newValue) { _globalPosition = newValue; _globalPositionChanged = usecTimestampNow(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 53a2a69119..b3443dbd8a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1186,6 +1186,9 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {} virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {} + void setSurrogateIndex(int surrogateIndex) { _surrogateIndex = surrogateIndex; } + int getSurrogateIndex() { return _surrogateIndex; } + signals: /**jsdoc @@ -1443,6 +1446,7 @@ protected: udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; + int _surrogateIndex{ 0 }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 64b26131be..a84d83e663 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,6 +21,8 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" +const int SURROGATE_COUNT = 2; + AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); @@ -139,14 +141,37 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointerisIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); + + if (isNewAvatar) { QWriteLocker locker(&_hashLock); _pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar }); + std::vector surrogateIDs; + for (int i = 0; i < SURROGATE_COUNT; i++) { + QUuid surrogateID = QUuid::createUuid(); + surrogateIDs.push_back(surrogateID); + auto surrogateAvatar = addAvatar(surrogateID, sendingNode); + surrogateAvatar->setSurrogateIndex(i + 1); + surrogateAvatar->parseDataFromBuffer(byteArray); + _pendingAvatars.insert(surrogateID, { std::chrono::steady_clock::now(), 0, surrogateAvatar }); + } + _surrogates.insert(std::pair>(sessionUUID, surrogateIDs)); + } else { + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); + if (!isNewAvatar) { + surrogateAvatar->parseDataFromBuffer(byteArray); + } + } + } // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); + + avatar->parseDataFromBuffer(byteArray); return avatar; } else { // create a dummy AvatarData class to throw this data on the ground @@ -191,13 +216,22 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer bool displayNameChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + auto surrogateIDs = _surrogates[identityUUID]; + for (auto id : surrogateIDs) { + auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); + if (!isNewAvatar) { + surrogateAvatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + } + } } } -void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { +void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { + message->seek(0); while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + avatarID = sessionUUID; // grab the avatar so we can ask it to process trait data bool isNewAvatar; @@ -225,10 +259,12 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (packetTraitVersion > lastProcessedVersions[traitType]) { avatar->processTrait(traitType, message->read(traitBinarySize)); lastProcessedVersions[traitType] = packetTraitVersion; - } else { + } + else { skipBinaryTrait = true; } - } else { + } + else { AvatarTraits::TraitInstanceID traitInstanceID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -238,11 +274,13 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (packetTraitVersion > processedInstanceVersion) { if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } else { + } + else { avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); } processedInstanceVersion = packetTraitVersion; - } else { + } + else { skipBinaryTrait = true; } } @@ -258,6 +296,18 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess } } +void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + processAvatarTraits(avatarID, message, sendingNode); + auto surrogateIDs = _surrogates[avatarID]; + for (auto id : surrogateIDs) { + processAvatarTraits(id, message, sendingNode); + } + } +} + void AvatarHashMap::processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode) { // read the node id QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -265,6 +315,10 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S KillAvatarReason reason; message->readPrimitive(&reason); removeAvatar(sessionUUID, reason); + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + removeAvatar(id, reason); + } } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { @@ -276,6 +330,14 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo if (removedAvatar) { handleRemovedAvatar(removedAvatar, removalReason); } + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + _pendingAvatars.remove(id); + auto removedSurrogate = _avatarHash.take(id); + if (removedSurrogate) { + handleRemovedAvatar(removedSurrogate, removalReason); + } + } } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index ba16fa9568..41981df693 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -134,6 +134,7 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); + void processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -167,6 +168,7 @@ protected: mutable QReadWriteLock _hashLock; std::unordered_map _processedTraitVersions; + std::map> _surrogates; private: QUuid _lastOwnerSessionUUID; }; From 3d959ca7ee6c59496f30dc15f6da02f666376166 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 15:52:42 -0700 Subject: [PATCH 15/42] Refactoring and cleaning --- .../src/avatars-renderer/Avatar.h | 1 - libraries/avatars/src/AvatarData.cpp | 21 +- libraries/avatars/src/AvatarData.h | 8 +- libraries/avatars/src/AvatarHashMap.cpp | 223 ++++++++++++++---- libraries/avatars/src/AvatarHashMap.h | 23 +- 5 files changed, 223 insertions(+), 53 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6e8ac59f1..63aa6d9b6f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -464,7 +464,6 @@ protected: glm::vec3 _lastAngularVelocity; glm::vec3 _angularAcceleration; glm::quat _lastOrientation; - glm::vec3 _worldUpDirection { Vectors::UP }; bool _moving { false }; ///< set when position is changing diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 63a905aa6e..ddb53f8acb 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -918,7 +918,26 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + glm::vec3(0, 2 * _surrogateIndex, 0); + + const float SPACE_BETWEEN_AVATARS = 2.0f; + const int RANDOM_RADIUS = 100; + const int AVATARS_PER_ROW = 3; + + glm::vec3 offset; + + if (false) { + qsrand(static_cast(getID().toByteArray().toInt())); + float xrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); + float yrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); + offset = glm::vec3(xrand, 0.0f, yrand); + } + else { + int row = _replicaIndex % AVATARS_PER_ROW; + int col = floor(_replicaIndex / AVATARS_PER_ROW); + offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); + } + + auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; if (_globalPosition != newValue) { _globalPosition = newValue; _globalPositionChanged = usecTimestampNow(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b3443dbd8a..36cc0f936d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -337,6 +337,7 @@ enum KillAvatarReason : uint8_t { TheirAvatarEnteredYourBubble, YourAvatarEnteredTheirBubble }; + Q_DECLARE_METATYPE(KillAvatarReason); class QDataStream; @@ -1185,9 +1186,8 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {} virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {} - - void setSurrogateIndex(int surrogateIndex) { _surrogateIndex = surrogateIndex; } - int getSurrogateIndex() { return _surrogateIndex; } + void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; } + int getReplicaIndex() { return _replicaIndex; } signals: @@ -1446,7 +1446,7 @@ protected: udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; - int _surrogateIndex{ 0 }; + int _replicaIndex { 0 }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index a84d83e663..800825f574 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,7 +21,80 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" -const int SURROGATE_COUNT = 2; + +void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { + if (_replicasMap.find(parentID) == _replicasMap.end()) { + std::vector emptyReplicas = std::vector(); + _replicasMap.insert(std::pair>(parentID, emptyReplicas)); + } + auto &replicas = _replicasMap[parentID]; + replica->setReplicaIndex((int)replicas.size() + 1); + replicas.push_back(replica); +} + +std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID, int count) { + std::vector ids; + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (int i = 0; i < replicas.size(); i++) { + ids.push_back(replicas[i]->getID()); + } + } else if (count > 0) { + for (int i = 0; i < count; i++) { + ids.push_back(QUuid::createUuid()); + } + } + return ids; +} + +void AvatarReplicas::parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->parseDataFromBuffer(buffer); + } + } +} + +void AvatarReplicas::removeReplicas(const QUuid& parentID) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + _replicasMap.erase(parentID); + } +} + +void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processAvatarIdentity(identityData, identityChanged, displayNameChanged); + } + } +} +void AvatarReplicas::processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processTrait(traitType, traitBinaryData); + } + } +} +void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processDeletedTraitInstance(traitType, instanceID); + } + } +} +void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processTraitInstance(traitType, instanceID, traitBinaryData); + } + } +} AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); @@ -138,40 +211,28 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer(); + const int REPLICAS_COUNT = 8; + bool isNewAvatar; if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); - - + if (isNewAvatar) { QWriteLocker locker(&_hashLock); _pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar }); - std::vector surrogateIDs; - for (int i = 0; i < SURROGATE_COUNT; i++) { - QUuid surrogateID = QUuid::createUuid(); - surrogateIDs.push_back(surrogateID); - auto surrogateAvatar = addAvatar(surrogateID, sendingNode); - surrogateAvatar->setSurrogateIndex(i + 1); - surrogateAvatar->parseDataFromBuffer(byteArray); - _pendingAvatars.insert(surrogateID, { std::chrono::steady_clock::now(), 0, surrogateAvatar }); + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID, REPLICAS_COUNT); + for (auto replicaID : replicaIDs) { + auto replicaAvatar = addAvatar(replicaID, sendingNode); + _replicas.addReplica(sessionUUID, replicaAvatar); + _pendingAvatars.insert(replicaID, { std::chrono::steady_clock::now(), 0, replicaAvatar }); } - _surrogates.insert(std::pair>(sessionUUID, surrogateIDs)); - } else { - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { - auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); - if (!isNewAvatar) { - surrogateAvatar->parseDataFromBuffer(byteArray); - } - } - - } + } // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); + _replicas.parseDataFromBuffer(sessionUUID, byteArray); - avatar->parseDataFromBuffer(byteArray); return avatar; } else { // create a dummy AvatarData class to throw this data on the ground @@ -216,17 +277,11 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer bool displayNameChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - auto surrogateIDs = _surrogates[identityUUID]; - for (auto id : surrogateIDs) { - auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); - if (!isNewAvatar) { - surrogateAvatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - } - } + _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); } } -void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { +void AvatarHashMap::processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { message->seek(0); while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for @@ -297,13 +352,88 @@ void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { + /* while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - processAvatarTraits(avatarID, message, sendingNode); - auto surrogateIDs = _surrogates[avatarID]; - for (auto id : surrogateIDs) { - processAvatarTraits(id, message, sendingNode); + processBulkAvatarTraitsForID(avatarID, message, sendingNode); + auto replicaIDs = _replicas.getReplicaIDs(avatarID); + for (auto id : replicaIDs) { + processBulkAvatarTraitsForID(id, message, sendingNode); + } + } + */ + int position = 0; + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + // grab the avatar so we can ask it to process trait data + bool isNewAvatar; + auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); + + // read the first trait type for this avatar + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + // grab the last trait versions for this avatar + auto& lastProcessedVersions = _processedTraitVersions[avatarID]; + + while (traitType != AvatarTraits::NullTrait) { + AvatarTraits::TraitVersion packetTraitVersion; + message->readPrimitive(&packetTraitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + bool skipBinaryTrait = false; + + + if (AvatarTraits::isSimpleTrait(traitType)) { + message->readPrimitive(&traitBinarySize); + + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + position = message->getPosition(); + avatar->processTrait(traitType, message->read(traitBinarySize)); + message->seek(position); + _replicas.processTrait(avatarID, traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } + else { + skipBinaryTrait = true; + } + } + else { + AvatarTraits::TraitInstanceID traitInstanceID = + QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + message->readPrimitive(&traitBinarySize); + + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); + _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); + } + else { + position = message->getPosition(); + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); + message->seek(position); + _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, message->read(traitBinarySize)); + } + processedInstanceVersion = packetTraitVersion; + } + else { + skipBinaryTrait = true; + } + } + + if (skipBinaryTrait) { + // we didn't read this trait because it was older or because we didn't have an avatar to process it for + message->seek(message->getPosition() + traitBinarySize); + } + + // read the next trait type, which is null if there are no more traits for this avatar + message->readPrimitive(&traitType); } } } @@ -315,8 +445,8 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S KillAvatarReason reason; message->readPrimitive(&reason); removeAvatar(sessionUUID, reason); - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); + for (auto id : replicaIDs) { removeAvatar(id, reason); } } @@ -324,20 +454,23 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { QWriteLocker locker(&_hashLock); + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); + _replicas.removeReplicas(sessionUUID); + for (auto id : replicaIDs) { + _pendingAvatars.remove(id); + auto removedReplica = _avatarHash.take(id); + if (removedReplica) { + handleRemovedAvatar(removedReplica, removalReason); + } + } + _pendingAvatars.remove(sessionUUID); auto removedAvatar = _avatarHash.take(sessionUUID); if (removedAvatar) { handleRemovedAvatar(removedAvatar, removalReason); } - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { - _pendingAvatars.remove(id); - auto removedSurrogate = _avatarHash.take(id); - if (removedSurrogate) { - handleRemovedAvatar(removedSurrogate, removalReason); - } - } + } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 41981df693..2d8d7f907f 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -41,6 +41,24 @@ * @hifi-assignment-client */ +class AvatarReplicas { +public: + AvatarReplicas() {}; + void addReplica(const QUuid& parentID, AvatarSharedPointer replica); + std::vector getReplicaIDs(const QUuid& parentID, int count = 0); + void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); + void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + void removeReplicas(const QUuid& parentID); + void processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData); + void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); + void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + +private: + std::map> _replicasMap; +}; + + class AvatarHashMap : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -134,7 +152,7 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); - void processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); + void processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -168,7 +186,8 @@ protected: mutable QReadWriteLock _hashLock; std::unordered_map _processedTraitVersions; - std::map> _surrogates; + AvatarReplicas _replicas; + private: QUuid _lastOwnerSessionUUID; }; From 220dbf586f6e6382d8dc8338c48ec9b42e210ce2 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 16:46:58 -0700 Subject: [PATCH 16/42] set the phase to 0.0 when it is negative --- libraries/animation/src/AnimBlendLinearMove.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 6313d4cbe9..27352fd546 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -155,6 +155,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn // integrate phase forward in time. _phase += omega * dt; + qCDebug(animation) << "the _phase is " << _phase; // detect loop trigger events if (_phase >= 1.0f) { @@ -172,4 +173,7 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; _phase = fmodf(frame / NUM_FRAMES, 1.0f); + if (_phase < 0.0f) { + _phase = 0.0f; // 1.0f + _phase; + } } From 1263db568823c5577bd7fa1cddf897acd7711c5a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Aug 2018 16:08:16 -0700 Subject: [PATCH 17/42] force NL reset if the domain changes session ID or local ID --- libraries/networking/src/NodeList.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index dd351ef940..4e8cbbf9db 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -273,6 +273,7 @@ void NodeList::reset(bool skipDomainHandlerReset) { // refresh the owner UUID to the NULL UUID setSessionUUID(QUuid()); + setSessionLocalID(Node::NULL_LOCAL_ID); // if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking if (_dtlsSocket) { @@ -647,6 +648,17 @@ void NodeList::processDomainServerList(QSharedPointer message) Node::LocalID newLocalID; packetStream >> newUUID; packetStream >> newLocalID; + + // when connected, if the session ID or local ID were not null and changed, we should reset + auto currentLocalID = getSessionLocalID(); + auto currentSessionID = getSessionUUID(); + if (_domainHandler.isConnected() && + ((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) || + (!currentSessionID.isNull() && newUUID != currentSessionID))) { + qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset"; + reset(true); + } + setSessionLocalID(newLocalID); setSessionUUID(newUUID); From 0cc302f68c9812325f19a55158503b5339ccd82b Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:28:38 -0700 Subject: [PATCH 18/42] set phase to 0.0 --- libraries/animation/src/AnimBlendLinearMove.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 27352fd546..d648b9966f 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -139,6 +139,8 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); assert(nextClipNode); + + float v0 = _characteristicSpeeds[prevPoseIndex]; float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float v1 = _characteristicSpeeds[nextPoseIndex]; @@ -153,9 +155,17 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn float f1 = nextClipNode->getStartFrame() + _phase * n1; nextClipNode->setCurrentFrame(f1); + + // integrate phase forward in time. _phase += omega * dt; - qCDebug(animation) << "the _phase is " << _phase; + + qCDebug(animation) << "the _phase is " << _phase << " and omega " << omega << _desiredSpeed; + + if (_phase < 0.0f) { + _phase = 0.0f; // 1.0f + _phase; + } + // detect loop trigger events if (_phase >= 1.0f) { @@ -173,7 +183,4 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; _phase = fmodf(frame / NUM_FRAMES, 1.0f); - if (_phase < 0.0f) { - _phase = 0.0f; // 1.0f + _phase; - } } From 884be2f99d4a7ea417260902880e412264ad2d5a Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:30:03 -0700 Subject: [PATCH 19/42] removed comment and whitespace --- libraries/animation/src/AnimBlendLinearMove.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index d648b9966f..8879f3d9d4 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -155,18 +155,13 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn float f1 = nextClipNode->getStartFrame() + _phase * n1; nextClipNode->setCurrentFrame(f1); - - // integrate phase forward in time. _phase += omega * dt; - - qCDebug(animation) << "the _phase is " << _phase << " and omega " << omega << _desiredSpeed; if (_phase < 0.0f) { - _phase = 0.0f; // 1.0f + _phase; + _phase = 0.0f; } - // detect loop trigger events if (_phase >= 1.0f) { triggersOut.setTrigger(_id + "Loop"); From 671bf32d5813ef84837e7d22fd83933d4f9cba5e Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:36:35 -0700 Subject: [PATCH 20/42] removed whitespace --- libraries/animation/src/AnimBlendLinearMove.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 8879f3d9d4..42098eb072 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -139,8 +139,6 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); assert(nextClipNode); - - float v0 = _characteristicSpeeds[prevPoseIndex]; float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float v1 = _characteristicSpeeds[nextPoseIndex]; From b5b71676448c1805321831e6c363076d1a4c0f94 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 23 Aug 2018 17:47:25 -0700 Subject: [PATCH 21/42] add to stats --- interface/resources/qml/Stats.qml | 16 ++++ interface/src/ui/Stats.cpp | 17 ++++- interface/src/ui/Stats.h | 75 ++++++++++++++++++- .../src/RenderablePolyVoxEntityItem.cpp | 1 - libraries/entities/src/EntityTreeElement.cpp | 6 -- libraries/pointers/src/PickCacheOptimizer.h | 10 ++- libraries/pointers/src/PickManager.cpp | 14 ++-- libraries/pointers/src/PickManager.h | 6 ++ libraries/render-utils/src/Model.cpp | 12 +-- 9 files changed, 134 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2e6e909312..8b277d4c20 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -116,6 +116,22 @@ Item { visible: root.expanded text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount } + StatText { + visible: root.expanded + text: "Total picks:\n " + + root.stylusPicksCount + " styluses\n " + + root.rayPicksCount + " rays\n " + + root.parabolaPicksCount + " parabolas\n " + + root.collisionPicksCount + " colliders" + } + StatText { + visible: root.expanded + text: "Intersection calls: Entities/Overlays/Avatars/HUD\n " + + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "/" + root.stylusPicksUpdated.w + "\n " + + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "/" + root.rayPicksUpdated.w + "\n " + + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "/" + root.parabolaPicksUpdated.w + "\n " + + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z + "/" + root.collisionPicksUpdated.w + } } } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ce1cd51de1..16e2bb955f 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -146,6 +147,20 @@ void Stats::updateStats(bool force) { } STAT_UPDATE(gameLoopRate, (int)qApp->getGameLoopRate()); + auto pickManager = DependencyManager::get(); + if (pickManager && (_expanded || force)) { + std::vector totalPicks = pickManager->getTotalPickCounts(); + STAT_UPDATE(stylusPicksCount, totalPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksCount, totalPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksCount, totalPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksCount, totalPicks[PickQuery::Collision]); + std::vector updatedPicks = pickManager->getUpdatedPickCounts(); + STAT_UPDATE(stylusPicksUpdated, updatedPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksUpdated, updatedPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksUpdated, updatedPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksUpdated, updatedPicks[PickQuery::Collision]); + } + auto bandwidthRecorder = DependencyManager::get(); STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, (int)bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); @@ -285,7 +300,7 @@ void Stats::updateStats(bool force) { // downloads << (int)(resource->getProgress() * 100.0f) << "% "; //} //downloads << "(" << << " pending)"; - } // expanded avatar column + } // Fourth column, octree stats int serverCount = 0; diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index cf624b54c3..7845a540de 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -22,7 +22,6 @@ public: \ private: \ type _##name{ initialValue }; - /**jsdoc * @namespace Stats * @@ -168,6 +167,15 @@ private: \ * @property {number} implicitHeight * * @property {object} layer - Read-only. + + * @property {number} stylusPicksCount - Read-only. + * @property {number} rayPicksCount - Read-only. + * @property {number} parabolaPicksCount - Read-only. + * @property {number} collisionPicksCount - Read-only. + * @property {Vec4} stylusPicksUpdated - Read-only. + * @property {Vec4} rayPicksUpdated - Read-only. + * @property {Vec4} parabolaPicksUpdated - Read-only. + * @property {Vec4} collisionPicksUpdated - Read-only. */ // Properties from x onwards are QQuickItem properties. @@ -285,6 +293,15 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, avatarSimulationTime, 0) Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged) + STATS_PROPERTY(int, stylusPicksCount, 0) + STATS_PROPERTY(int, rayPicksCount, 0) + STATS_PROPERTY(int, parabolaPicksCount, 0) + STATS_PROPERTY(int, collisionPicksCount, 0) + STATS_PROPERTY(QVector4D, stylusPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, rayPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, parabolaPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, collisionPicksUpdated, QVector4D(0, 0, 0, 0)) + public: static Stats* getInstance(); @@ -1245,6 +1262,62 @@ signals: * @function Stats.update */ + /**jsdoc + * Triggered when the value of the stylusPicksCount property changes. + * @function Stats.stylusPicksCountChanged + * @returns {Signal} + */ + void stylusPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksCount property changes. + * @function Stats.rayPicksCountChanged + * @returns {Signal} + */ + void rayPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksCount property changes. + * @function Stats.parabolaPicksCountChanged + * @returns {Signal} + */ + void parabolaPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksCount property changes. + * @function Stats.collisionPicksCountChanged + * @returns {Signal} + */ + void collisionPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the stylusPicksUpdated property changes. + * @function Stats.stylusPicksUpdatedChanged + * @returns {Signal} + */ + void stylusPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksUpdated property changes. + * @function Stats.rayPicksUpdatedChanged + * @returns {Signal} + */ + void rayPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksUpdated property changes. + * @function Stats.parabolaPicksUpdatedChanged + * @returns {Signal} + */ + void parabolaPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksUpdated property changes. + * @function Stats.collisionPicksUpdatedChanged + * @returns {Signal} + */ + void collisionPicksUpdatedChanged(); + private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon{ true }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 63f27dd170..af8d4fac86 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -571,7 +571,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o } glm::mat4 wtvMatrix = worldToVoxelMatrix(); - glm::mat4 vtwMatrix = voxelToWorldMatrix(); glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index e8e11c0ee1..b7288755a6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -145,7 +145,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -153,8 +152,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = distance; EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails, @@ -285,7 +282,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -293,8 +289,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = parabolicDistance; // We can precompute the world-space parabola normal and reuse it for the parabola plane intersects AABox sphere check diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index 49a039935c..7a52cfc410 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - void update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); + QVector4D update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,8 +67,9 @@ void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultP } template -void PickCacheOptimizer::update(std::unordered_map>& picks, +QVector4D PickCacheOptimizer::update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) { + QVector4D numIntersectionsComputed; PickCache results; const uint32_t INVALID_PICK_ID = 0; auto itr = picks.begin(); @@ -91,6 +92,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); + numIntersectionsComputed[0]++; if (entityRes) { cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick); } @@ -101,6 +103,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); + numIntersectionsComputed[1]++; if (overlayRes) { cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); } @@ -111,6 +114,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) { PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick); + numIntersectionsComputed[2]++; if (avatarRes) { cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } @@ -122,6 +126,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getHUDFlags(), QVector(), QVector() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) { PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick); + numIntersectionsComputed[3]++; if (hudRes) { cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick); } @@ -145,6 +150,7 @@ void PickCacheOptimizer::update(std::unordered_mapsecond].erase(uid); _typeMap.erase(uid); + _totalPickCounts[type->second]--; } }); } @@ -96,12 +98,12 @@ void PickManager::update() { }); bool shouldPickHUD = _shouldPickHUDOperator(); - // we pass the same expiry to both updates, but the stylus updates are relatively cheap - // and the rayPicks updae will ALWAYS update at least one ray even when there is no budget - _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); - _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); - _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); - _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); + // FIXME: give each type its own expiry + // Each type will update at least one pick, regardless of the expiry + _updatedPickCounts[PickQuery::Stylus] = _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); + _updatedPickCounts[PickQuery::Ray] = _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Parabola] = _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Collision] = _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); } bool PickManager::isLeftHand(unsigned int uid) { diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 595c43e71d..e8d32771e0 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -58,10 +58,16 @@ public: bool getForceCoarsePicking() { return _forceCoarsePicking; } + const std::vector& getUpdatedPickCounts() { return _updatedPickCounts; } + const std::vector& getTotalPickCounts() { return _totalPickCounts; } + public slots: void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } protected: + std::vector _updatedPickCounts { PickQuery::NUM_PICK_TYPES }; + std::vector _totalPickCounts { 0, 0, 0, 0 }; + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 22bc6b6ca7..ae3ee6e9a2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -385,9 +385,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { @@ -527,9 +527,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { From 1fd0d7da94c3cca2f4dc367cc41f8e84eed1f072 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 18:03:27 -0700 Subject: [PATCH 22/42] Create API call --- libraries/avatars/src/AvatarHashMap.cpp | 24 +++++++++++++++++------- libraries/avatars/src/AvatarHashMap.h | 12 ++++++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 800825f574..6949efc19c 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -32,15 +32,15 @@ void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer repli replicas.push_back(replica); } -std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID, int count) { +std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID) { std::vector ids; if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; for (int i = 0; i < replicas.size(); i++) { ids.push_back(replicas[i]->getID()); } - } else if (count > 0) { - for (int i = 0; i < count; i++) { + } else if (_replicaCount > 0) { + for (int i = 0; i < _replicaCount; i++) { ids.push_back(QUuid::createUuid()); } } @@ -139,6 +139,19 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range return false; } +void AvatarHashMap::setReplicaCount(int count) { + _replicas.setReplicaCount(count); + auto avatars = getAvatarIdentifiers(); + for (int i = 0; i < avatars.size(); i++) { + KillAvatarReason reason = KillAvatarReason::NoReason; + removeAvatar(avatars[i], reason); + auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); + for (auto id : replicaIDs) { + removeAvatar(id, reason); + } + } +} + int AvatarHashMap::numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters) { auto hashCopy = getHashCopy(); auto rangeMeters2 = rangeMeters * rangeMeters; @@ -210,9 +223,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer(); - - const int REPLICAS_COUNT = 8; - bool isNewAvatar; if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); @@ -220,7 +230,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer getReplicaIDs(const QUuid& parentID, int count = 0); + std::vector getReplicaIDs(const QUuid& parentID); void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); void removeReplicas(const QUuid& parentID); @@ -53,9 +53,11 @@ public: void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + void setReplicaCount(int count) { _replicaCount = count; } private: std::map> _replicasMap; + int _replicaCount; }; @@ -92,6 +94,12 @@ public: // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } + /**jsdoc + * @function AvatarList.setReplicaCount + * @param {number} count // The times an avatar will get replicated + */ + Q_INVOKABLE void setReplicaCount(int count); + virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); From 5654acf5bf28c9476402a1bc6a2cb4ef3545bf07 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 24 Aug 2018 09:24:58 -0700 Subject: [PATCH 23/42] Fix warnings --- libraries/avatars/src/AvatarHashMap.cpp | 99 ++----------------------- libraries/avatars/src/AvatarHashMap.h | 8 +- 2 files changed, 15 insertions(+), 92 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 6949efc19c..67cc9f0563 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -36,7 +36,7 @@ std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID) { std::vector ids; if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; - for (int i = 0; i < replicas.size(); i++) { + for (int i = 0; i < (int)replicas.size(); i++) { ids.push_back(replicas[i]->getID()); } } else if (_replicaCount > 0) { @@ -288,92 +288,12 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); - } -} -void AvatarHashMap::processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { - message->seek(0); - while (message->getBytesLeftToRead()) { - // read the avatar ID to figure out which avatar this is for - auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - avatarID = sessionUUID; - - // grab the avatar so we can ask it to process trait data - bool isNewAvatar; - auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); - - // read the first trait type for this avatar - AvatarTraits::TraitType traitType; - message->readPrimitive(&traitType); - - // grab the last trait versions for this avatar - auto& lastProcessedVersions = _processedTraitVersions[avatarID]; - - while (traitType != AvatarTraits::NullTrait) { - AvatarTraits::TraitVersion packetTraitVersion; - message->readPrimitive(&packetTraitVersion); - - AvatarTraits::TraitWireSize traitBinarySize; - bool skipBinaryTrait = false; - - - if (AvatarTraits::isSimpleTrait(traitType)) { - message->readPrimitive(&traitBinarySize); - - // check if this trait version is newer than what we already have for this avatar - if (packetTraitVersion > lastProcessedVersions[traitType]) { - avatar->processTrait(traitType, message->read(traitBinarySize)); - lastProcessedVersions[traitType] = packetTraitVersion; - } - else { - skipBinaryTrait = true; - } - } - else { - AvatarTraits::TraitInstanceID traitInstanceID = - QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - message->readPrimitive(&traitBinarySize); - - auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); - if (packetTraitVersion > processedInstanceVersion) { - if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { - avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } - else { - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - } - processedInstanceVersion = packetTraitVersion; - } - else { - skipBinaryTrait = true; - } - } - - if (skipBinaryTrait) { - // we didn't read this trait because it was older or because we didn't have an avatar to process it for - message->seek(message->getPosition() + traitBinarySize); - } - - // read the next trait type, which is null if there are no more traits for this avatar - message->readPrimitive(&traitType); - } } } void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { - /* - while (message->getBytesLeftToRead()) { - // read the avatar ID to figure out which avatar this is for - auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - processBulkAvatarTraitsForID(avatarID, message, sendingNode); - auto replicaIDs = _replicas.getReplicaIDs(avatarID); - for (auto id : replicaIDs) { - processBulkAvatarTraitsForID(id, message, sendingNode); - } - } - */ - int position = 0; + while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -381,7 +301,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // grab the avatar so we can ask it to process trait data bool isNewAvatar; auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); - // read the first trait type for this avatar AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); @@ -402,10 +321,9 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // check if this trait version is newer than what we already have for this avatar if (packetTraitVersion > lastProcessedVersions[traitType]) { - position = message->getPosition(); - avatar->processTrait(traitType, message->read(traitBinarySize)); - message->seek(position); - _replicas.processTrait(avatarID, traitType, message->read(traitBinarySize)); + auto traitData = message->read(traitBinarySize); + avatar->processTrait(traitType, traitData); + _replicas.processTrait(avatarID, traitType, traitData); lastProcessedVersions[traitType] = packetTraitVersion; } else { @@ -425,10 +343,9 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); } else { - position = message->getPosition(); - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - message->seek(position); - _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, message->read(traitBinarySize)); + auto traitData = message->read(traitBinarySize); + avatar->processTraitInstance(traitType, traitInstanceID, traitData); + _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData); } processedInstanceVersion = packetTraitVersion; } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index b7199d25ac..97c54e1787 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -54,6 +54,7 @@ public: void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); void setReplicaCount(int count) { _replicaCount = count; } + int getReplicaCount() { return _replicaCount; } private: std::map> _replicasMap; @@ -99,6 +100,12 @@ public: * @param {number} count // The times an avatar will get replicated */ Q_INVOKABLE void setReplicaCount(int count); + + /**jsdoc + * @function AvatarList.setReplicaCount + * @param {number} count // The times an avatar will get replicated + */ + Q_INVOKABLE int getReplicaCount() { return _replicas.getReplicaCount(); }; virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); @@ -160,7 +167,6 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); - void processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc From 1c0c17cecbb879901301feaaa73b0868a5f1335e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 11:20:10 -0700 Subject: [PATCH 24/42] faster triangle intersection --- libraries/shared/src/GeometryUtil.cpp | 38 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index a93c2ec9f3..c69b05db53 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -392,24 +392,34 @@ Triangle Triangle::operator*(const glm::mat4& transform) const { }; } +// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance, bool allowBackface) { - glm::vec3 firstSide = v0 - v1; - glm::vec3 secondSide = v2 - v1; - glm::vec3 normal = glm::cross(secondSide, firstSide); - float dividend = glm::dot(normal, v1) - glm::dot(origin, normal); - if (!allowBackface && dividend > 0.0f) { - return false; // origin below plane - } - float divisor = glm::dot(normal, direction); - if (divisor >= 0.0f) { + glm::vec3 firstSide = v1 - v0; + glm::vec3 secondSide = v2 - v0; + glm::vec3 P = glm::cross(direction, secondSide); + float det = glm::dot(firstSide, P); + if (!allowBackface && det < EPSILON) { + return false; + } else if (fabsf(det) < EPSILON) { return false; } - float t = dividend / divisor; - glm::vec3 point = origin + direction * t; - if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f && - glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f && - glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) { + + float invDet = 1.0f / det; + glm::vec3 T = origin - v0; + float u = glm::dot(T, P) * invDet; + if (u < 0.0f || u > 1.0f) { + return false; + } + + glm::vec3 Q = glm::cross(T, firstSide); + float v = glm::dot(direction, Q) * invDet; + if (v < 0.0f || u + v > 1.0f) { + return false; + } + + float t = glm::dot(secondSide, Q) * invDet; + if (t > EPSILON) { distance = t; return true; } From ddbadf5a6936e29a426bd862ffca06129cfd1fac Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 24 Aug 2018 11:59:46 -0700 Subject: [PATCH 25/42] change to Test API --- interface/src/Application.h | 3 +++ .../src/scripting/TestScriptingInterface.cpp | 8 ++++++++ interface/src/scripting/TestScriptingInterface.h | 14 ++++++++++++++ libraries/avatars/src/AvatarHashMap.h | 15 +++------------ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 742cf075f6..0d4417cd91 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -311,6 +311,9 @@ public: Q_INVOKABLE void copyToClipboard(const QString& text); + int getOtherAvatarsReplicaCount() { return DependencyManager::get()->getReplicaCount(); } + void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } + #if defined(Q_OS_ANDROID) void beforeEnterBackground(); void enterBackground(); diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 430441226f..52f6a3ebc0 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -190,4 +190,12 @@ void TestScriptingInterface::saveObject(QVariant variant, const QString& filenam void TestScriptingInterface::showMaximized() { qApp->getWindow()->showMaximized(); +} + +void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { + qApp->setOtherAvatarsReplicaCount(count); +} + +int TestScriptingInterface::getOtherAvatarsReplicaCount() { + return qApp->getOtherAvatarsReplicaCount(); } \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index c47e39d1f3..4a1d1a3eeb 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -149,6 +149,20 @@ public slots: */ void showMaximized(); + /**jsdoc + * Values higher than 0 will create replicas of other-avatars when entering a domain for testing purpouses + * @function Test.setOtherAvatarsReplicaCount + * @param {number} count - Number of replicas we want to create + */ + Q_INVOKABLE void setOtherAvatarsReplicaCount(int count); + + /**jsdoc + * Return the number of replicas that are being created of other-avatars when entering a domain + * @function Test.getOtherAvatarsReplicaCount + * @returns {number} Current number of replicas of other-avatars. + */ + Q_INVOKABLE int getOtherAvatarsReplicaCount(); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 97c54e1787..e4e197a080 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -95,21 +95,12 @@ public: // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } - /**jsdoc - * @function AvatarList.setReplicaCount - * @param {number} count // The times an avatar will get replicated - */ - Q_INVOKABLE void setReplicaCount(int count); - - /**jsdoc - * @function AvatarList.setReplicaCount - * @param {number} count // The times an avatar will get replicated - */ - Q_INVOKABLE int getReplicaCount() { return _replicas.getReplicaCount(); }; - virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); + void setReplicaCount(int count); + int getReplicaCount() { return _replicas.getReplicaCount(); }; + signals: /**jsdoc From cb4ce4fedb8bc6155b71ae08ad074037b750defd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 24 Aug 2018 13:05:04 -0700 Subject: [PATCH 26/42] make sure settings are re-requested on new connection --- libraries/networking/src/NodeList.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 4e8cbbf9db..e458ffab7e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -656,7 +656,13 @@ void NodeList::processDomainServerList(QSharedPointer message) ((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) || (!currentSessionID.isNull() && newUUID != currentSessionID))) { qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset"; + + // reset the nodelist, but don't do a domain handler reset since we're about to process a good domain list reset(true); + + // tell the domain handler that we're no longer connected so that below + // it can re-perform actions as if we just connected + _domainHandler.setIsConnected(false); } setSessionLocalID(newLocalID); From d8b5a1a4a6ddd80ec02ce3bce8911c6ff9d9766a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 24 Aug 2018 14:21:54 -0700 Subject: [PATCH 27/42] always remove old socket-matching node before add --- domain-server/src/DomainGatekeeper.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 2307b1be3b..e23d9e57a8 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -463,19 +463,15 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect limitedNodeList->eachNodeBreakable([nodeConnection, username, &existingNodeID](const SharedNodePointer& node){ if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) { - // we have a node that already has these exact sockets - this can occur if a node - // is failing to connect to the domain - - // we'll re-use the existing node ID - // as long as the user hasn't changed their username (by logging in or logging out) - auto existingNodeData = static_cast(node->getLinkedData()); - - if (existingNodeData->getUsername() == username) { - qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); - existingNodeID = node->getUUID(); - return false; - } + // we have a node that already has these exact sockets + // this can occur if a node is failing to connect to the domain + + // remove the old node before adding the new node + qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); + existingNodeID = node->getUUID(); + return false; } + return true; }); From 98d145dc2487508bcf1d4076892cb6f0cf14e3dc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 15:19:48 -0700 Subject: [PATCH 28/42] fix crash on shutdown in domain with material --- interface/src/Application.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e290531471..44bba6250b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1841,6 +1841,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) { + if (_aboutToQuit) { + return false; + } + // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); if (renderable) { @@ -1856,6 +1860,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return false; }); EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) { + if (_aboutToQuit) { + return false; + } + // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); if (renderable) { From ff6020e0ef2489f3cfbe5e883d1688b73a0b3971 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 15:41:25 -0700 Subject: [PATCH 29/42] fix compile errors, maybe --- libraries/render-utils/src/Model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 4fc8e265ab..71250c6483 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -319,7 +319,7 @@ bool Model::updateGeometry() { // Interleave normals and tangents // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { + tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end()); auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end()); auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin(); @@ -1281,7 +1281,7 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; const FBXBlendshape& blendshape = mesh.blendshapes.at(i); - tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { + tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { for (auto j = range.begin(); j < range.end(); j++) { int index = blendshape.indices.at(j); meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; From 8ede6f1cd09cf2ed33a006fa9629dae134fe0d5f Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 27 Aug 2018 06:55:52 -0700 Subject: [PATCH 30/42] Fix removing my avatar and code standards --- libraries/avatars/src/AvatarData.cpp | 16 ++++----------- libraries/avatars/src/AvatarHashMap.cpp | 26 ++++++++++++------------- libraries/avatars/src/AvatarHashMap.h | 4 ++-- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ddb53f8acb..5e1094276c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -919,19 +919,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - const float SPACE_BETWEEN_AVATARS = 2.0f; - const int RANDOM_RADIUS = 100; - const int AVATARS_PER_ROW = 3; + glm::vec3 offset = glm::vec3(0.0f, 0.0f, 0.0f); - glm::vec3 offset; - - if (false) { - qsrand(static_cast(getID().toByteArray().toInt())); - float xrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); - float yrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); - offset = glm::vec3(xrand, 0.0f, yrand); - } - else { + if (_replicaIndex > 0) { + const float SPACE_BETWEEN_AVATARS = 2.0f; + const int AVATARS_PER_ROW = 3; int row = _replicaIndex % AVATARS_PER_ROW; int col = floor(_replicaIndex / AVATARS_PER_ROW); offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 67cc9f0563..8dfee3551e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -23,6 +23,9 @@ void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { + if (parentID == QUuid()) { + return; + } if (_replicasMap.find(parentID) == _replicasMap.end()) { std::vector emptyReplicas = std::vector(); _replicasMap.insert(std::pair>(parentID, emptyReplicas)); @@ -144,10 +147,12 @@ void AvatarHashMap::setReplicaCount(int count) { auto avatars = getAvatarIdentifiers(); for (int i = 0; i < avatars.size(); i++) { KillAvatarReason reason = KillAvatarReason::NoReason; - removeAvatar(avatars[i], reason); - auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); - for (auto id : replicaIDs) { - removeAvatar(id, reason); + if (avatars[i] != QUuid()) { + removeAvatar(avatars[i], reason); + auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); + for (auto id : replicaIDs) { + removeAvatar(id, reason); + } } } } @@ -315,7 +320,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitWireSize traitBinarySize; bool skipBinaryTrait = false; - if (AvatarTraits::isSimpleTrait(traitType)) { message->readPrimitive(&traitBinarySize); @@ -325,12 +329,10 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess avatar->processTrait(traitType, traitData); _replicas.processTrait(avatarID, traitType, traitData); lastProcessedVersions[traitType] = packetTraitVersion; - } - else { + } else { skipBinaryTrait = true; } - } - else { + } else { AvatarTraits::TraitInstanceID traitInstanceID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -341,15 +343,13 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { avatar->processDeletedTraitInstance(traitType, traitInstanceID); _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); - } - else { + } else { auto traitData = message->read(traitBinarySize); avatar->processTraitInstance(traitType, traitInstanceID, traitData); _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData); } processedInstanceVersion = packetTraitVersion; - } - else { + } else { skipBinaryTrait = true; } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index e4e197a080..0f847b2a61 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -43,7 +43,7 @@ class AvatarReplicas { public: - AvatarReplicas() : _replicaCount(0) {} + AvatarReplicas() {} void addReplica(const QUuid& parentID, AvatarSharedPointer replica); std::vector getReplicaIDs(const QUuid& parentID); void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); @@ -58,7 +58,7 @@ public: private: std::map> _replicasMap; - int _replicaCount; + int _replicaCount { 0 }; }; From 945982b1bdfa576ab07a2e8cbb416ed1f34c44f3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 11 Jul 2018 14:05:07 -0700 Subject: [PATCH 31/42] Working on pauses in tomb domain --- libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp | 1 - libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 01424c2ac6..c1848d99b1 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -765,7 +765,6 @@ void GLBackend::recycle() const { } _textureManagement._transferEngine->manageMemory(); - Texture::KtxStorage::releaseOpenKtxFiles(); } void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp index eaee054b7d..f4e81448cc 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp @@ -405,6 +405,7 @@ bool GLTextureTransferEngineDefault::processActiveBufferQueue() { _activeTransferQueue.splice(_activeTransferQueue.end(), activeBufferQueue); } + Texture::KtxStorage::releaseOpenKtxFiles(); return true; } From b6edb2c9d6afcc07d266b45fa63b3536e35469eb Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 14:53:13 -0700 Subject: [PATCH 32/42] platform states, edit controller mapping, separate mac/win delete --- interface/src/Application.cpp | 27 ++++++++++++++++++- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 ++ scripts/system/edit.js | 17 +++++++++--- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e290531471..12aed213c9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -726,6 +726,9 @@ static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; +static const QString STATE_PLATFORM_WINDOWS = "PlatformWindows"; +static const QString STATE_PLATFORM_MAC = "PlatformMac"; +static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid"; // Statically provided display and input plugins extern DisplayPluginList getDisplayPlugins(); @@ -909,7 +912,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, - STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, + STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_WINDOWS } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1683,6 +1687,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_NAV_FOCUSED, []() -> float { return DependencyManager::get()->navigationFocused() ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_WINDOWS, []() -> float { +#if defined(Q_OS_WIN) + return 1; +#else + return 0; +#endif + }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_MAC, []() -> float { +#if defined(Q_OS_MAC) + return 1; +#else + return 0; +#endif + }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float { +#if defined(Q_OS_ANDROID) + return 1; +#else + return 0; +#endif + }); // Setup the _keyboardMouseDevice, _touchscreenDevice, _touchscreenVirtualPadDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 650c9675a7..ddd51e9b9b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -302,6 +302,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index adee7c6236..776c09addd 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -20,6 +20,8 @@ var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton"; +var CONTROLLER_MAPPING_NAME = "com.highfidelity.editMode"; + Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", @@ -860,6 +862,7 @@ var toolBar = (function () { cameraManager.disable(); selectionDisplay.triggerMapping.disable(); tablet.landscape = false; + Controller.disableMapping(CONTROLLER_MAPPING_NAME); } else { if (shouldUseEditTabletApp()) { tablet.loadQMLSource("hifi/tablet/Edit.qml", true); @@ -876,6 +879,7 @@ var toolBar = (function () { selectionDisplay.triggerMapping.enable(); print("starting tablet in landscape mode"); tablet.landscape = true; + Controller.enableMapping(CONTROLLER_MAPPING_NAME); // Not sure what the following was meant to accomplish, but it currently causes // everybody else to think that Interface has lost focus overall. fogbugzid:558 // Window.setFocus(); @@ -1859,9 +1863,7 @@ var keyReleaseEvent = function (event) { cameraManager.keyReleaseEvent(event); } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text === "DELETE") { - deleteSelectedEntities(); - } else if (event.text === 'd' && event.isControl) { + if (event.text === 'd' && event.isControl) { selectionManager.clearSelections(); } else if (event.text === "t") { selectionDisplay.toggleSpaceMode(); @@ -1893,6 +1895,15 @@ var keyReleaseEvent = function (event) { Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); +function deleteKey(value) { + if (value === 0) { // on release + deleteSelectedEntities(); + } +} +var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).peek().to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).peek().to(deleteKey); + function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { var children = parentData.children; From aae06e8f49d2c0ecf33ccd8eb343992cb5d6379f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 27 Aug 2018 15:40:33 -0700 Subject: [PATCH 33/42] faster aabox ray intersection and pre-computed inverse direction --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Volume3DOverlay.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 4 +- libraries/entities/src/EntityTree.cpp | 5 +- libraries/entities/src/EntityTreeElement.cpp | 2 +- libraries/render-utils/src/Model.cpp | 9 +- libraries/render-utils/src/PickItemsJob.cpp | 3 +- libraries/shared/src/AABox.cpp | 6 +- libraries/shared/src/AABox.h | 2 +- libraries/shared/src/AACube.cpp | 6 +- libraries/shared/src/AACube.h | 6 +- libraries/shared/src/GeometryUtil.cpp | 89 +++++++------------ libraries/shared/src/GeometryUtil.h | 4 +- libraries/shared/src/TriangleSet.cpp | 16 ++-- libraries/shared/src/TriangleSet.h | 4 +- 15 files changed, 69 insertions(+), 91 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 789b1f9969..ba9a1f9fc9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -180,7 +180,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distance; BoxFace face; glm::vec3 normal; - boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + boundingBox.findRayIntersection(cameraPosition, direction, 1.0f / direction, distance, face, normal); float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; if (event.getID() == 1) { // "1" is left hand offsetAngle *= -1.0f; diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index c87650a77b..a307d445c0 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -88,7 +88,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve // we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame // and testing intersection there. - bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal); + bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, 1.0f / overlayFrameDirection, distance, face, surfaceNormal); if (hit) { surfaceNormal = transform.getRotation() * surfaceNormal; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index af8d4fac86..2da5c30dc0 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -596,8 +596,8 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o voxelBox += result3 - Vectors::HALF; voxelBox += result3 + Vectors::HALF; - glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f); - return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), + glm::vec3 directionInVoxel = vec3(wtvMatrix * glm::vec4(direction, 0.0f)); + return voxelBox.findRayIntersection(glm::vec3(originInVoxel), directionInVoxel, 1.0f / directionInVoxel, distance, face, surfaceNormal); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1cf6e45d5b..a7c88ddc7d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -48,6 +48,7 @@ public: // Inputs glm::vec3 origin; glm::vec3 direction; + glm::vec3 invDirection; const QVector& entityIdsToInclude; const QVector& entityIdsToDiscard; bool visibleOnly; @@ -847,7 +848,7 @@ float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex float boundDistance = FLT_MAX; BoxFace face; glm::vec3 surfaceNormal; - if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) { + if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, args->invDirection, boundDistance, face, surfaceNormal)) { // Don't add this cell if it's already farther than our best distance so far if (boundDistance < args->distance) { distance = boundDistance; @@ -863,7 +864,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { - RayArgs args = { origin, direction, entityIdsToInclude, entityIdsToDiscard, + RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; distance = FLT_MAX; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index b7288755a6..efe5dafccf 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -215,7 +215,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori float localDistance; BoxFace localFace; glm::vec3 localSurfaceNormal; - if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, + if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, 1.0f / entityFrameDirection, localDistance, localFace, localSurfaceNormal)) { if (entityFrameBox.contains(entityFrameOrigin) || localDistance < distance) { // now ask the entity if we actually intersect diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ae3ee6e9a2..18ee05ddd4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -376,7 +376,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // we can use the AABox's intersection by mapping our origin and direction into the model frame // and testing intersection there. - if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { + if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, 1.0f / modelFrameDirection, distance, face, surfaceNormal)) { QMutexLocker locker(&_mutex); float bestDistance = FLT_MAX; @@ -400,6 +400,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); + glm::vec3 meshFrameInvDirection = 1.0f / meshFrameDirection; int shapeID = 0; int subMeshIndex = 0; @@ -415,8 +416,8 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float partBoundDistance = FLT_MAX; BoxFace partBoundFace; glm::vec3 partBoundNormal; - if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance, - partBoundFace, partBoundNormal)) { + if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, + partBoundDistance, partBoundFace, partBoundNormal)) { priority = partBoundDistance; } } @@ -444,7 +445,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float triangleSetDistance = FLT_MAX; BoxFace triangleSetFace; Triangle triangleSetTriangle; - if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, + if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { if (triangleSetDistance < bestDistance) { bestDistance = triangleSetDistance; diff --git a/libraries/render-utils/src/PickItemsJob.cpp b/libraries/render-utils/src/PickItemsJob.cpp index 860262a969..f41222d358 100644 --- a/libraries/render-utils/src/PickItemsJob.cpp +++ b/libraries/render-utils/src/PickItemsJob.cpp @@ -33,6 +33,7 @@ void PickItemsJob::run(const render::RenderContextPointer& renderContext, const render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); + const glm::vec3 rayInvDirection = 1.0f / rayDirection; BoxFace face; glm::vec3 normal; float isectDistance; @@ -42,7 +43,7 @@ render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPoint render::ItemKey itemKey; for (const auto& itemBound : inputs) { - if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { + if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, rayInvDirection, isectDistance, face, normal)) { auto& item = renderContext->_scene->getItem(itemBound.id); itemKey = item.getKey(); if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; - surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f); - return true; - } - if ((findInsideOutIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; - surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f); - return true; - } - if ((findInsideOutIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) { - distance = axisDistance; - face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; - surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f); - return true; - } - // This case is unexpected, but mimics the previous behavior for inside out intersections - distance = 0; - return true; +// https://tavianator.com/fast-branchless-raybounding-box-intersections/ +bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float t1, t2, newTmin, newTmax, tmin = -INFINITY, tmax = INFINITY; + int minAxis = -1, maxAxis = -1; + + for (int i = 0; i < 3; ++i) { + t1 = (corner[i] - origin[i]) * invDirection[i]; + t2 = (corner[i] + scale[i] - origin[i]) * invDirection[i]; + + newTmin = glm::min(t1, t2); + newTmax = glm::max(t1, t2); + + minAxis = newTmin > tmin ? i : minAxis; + tmin = glm::max(tmin, newTmin); + maxAxis = newTmax < tmax ? i : maxAxis; + tmax = glm::min(tmax, newTmax); } - // check each axis - float axisDistance; - if ((findIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; - surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f); - return true; - } - if ((findIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; - surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f); - return true; - } - if ((findIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) { - distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; - surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f); + bool inside = tmin < 0.0f; + if (tmax >= glm::max(tmin, 0.0f)) { + if (inside) { + distance = tmax; + bool positiveDirection = direction[maxAxis] > 0.0f; + surfaceNormal = glm::vec3(0.0f); + surfaceNormal[maxAxis] = positiveDirection ? -1.0f : 1.0f; + face = positiveDirection ? BoxFace(2 * maxAxis + 1) : BoxFace(2 * maxAxis); + } else { + distance = tmin; + bool positiveDirection = direction[minAxis] > 0.0f; + surfaceNormal = glm::vec3(0.0f); + surfaceNormal[minAxis] = positiveDirection ? -1.0f : 1.0f; + face = positiveDirection ? BoxFace(2 * minAxis) : BoxFace(2 * minAxis + 1); + } return true; } return false; diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 54f9062469..8ec75f71bd 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -76,8 +76,8 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& bool findIntersection(float origin, float direction, float corner, float size, float& distance); bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance); -bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance, - BoxFace& face, glm::vec3& surfaceNormal); +bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal); bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& center, float radius, float& distance); diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 1dc8c5657a..02a8d458a9 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -176,7 +176,7 @@ void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) { _triangleIndices.push_back(triangleIndex); } -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { balanceTree(); @@ -184,7 +184,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& float localDistance = distance; int trianglesTouched = 0; - bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findRayIntersection(origin, direction, invDirection, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { distance = localDistance; } @@ -232,9 +232,9 @@ bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& return intersectedSomething; } -bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, - bool allowBackface) { +bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, + bool allowBackface) { if (_population < 1) { return false; // no triangles below here, so we can't intersect } @@ -270,7 +270,7 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, float childBoundDistance = FLT_MAX; BoxFace childBoundFace; glm::vec3 childBoundNormal; - if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) { + if (child->getBounds().findRayIntersection(origin, direction, invDirection, childBoundDistance, childBoundFace, childBoundNormal)) { // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) if (childBoundDistance < bestLocalDistance) { priority = childBoundDistance; @@ -301,11 +301,11 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, if (!precision && childDistance < EPSILON) { BoxFace childBoundFace; glm::vec3 childBoundNormal; - sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal); + sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, invDirection, childDistance, childBoundFace, childBoundNormal); } BoxFace childFace; Triangle childTriangle; - if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (sortedTriangleCell.second->findRayIntersection(origin, direction, invDirection, childDistance, childFace, childTriangle, precision, trianglesTouched)) { if (childDistance < bestLocalDistance) { bestLocalDistance = childDistance; bestLocalFace = childFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 84e362f0d0..3417b36b4a 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -28,7 +28,7 @@ class TriangleSet { void reset(const AABox& bounds, int depth = 0); void clear(); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, @@ -69,7 +69,7 @@ public: void insert(const Triangle& t); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); From 52bbb4d2abac51afe38fe5050a168464625d1ce3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Aug 2018 12:47:17 -0700 Subject: [PATCH 34/42] fix ray-pick results on entities with non-default registration point. clean up grab scripts to correctly use this fix. --- interface/src/raypick/PathPointer.cpp | 4 +-- .../controllerModules/farActionGrabEntity.js | 18 +++-------- .../farActionGrabEntityDynOnly.js | 17 +++------- .../controllerModules/farParentGrabEntity.js | 18 +++-------- .../libraries/controllerDispatcherUtils.js | 32 +++++++++++++++++-- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index 685611d77b..d434c667de 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -105,7 +105,7 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition()); glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat; pos = extractTranslation(finalPosAndRotMat); - rot = glmExtractRotation(finalPosAndRotMat); + rot = props.getRotation(); dim = props.getDimensions(); registrationPoint = props.getRegistrationPoint(); } @@ -350,4 +350,4 @@ glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::ve default: return glm::vec2(NAN); } -} \ No newline at end of file +} diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index e4563fda14..5e798ed680 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -14,7 +14,8 @@ PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST + Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -593,18 +594,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js index a080e75325..78abcb9b20 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js @@ -12,7 +12,7 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - Uuid + Uuid, worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -572,18 +572,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js index 439b5e5f51..a9ec246a32 100644 --- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js @@ -11,7 +11,8 @@ Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, - getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent + getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent, + worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -615,18 +616,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index a386dcf5b4..c34fd76802 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, +/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4, Selection, Uuid, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, @@ -58,7 +58,8 @@ highlightTargetEntity:true, clearHighlightedEntities:true, unhighlightTargetEntity:true, - distanceBetweenEntityLocalPositionAndBoundingBox: true + distanceBetweenEntityLocalPositionAndBoundingBox: true, + worldPositionToRegistrationFrameMatrix: true */ MSECS_PER_SEC = 1000.0; @@ -487,6 +488,30 @@ entityIsFarGrabbedByOther = function(entityID) { return false; }; + +worldPositionToRegistrationFrameMatrix = function(wptrProps, pos) { + // get world matrix for intersection point + var intersectionMat = new Xform({ x: 0, y: 0, z:0, w: 1 }, pos); + + // calculate world matrix for registrationPoint addjusted entity + var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; + var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, wptrProps.registrationPoint); + var regOffset = Vec3.multiplyVbyV(regRatio, wptrProps.dimensions); + var regOffsetRot = Vec3.multiplyQbyV(wptrProps.rotation, regOffset); + var modelMat = new Xform(wptrProps.rotation, Vec3.sum(wptrProps.position, regOffsetRot)); + + // get inverse of model matrix + var modelMatInv = modelMat.inv(); + + // transform world intersection point into object's registrationPoint frame + var xformMat = Xform.mul(modelMatInv, intersectionMat); + + // convert to Mat4 + var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); + return offsetMat; +}; + + if (typeof module !== 'undefined') { module.exports = { makeDispatcherModuleParameters: makeDispatcherModuleParameters, @@ -508,6 +533,7 @@ if (typeof module !== 'undefined') { projectOntoEntityXYPlane: projectOntoEntityXYPlane, TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE: TRIGGER_ON_VALUE, - DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST + DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix }; } From f81fe6b29f3998c4dbb1a941b38b7d93d7f2c7c3 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 16:35:36 -0700 Subject: [PATCH 35/42] add more inputs to controller mapping --- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 + scripts/system/edit.js | 64 ++++++++++++------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index ddd51e9b9b..539546dd96 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -304,6 +304,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketLeft), "BracketLeft")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketRight), "BracketRight")); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 776c09addd..e145c1ab3d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1863,28 +1863,7 @@ var keyReleaseEvent = function (event) { cameraManager.keyReleaseEvent(event); } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text === 'd' && event.isControl) { - selectionManager.clearSelections(); - } else if (event.text === "t") { - selectionDisplay.toggleSpaceMode(); - } else if (event.text === "f") { - if (isActive) { - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } - } else if (event.text === '[') { - if (isActive) { - cameraManager.enable(); - } - } else if (event.text === 'g') { - if (isActive && selectionManager.hasSelection()) { - grid.moveToSelection(); - } - } else if (event.key === KEY_P && event.isControl && !event.isAutoRepeat ) { + if (event.key === KEY_P && event.isControl && !event.isAutoRepeat) { if (event.isShifted) { unparentSelectedEntities(); } else { @@ -1900,9 +1879,46 @@ function deleteKey(value) { deleteSelectedEntities(); } } +function deselectKey(value) { + if (value === 0) { // on release + selectionManager.clearSelections(); + } +} +function toggleKey(value) { + if (value === 0) { // on release + selectionDisplay.toggleSpaceMode(); + } +} +function focusKey(value) { + if (value === 0) { // on release + if (selectionManager.hasSelection()) { + cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } + } +} +function cameraKey(value) { + if (value === 0) { // on release + cameraManager.enable(); + } +} +function gridKey(value) { + if (value === 0) { // on release + if (selectionManager.hasSelection()) { + grid.moveToSelection(); + } + } +} var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).peek().to(deleteKey); -mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).peek().to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); +mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); +mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); +mapping.from([Controller.Hardware.Keyboard.BracketLeft]).to(cameraKey); +mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { From c19bd8968ad315df6abdfc336ba58fcafe47e041 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 27 Aug 2018 16:48:47 -0700 Subject: [PATCH 36/42] Fix mixer crash --- libraries/avatars/src/AvatarHashMap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 8dfee3551e..c437b56f32 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -239,7 +239,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer Date: Mon, 27 Aug 2018 18:11:19 -0700 Subject: [PATCH 37/42] use not Mac instead of Windows --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e145c1ab3d..dd53e8c395 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1912,7 +1912,7 @@ function gridKey(value) { } } var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey); mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); From dcf2074d7eb85bb35b60f89dbf97737f92b798cd Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 29 Aug 2018 11:56:37 -0700 Subject: [PATCH 38/42] remove [ camera key, allow F to enter camera without selection --- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 -- scripts/system/edit.js | 13 ++----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 539546dd96..ddd51e9b9b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -304,8 +304,6 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); - availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketLeft), "BracketLeft")); - availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketRight), "BracketRight")); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index dd53e8c395..b086b5624c 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1890,18 +1890,10 @@ function toggleKey(value) { } } function focusKey(value) { - if (value === 0) { // on release - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } -} -function cameraKey(value) { if (value === 0) { // on release cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } function gridKey(value) { @@ -1917,7 +1909,6 @@ mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); -mapping.from([Controller.Hardware.Keyboard.BracketLeft]).to(cameraKey); mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); function recursiveAdd(newParentID, parentData) { From 0e53fa3f8b8a2edb850652e8b461b967a7669fd2 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 29 Aug 2018 12:05:46 -0700 Subject: [PATCH 39/42] only focus with selection --- scripts/system/edit.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index b086b5624c..e340c75a8b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1892,8 +1892,10 @@ function toggleKey(value) { function focusKey(value) { if (value === 0) { // on release cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + if (selectionManager.hasSelection()) { + cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } } } function gridKey(value) { From cda8b82ead7a74b411e4219905f5ca29e8a0f8e3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 29 Aug 2018 13:32:53 -0700 Subject: [PATCH 40/42] improve pointer performance --- interface/src/raypick/PathPointer.cpp | 77 ++++++++++++++----------- interface/src/raypick/PathPointer.h | 4 ++ interface/src/raypick/StylusPointer.cpp | 6 +- interface/src/raypick/StylusPointer.h | 2 + 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index d434c667de..0301136bfa 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -54,11 +54,13 @@ PathPointer::~PathPointer() { void PathPointer::setRenderState(const std::string& state) { withWriteLock([&] { if (!_currentRenderState.empty() && state != _currentRenderState) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + auto renderState = _renderStates.find(_currentRenderState); + if (renderState != _renderStates.end()) { + renderState->second->disable(); } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + auto defaultRenderState = _defaultRenderStates.find(_currentRenderState); + if (defaultRenderState != _defaultRenderStates.end()) { + defaultRenderState->second.second->disable(); } } _currentRenderState = state; @@ -142,52 +144,57 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick void PathPointer::updateVisuals(const PickResultPointer& pickResult) { IntersectionType type = getPickedObjectType(pickResult); - if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && + auto renderState = _renderStates.find(_currentRenderState); + auto defaultRenderState = _defaultRenderStates.find(_currentRenderState); + if (_enabled && !_currentRenderState.empty() && renderState != _renderStates.end() && (type != IntersectionType::NONE || _pathLength > 0.0f)) { glm::vec3 origin = getPickOrigin(pickResult); glm::vec3 end = getPickEnd(pickResult, _pathLength); glm::vec3 surfaceNormal = getPickedObjectNormal(pickResult); - _renderStates[_currentRenderState]->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar, - _followNormal, _followNormalStrength, _pathLength, pickResult); - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + renderState->second->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar, + _followNormal, _followNormalStrength, _pathLength, pickResult); + if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) { + defaultRenderState->second.second->disable(); } - } else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + } else if (_enabled && !_currentRenderState.empty() && defaultRenderState != _defaultRenderStates.end()) { + if (renderState != _renderStates.end() && renderState->second->isEnabled()) { + renderState->second->disable(); } glm::vec3 origin = getPickOrigin(pickResult); - glm::vec3 end = getPickEnd(pickResult, _defaultRenderStates[_currentRenderState].first); - _defaultRenderStates[_currentRenderState].second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, - _faceAvatar, _followNormal, _followNormalStrength, _defaultRenderStates[_currentRenderState].first, pickResult); + glm::vec3 end = getPickEnd(pickResult, defaultRenderState->second.first); + defaultRenderState->second.second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, + _faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first, pickResult); } else if (!_currentRenderState.empty()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + if (renderState != _renderStates.end() && renderState->second->isEnabled()) { + renderState->second->disable(); } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) { + defaultRenderState->second.second->disable(); } } } void PathPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { withWriteLock([&] { - updateRenderStateOverlay(_renderStates[state]->getStartID(), startProps); - updateRenderStateOverlay(_renderStates[state]->getEndID(), endProps); - QVariant startDim = startProps.toMap()["dimensions"]; - if (startDim.isValid()) { - _renderStates[state]->setStartDim(vec3FromVariant(startDim)); - } - QVariant endDim = endProps.toMap()["dimensions"]; - if (endDim.isValid()) { - _renderStates[state]->setEndDim(vec3FromVariant(endDim)); - } - QVariant rotation = endProps.toMap()["rotation"]; - if (rotation.isValid()) { - _renderStates[state]->setEndRot(quatFromVariant(rotation)); - } + auto renderState = _renderStates.find(state); + if (renderState != _renderStates.end()) { + updateRenderStateOverlay(renderState->second->getStartID(), startProps); + updateRenderStateOverlay(renderState->second->getEndID(), endProps); + QVariant startDim = startProps.toMap()["dimensions"]; + if (startDim.isValid()) { + renderState->second->setStartDim(vec3FromVariant(startDim)); + } + QVariant endDim = endProps.toMap()["dimensions"]; + if (endDim.isValid()) { + renderState->second->setEndDim(vec3FromVariant(endDim)); + } + QVariant rotation = endProps.toMap()["rotation"]; + if (rotation.isValid()) { + renderState->second->setEndRot(quatFromVariant(rotation)); + } - editRenderStatePath(state, pathProps); + editRenderStatePath(state, pathProps); + } }); } @@ -271,6 +278,7 @@ void StartEndRenderState::disable() { endProps.insert("ignoreRayIntersection", true); qApp->getOverlays().editOverlay(getEndID(), endProps); } + _enabled = false; } void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY, @@ -337,6 +345,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, endProps.insert("ignoreRayIntersection", doesEndIgnoreRays()); qApp->getOverlays().editOverlay(getEndID(), endProps); } + _enabled = true; } glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) { diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h index 44c1b7f82b..b3638d1f7d 100644 --- a/interface/src/raypick/PathPointer.h +++ b/interface/src/raypick/PathPointer.h @@ -47,6 +47,8 @@ public: virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult); + bool isEnabled() const { return _enabled; } + protected: OverlayID _startID; OverlayID _endID; @@ -59,6 +61,8 @@ protected: glm::quat _avgEndRot; bool _avgEndRotInitialized { false }; + + bool _enabled { true }; }; typedef std::unordered_map> RenderStateMap; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 8c0fb59106..c74b6d8509 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -64,7 +64,9 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { return; } } - hide(); + if (_showing) { + hide(); + } } void StylusPointer::show(const StylusTip& tip) { @@ -80,6 +82,7 @@ void StylusPointer::show(const StylusTip& tip) { props["visible"] = true; qApp->getOverlays().editOverlay(_stylusOverlay, props); } + _showing = true; } void StylusPointer::hide() { @@ -88,6 +91,7 @@ void StylusPointer::hide() { props.insert("visible", false); qApp->getOverlays().editOverlay(_stylusOverlay, props); } + _showing = false; } bool StylusPointer::shouldHover(const PickResultPointer& pickResult) { diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 950b03b7c9..b1903840b3 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -76,6 +76,8 @@ private: static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction); static glm::vec2 findPos2D(const PickedObject& pickedObject, const glm::vec3& origin); + bool _showing { true }; + }; #endif // hifi_StylusPointer_h From aaf5f593c598ab1b0df24b89a2cbf52beac39e86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 29 Aug 2018 14:29:15 -0700 Subject: [PATCH 41/42] send MyAvatar avatar entity updates through entity tree --- interface/src/AvatarBookmarks.cpp | 18 ++--------- interface/src/avatar/MyAvatar.cpp | 52 +++++++++++++++++++++++++------ interface/src/avatar/MyAvatar.h | 5 ++- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 4119b843e1..5c79bedc9a 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -145,20 +145,9 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { emit bookmarkDeleted(bookmarkName); } -bool isWearableEntity(const EntityItemPointer& entity) { - return entity->isVisible() && (entity->getParentJointIndex() != INVALID_JOINT_INDEX || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) - && (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); -} - void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); - + myAvatar->removeWearableAvatarEntities(); addAvatarEntities(avatarEntities); } @@ -183,10 +172,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); + myAvatar->removeWearableAvatarEntities(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 923d071cc0..7bf18f468c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,18 +1703,50 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } -void MyAvatar::removeAvatarEntities(const std::function& condition) { +bool isWearableEntity(const EntityItemPointer& entity) { + return entity->isVisible() + && (entity->getParentJointIndex() != INVALID_JOINT_INDEX + || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) + && (entity->getParentID() == DependencyManager::get()->getSessionUUID() + || entity->getParentID() == AVATAR_SELF_ID); +} + +void MyAvatar::clearAvatarEntities() { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (entityTree) { - entityTree->withWriteLock([&] { - AvatarEntityMap avatarEntities = getAvatarEntityData(); - for (auto entityID : avatarEntities.keys()) { - if (!condition || condition(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } + + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + entityTree->withWriteLock([&entityID, &entityTree] { + // remove this entity first from the entity tree + entityTree->deleteEntity(entityID, true, true); }); + + // remove the avatar entity from our internal list + // (but indicate it doesn't need to be pulled from the tree) + clearAvatarEntity(entityID, false); + } +} + +void MyAvatar::removeWearableAvatarEntities() { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + + if (entityTree) { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + auto entity = entityTree->findEntityByID(entityID); + if (entity && isWearableEntity(entity)) { + entityTree->withWriteLock([&entityID, &entityTree] { + // remove this entity first from the entity tree + entityTree->deleteEntity(entityID, true, true); + }); + + // remove the avatar entity from our internal list + // (but indicate it doesn't need to be pulled from the tree) + clearAvatarEntity(entityID, false); + } + } } } @@ -2116,7 +2148,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) } // clear any existing avatar entities - setAvatarEntityData(AvatarEntityMap()); + clearAvatarEntities(); for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ba6348cc22..bb0df1af62 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,8 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void removeAvatarEntities(const std::function& condition = {}); + void clearAvatarEntities(); + void removeWearableAvatarEntities(); /**jsdoc * @function MyAvatar.isFlying @@ -1782,4 +1783,6 @@ void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMod QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys); +bool isWearableEntity(const EntityItemPointer& entity); + #endif // hifi_MyAvatar_h From b8cd5dd8b97fef9a22fee72dcc5a13df69b7a6e7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 29 Aug 2018 16:58:19 -0700 Subject: [PATCH 42/42] Fixup PR13870: Make the feature work in Master --- interface/src/Application.cpp | 10 +++++----- libraries/networking/src/AccountManager.h | 3 ++- scripts/system/commerce/wallet.js | 6 ++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 44bba6250b..af29ea0bd1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1735,11 +1735,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QTimer* settingsTimer = new QTimer(); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{ connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{ - bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); - if (autoLogout) { - auto accountManager = DependencyManager::get(); - accountManager->logout(); - } // Disconnect the signal from the save settings QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings); // Stop the settings timer @@ -2491,6 +2486,11 @@ void Application::cleanupBeforeQuit() { } DependencyManager::destroy(); + bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); + if (autoLogout) { + DependencyManager::get()->removeAccountFromFile(); + } + _displayPlugin.reset(); PluginManager::getInstance()->shutdown(); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index a79b69fe2b..b122115dd0 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -96,6 +96,8 @@ public: QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL(); } + void removeAccountFromFile(); + public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); @@ -133,7 +135,6 @@ private: void operator=(AccountManager const& other) = delete; void persistAccountToFile(); - void removeAccountFromFile(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 8730273e7c..5939b36438 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -407,8 +407,10 @@ } function onUsernameChanged() { - Settings.setValue("wallet/autoLogout", false); - Settings.setValue("wallet/savedUsername", ""); + if (Account.username !== Settings.getValue("wallet/savedUsername")) { + Settings.setValue("wallet/autoLogout", false); + Settings.setValue("wallet/savedUsername", ""); + } } // Function Name: fromQml()