mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-17 14:58:25 +02:00
Merge remote-tracking branch 'hifi/master' into android_loading_stuck
This commit is contained in:
commit
9bb4bada8a
71 changed files with 1151 additions and 771 deletions
|
@ -4,6 +4,7 @@ import android.app.Fragment;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Editable;
|
||||
|
@ -32,6 +33,7 @@ public class HomeFragment extends Fragment {
|
|||
|
||||
|
||||
private OnHomeInteractionListener mListener;
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
public native String nativeGetLastLocation();
|
||||
|
||||
|
@ -57,6 +59,7 @@ public class HomeFragment extends Fragment {
|
|||
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
|
||||
|
||||
searchNoResultsView = rootView.findViewById(R.id.searchNoResultsView);
|
||||
mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||
|
||||
mDomainsView = rootView.findViewById(R.id.rvDomains);
|
||||
int numberOfColumns = 1;
|
||||
|
@ -76,12 +79,14 @@ public class HomeFragment extends Fragment {
|
|||
searchNoResultsView.setText(R.string.search_no_results);
|
||||
searchNoResultsView.setVisibility(View.VISIBLE);
|
||||
mDomainsView.setVisibility(View.GONE);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonEmptyAdapter() {
|
||||
searchNoResultsView.setVisibility(View.GONE);
|
||||
mDomainsView.setVisibility(View.VISIBLE);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,7 +109,7 @@ public class HomeFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mDomainAdapter.loadDomains(editable.toString());
|
||||
mDomainAdapter.loadDomains(editable.toString(), false);
|
||||
if (editable.length() > 0) {
|
||||
mSearchIconView.setVisibility(View.GONE);
|
||||
mClearSearch.setVisibility(View.VISIBLE);
|
||||
|
@ -130,6 +135,13 @@ public class HomeFragment extends Fragment {
|
|||
|
||||
mClearSearch.setOnClickListener(view -> onSearchClear(view));
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
mDomainAdapter.loadDomains(mSearchView.getText().toString(), true);
|
||||
}
|
||||
});
|
||||
|
||||
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
|
||||
return rootView;
|
||||
|
|
|
@ -10,7 +10,7 @@ import io.highfidelity.hifiinterface.view.DomainAdapter;
|
|||
|
||||
public interface DomainProvider {
|
||||
|
||||
void retrieve(String filterText, DomainCallback domainCallback);
|
||||
void retrieve(String filterText, DomainCallback domainCallback, boolean forceRefresh);
|
||||
|
||||
interface DomainCallback {
|
||||
void retrieveOk(List<DomainAdapter.Domain> domain);
|
||||
|
|
|
@ -49,8 +49,8 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized void retrieve(String filterText, DomainCallback domainCallback) {
|
||||
if (!startedToGetFromAPI) {
|
||||
public synchronized void retrieve(String filterText, DomainCallback domainCallback, boolean forceRefresh) {
|
||||
if (!startedToGetFromAPI || forceRefresh) {
|
||||
startedToGetFromAPI = true;
|
||||
fillDestinations(filterText, domainCallback);
|
||||
} else {
|
||||
|
@ -72,6 +72,7 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
allStories.clear();
|
||||
getUserStoryPage(1, allStories, null,
|
||||
ex -> {
|
||||
suggestions.clear();
|
||||
allStories.forEach(userStory -> {
|
||||
if (taggedStoriesIds.contains(userStory.id)) {
|
||||
userStory.tagFound = true;
|
||||
|
|
|
@ -42,14 +42,14 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
|||
mProtocol = protocol;
|
||||
mLastLocation = lastLocation;
|
||||
domainProvider = new UserStoryDomainProvider(mProtocol);
|
||||
loadDomains("");
|
||||
loadDomains("", true);
|
||||
}
|
||||
|
||||
public void setListener(AdapterListener adapterListener) {
|
||||
mAdapterListener = adapterListener;
|
||||
}
|
||||
|
||||
public void loadDomains(String filterText) {
|
||||
public void loadDomains(String filterText, boolean forceRefresh) {
|
||||
domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<Domain> domain) {
|
||||
|
@ -76,7 +76,7 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
|||
Log.e("DOMAINS", message, e);
|
||||
if (mAdapterListener != null) mAdapterListener.onError(e, message);
|
||||
}
|
||||
});
|
||||
}, forceRefresh);
|
||||
}
|
||||
|
||||
private void overrideDefaultThumbnails(List<Domain> domain) {
|
||||
|
|
|
@ -63,13 +63,18 @@
|
|||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvDomains"
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
app:layout_constraintTop_toBottomOf="@id/searchView"
|
||||
app:layout_constraintBottom_toBottomOf="@id/contentHomeRoot"
|
||||
app:layout_constraintStart_toStartOf="@id/contentHomeRoot"
|
||||
app:layout_constraintEnd_toEndOf="@id/contentHomeRoot"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
android:layout_height="0dp">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvDomains"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QSaveFile>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QtCore/QVector>
|
||||
|
@ -1042,7 +1043,7 @@ bool AssetServer::loadMappingsFromFile() {
|
|||
bool AssetServer::writeMappingsToFile() {
|
||||
auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME);
|
||||
|
||||
QFile mapFile { mapFilePath };
|
||||
QSaveFile mapFile { mapFilePath };
|
||||
if (mapFile.open(QIODevice::WriteOnly)) {
|
||||
QJsonObject root;
|
||||
|
||||
|
@ -1053,8 +1054,12 @@ bool AssetServer::writeMappingsToFile() {
|
|||
QJsonDocument jsonDocument { root };
|
||||
|
||||
if (mapFile.write(jsonDocument.toJson()) != -1) {
|
||||
qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath;
|
||||
return true;
|
||||
if (mapFile.commit()) {
|
||||
qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath;
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(asset_server) << "Failed to commit JSON mappings to file at" << mapFilePath;
|
||||
}
|
||||
} else {
|
||||
qCWarning(asset_server) << "Failed to write JSON mappings to file at" << mapFilePath;
|
||||
}
|
||||
|
|
2
cmake/externals/GifCreator/CMakeLists.txt
vendored
2
cmake/externals/GifCreator/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME GifCreator)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/GifCreator.zip
|
||||
URL https://public.highfidelity.com/dependencies/GifCreator.zip
|
||||
URL_MD5 8ac8ef5196f47c658dce784df5ecdb70
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
4
cmake/externals/LibOVR/CMakeLists.txt
vendored
4
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -17,7 +17,7 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.26.0_public.zip
|
||||
URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip
|
||||
URL_MD5 06804ff9727b910dcd04a37c800053b5
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
|
||||
|
@ -38,7 +38,7 @@ elseif(APPLE)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://static.oculus.com/sdk-downloads/ovr_sdk_macos_0.5.0.1.tar.gz
|
||||
URL https://public.highfidelity.com/dependencies/ovr_sdk_macos_0.5.0.1.tar.gz
|
||||
URL_MD5 0a0785a04fb285f64f62267388344ad6
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
|
@ -9,7 +9,7 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/OVRPlatformSDK_v1.10.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/OVRPlatformSDK_v1.10.0.zip
|
||||
URL_MD5 e6c8264af16d904e6506acd5172fa0a9
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
2
cmake/externals/boostconfig/CMakeLists.txt
vendored
2
cmake/externals/boostconfig/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(ExternalProject)
|
|||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
#URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/config-boost-1.58.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/config-boost-1.58.0.zip
|
||||
URL_MD5 42fa673bae2b7645a22736445e80eb8d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
4
cmake/externals/bullet/CMakeLists.txt
vendored
4
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -17,7 +17,7 @@ include(ExternalProject)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
|
||||
URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz
|
||||
URL_MD5 0a6876607ebe83e227427215f15946fd
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
LOG_DOWNLOAD 1
|
||||
|
@ -28,7 +28,7 @@ if (WIN32)
|
|||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
|
||||
URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz
|
||||
URL_MD5 0a6876607ebe83e227427215f15946fd
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
4
cmake/externals/crashpad/CMakeLists.txt
vendored
4
cmake/externals/crashpad/CMakeLists.txt
vendored
|
@ -6,7 +6,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://public.highfidelity.com/dependencies/crashpad_062317.1.zip
|
||||
URL https://public.highfidelity.com/dependencies/crashpad_062317.1.zip
|
||||
URL_MD5 9c84b77f5f23daf939da1371825ed2b1
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -25,7 +25,7 @@ if (WIN32)
|
|||
elseif (APPLE)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://public.highfidelity.com/dependencies/crashpad_mac_070318.zip
|
||||
URL https://public.highfidelity.com/dependencies/crashpad_mac_070318.zip
|
||||
URL_MD5 ba1501dc163591ac2d1be74946967e2a
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
2
cmake/externals/draco/CMakeLists.txt
vendored
2
cmake/externals/draco/CMakeLists.txt
vendored
|
@ -11,7 +11,7 @@ endif ()
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.1.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/draco-1.1.0.zip
|
||||
URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>-$<CONFIG> ${EXTRA_CMAKE_FLAGS}
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/etc2comp/CMakeLists.txt
vendored
2
cmake/externals/etc2comp/CMakeLists.txt
vendored
|
@ -15,7 +15,7 @@ include(ExternalProject)
|
|||
# that would override CMAKE_CXX_FLAGS
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip
|
||||
URL https://public.highfidelity.com/dependencies/etc2comp-patched.zip
|
||||
URL_MD5 4c96153eb179acbe619e3d99d3330595
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS}
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
|
|
2
cmake/externals/glad32es/CMakeLists.txt
vendored
2
cmake/externals/glad32es/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(SelectLibraryConfigurations)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad32es.zip
|
||||
URL https://public.highfidelity.com/dependencies/glad/glad32es.zip
|
||||
URL_MD5 6a641d8c49dee4c895fa59315f5682a6
|
||||
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/glad41/CMakeLists.txt
vendored
2
cmake/externals/glad41/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(SelectLibraryConfigurations)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad41.zip
|
||||
URL https://public.highfidelity.com/dependencies/glad/glad41.zip
|
||||
URL_MD5 1324eeec33abe91e67d19ae551ba624d
|
||||
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/glad45/CMakeLists.txt
vendored
2
cmake/externals/glad45/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(SelectLibraryConfigurations)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad45.zip
|
||||
URL https://public.highfidelity.com/dependencies/glad/glad45.zip
|
||||
URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05
|
||||
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/gli/CMakeLists.txt
vendored
2
cmake/externals/gli/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME gli)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/gli-0.8.1.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/gli-0.8.1.0.zip
|
||||
URL_MD5 00c990f59c12bbf367956ef399d6f798
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CONFIGURE_COMMAND ""
|
||||
|
|
2
cmake/externals/glm/CMakeLists.txt
vendored
2
cmake/externals/glm/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME glm)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip
|
||||
URL https://public.highfidelity.com/dependencies/glm-0.9.8.5-patched.zip
|
||||
URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
|
|
|
@ -6,16 +6,16 @@ set(EXTERNAL_NAME hifiAudioCodec)
|
|||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-win-2.0.zip)
|
||||
set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67)
|
||||
elseif (APPLE)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-mac-2.0.zip)
|
||||
set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba)
|
||||
elseif (ANDROID)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-android-2.0.zip)
|
||||
set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683)
|
||||
elseif (UNIX)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-linux-2.0.zip)
|
||||
set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481)
|
||||
else()
|
||||
return()
|
||||
|
|
2
cmake/externals/neuron/CMakeLists.txt
vendored
2
cmake/externals/neuron/CMakeLists.txt
vendored
|
@ -4,7 +4,7 @@ set(EXTERNAL_NAME neuron)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.2.zip")
|
||||
set(NEURON_URL "https://public.highfidelity.com/dependencies/neuron_datareader_b.12.2.zip")
|
||||
set(NEURON_URL_MD5 "84273ad2200bf86a9279d1f412a822ca")
|
||||
|
||||
ExternalProject_Add(${EXTERNAL_NAME}
|
||||
|
|
4
cmake/externals/nvtt/CMakeLists.txt
vendored
4
cmake/externals/nvtt/CMakeLists.txt
vendored
|
@ -8,7 +8,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.hifi.zip
|
||||
URL https://public.highfidelity.com/dependencies/nvtt-win-2.1.0.hifi.zip
|
||||
URL_MD5 10da01cf601f88f6dc12a6bc13c89136
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -29,7 +29,7 @@ else ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip
|
||||
URL https://public.highfidelity.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip
|
||||
URL_MD5 602776e08515b54bfa1b8dc455003f0f
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/openvr/CMakeLists.txt
vendored
2
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v1.0.6.zip
|
||||
URL https://public.highfidelity.com/dependencies/openvr-1.0.6.zip
|
||||
URL_MD5 f6892cd3a3078f505d03b4297f5a1951
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
2
cmake/externals/polyvox/CMakeLists.txt
vendored
2
cmake/externals/polyvox/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME polyvox)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-7-15.zip
|
||||
URL https://public.highfidelity.com/dependencies/polyvox-master-2015-7-15.zip
|
||||
URL_MD5 9ec6323b87e849ae36e562ae1c7494a9
|
||||
CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
|
|
2
cmake/externals/quazip/CMakeLists.txt
vendored
2
cmake/externals/quazip/CMakeLists.txt
vendored
|
@ -13,7 +13,7 @@ endif ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip
|
||||
URL https://public.highfidelity.com/dependencies/quazip-0.7.3.zip
|
||||
URL_MD5 ed03754d39b9da1775771819b8001d45
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS ${QUAZIP_CMAKE_ARGS}
|
||||
|
|
7
cmake/externals/sdl2/CMakeLists.txt
vendored
7
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-devel-2.0.3-VC.zip
|
||||
URL https://public.highfidelity.com/dependencies/SDL2-devel-2.0.3-VC.zip
|
||||
URL_MD5 30a333bcbe94bc5016e8799c73e86233
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -18,7 +18,8 @@ elseif (APPLE)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip
|
||||
URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.zip
|
||||
URL_MD5 55f1eae5142d20db11c844d8d4d6deed
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DVIDEO_OPENGL=OFF
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
|
@ -49,7 +50,7 @@ else ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://www.libsdl.org/release/SDL2-2.0.3.tar.gz
|
||||
URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.tar.gz
|
||||
URL_MD5 fe6c61d2e9df9ef570e7e80c6e822537
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
6
cmake/externals/sixense/CMakeLists.txt
vendored
6
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -4,15 +4,15 @@ set(EXTERNAL_NAME sixense)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip")
|
||||
#set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_062612.zip")
|
||||
#set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc")
|
||||
#set(SIXENSE_NEW_LAYOUT 0)
|
||||
|
||||
set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip")
|
||||
set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_071615.zip")
|
||||
set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d")
|
||||
set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip")
|
||||
#set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_102215.zip")
|
||||
#set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1")
|
||||
#set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
|
|
2
cmake/externals/steamworks/CMakeLists.txt
vendored
2
cmake/externals/steamworks/CMakeLists.txt
vendored
|
@ -4,7 +4,7 @@ set(EXTERNAL_NAME steamworks)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
set(STEAMWORKS_URL "https://s3.amazonaws.com/hifi-public/dependencies/steamworks_sdk_137.zip")
|
||||
set(STEAMWORKS_URL "https://public.highfidelity.com/dependencies/steamworks_sdk_137.zip")
|
||||
set(STEAMWORKS_URL_MD5 "95ba9d0e3ddc04f8a8be17d2da806cbb")
|
||||
|
||||
ExternalProject_Add(
|
||||
|
|
6
cmake/externals/tbb/CMakeLists.txt
vendored
6
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -3,13 +3,13 @@ set(EXTERNAL_NAME tbb)
|
|||
include(ExternalProject)
|
||||
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_win_slim.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_win_slim.zip)
|
||||
set(DOWNLOAD_MD5 065934458e3db88397f3d10e7eea536c)
|
||||
elseif (APPLE)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105)
|
||||
else ()
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c)
|
||||
endif ()
|
||||
|
||||
|
|
2
cmake/externals/vhacd/CMakeLists.txt
vendored
2
cmake/externals/vhacd/CMakeLists.txt
vendored
|
@ -7,7 +7,7 @@ endif ()
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/v-hacd-master.zip
|
||||
URL https://public.highfidelity.com/dependencies/v-hacd-master.zip
|
||||
URL_MD5 3bfc94f8dd3dfbfe8f4dc088f4820b3e
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
|
|
2
cmake/externals/wasapi/CMakeLists.txt
vendored
2
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -6,7 +6,7 @@ if (WIN32)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip
|
||||
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi10.zip
|
||||
URL_MD5 4f40e49715a420fb67b45b9cee19052c
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
3
cmake/externals/zlib/CMakeLists.txt
vendored
3
cmake/externals/zlib/CMakeLists.txt
vendored
|
@ -5,7 +5,8 @@ include(ExternalProject)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/zlib128.zip
|
||||
URL https://public.highfidelity.com/dependencies/zlib128.zip
|
||||
URL_MD5 126f8676442ffbd97884eb4d6f32afb4
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
|
@ -1916,14 +1916,16 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
// don't handle if we don't have a matching node
|
||||
if (!matchingNode) {
|
||||
return false;
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||
|
||||
// don't handle if we don't have node data for this node
|
||||
if (!nodeData) {
|
||||
return false;
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID());
|
||||
|
@ -1944,7 +1946,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
// request not handled
|
||||
return false;
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if this is a request for our domain ID
|
||||
|
|
|
@ -26,6 +26,7 @@ ScrollingWindow {
|
|||
y: 100
|
||||
|
||||
Component.onCompleted: {
|
||||
focus = true
|
||||
shown = true
|
||||
addressBar.text = webview.url
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ FocusScope {
|
|||
hoverEnabled: true
|
||||
visible: true
|
||||
height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control.
|
||||
textRole: "text"
|
||||
|
||||
function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; }
|
||||
function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; }
|
||||
|
|
|
@ -53,7 +53,7 @@ Column {
|
|||
'protocol=' + encodeURIComponent(Window.protocolSignature())
|
||||
];
|
||||
endpoint: '/api/v1/user_stories?' + options.join('&');
|
||||
itemsPerPage: 3;
|
||||
itemsPerPage: 4;
|
||||
processPage: function (data) {
|
||||
return data.user_stories.map(makeModelData);
|
||||
};
|
||||
|
@ -106,7 +106,6 @@ Column {
|
|||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
currentIndex: -1;
|
||||
onAtXEndChanged: { if (scroll.atXEnd && !scroll.atXBeginning) { suggestions.getNextPage(); } }
|
||||
|
||||
spacing: 12;
|
||||
width: parent.width;
|
||||
|
|
|
@ -61,7 +61,7 @@ Rectangle {
|
|||
'username';
|
||||
}
|
||||
sortAscending: connectionsTable.sortIndicatorOrder === Qt.AscendingOrder;
|
||||
itemsPerPage: 9;
|
||||
itemsPerPage: 10;
|
||||
listView: connectionsTable;
|
||||
processPage: function (data) {
|
||||
return data.users.map(function (user) {
|
||||
|
@ -786,14 +786,6 @@ Rectangle {
|
|||
}
|
||||
|
||||
model: connectionsUserModel;
|
||||
Connections {
|
||||
target: connectionsTable.flickableItem;
|
||||
onAtYEndChanged: {
|
||||
if (connectionsTable.flickableItem.atYEnd && !connectionsTable.flickableItem.atYBeginning) {
|
||||
connectionsUserModel.getNextPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This Rectangle refers to each Row in the connectionsTable.
|
||||
rowDelegate: Rectangle {
|
||||
|
@ -1108,7 +1100,7 @@ Rectangle {
|
|||
function findNearbySessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
||||
var data = optionalData || nearbyUserModelData, length = data.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (data[i].sessionId === sessionId) {
|
||||
if (data[i].sessionId === sessionId.toString()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -1120,7 +1112,7 @@ Rectangle {
|
|||
var data = message.params;
|
||||
var index = -1;
|
||||
iAmAdmin = Users.canKick;
|
||||
index = findNearbySessionIndex('', data);
|
||||
index = findNearbySessionIndex("", data);
|
||||
if (index !== -1) {
|
||||
myData = data[index];
|
||||
data.splice(index, 1);
|
||||
|
@ -1197,8 +1189,8 @@ Rectangle {
|
|||
for (var userId in message.params) {
|
||||
var audioLevel = message.params[userId][0];
|
||||
var avgAudioLevel = message.params[userId][1];
|
||||
// If the userId is 0, we're updating "myData".
|
||||
if (userId == 0) {
|
||||
// If the userId is "", we're updating "myData".
|
||||
if (userId === "") {
|
||||
myData.audioLevel = audioLevel;
|
||||
myCard.audioLevel = audioLevel; // Defensive programming
|
||||
myData.avgAudioLevel = avgAudioLevel;
|
||||
|
|
|
@ -398,7 +398,7 @@ Item {
|
|||
http: root.http;
|
||||
listModelName: root.listModelName;
|
||||
endpoint: "/api/v1/users?filter=connections";
|
||||
itemsPerPage: 8;
|
||||
itemsPerPage: 9;
|
||||
listView: connectionsList;
|
||||
processPage: function (data) {
|
||||
return data.users;
|
||||
|
@ -520,7 +520,6 @@ Item {
|
|||
visible: !connectionsLoading.visible;
|
||||
clip: true;
|
||||
model: connectionsModel;
|
||||
onAtYEndChanged: if (connectionsList.atYEnd && !connectionsList.atYBeginning) { connectionsModel.getNextPage(); }
|
||||
snapMode: ListView.SnapToItem;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
|
|
@ -328,7 +328,15 @@ Item {
|
|||
item.buttonColor = "#E2334D";
|
||||
item.buttonClicked = function() {
|
||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||
sendToPurchases({method: 'updateItemClicked', itemId: root.itemId, itemEdition: root.itemEdition, upgradeUrl: root.upgradeUrl});
|
||||
sendToPurchases({
|
||||
method: 'updateItemClicked',
|
||||
itemId: root.itemId,
|
||||
itemEdition: root.itemEdition,
|
||||
upgradeUrl: root.upgradeUrl,
|
||||
itemHref: root.itemHref,
|
||||
itemType: root.itemType,
|
||||
isInstalled: root.isInstalled
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -723,7 +731,7 @@ Item {
|
|||
}
|
||||
HiFiGlyphs {
|
||||
id: rezIcon;
|
||||
text: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)];
|
||||
text: root.isInstalled ? "" : (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)];
|
||||
anchors.right: rezIconLabel.left;
|
||||
anchors.rightMargin: 2;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
|
|
@ -551,8 +551,9 @@ Rectangle {
|
|||
|
||||
HifiModels.PSFListModel {
|
||||
id: purchasesModel;
|
||||
itemsPerPage: 6;
|
||||
itemsPerPage: 7;
|
||||
listModelName: 'purchases';
|
||||
listView: purchasesContentsList;
|
||||
getPage: function () {
|
||||
console.debug('getPage', purchasesModel.listModelName, root.isShowingMyItems, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage);
|
||||
Commerce.inventory(
|
||||
|
@ -706,7 +707,23 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
} else if (msg.method === "updateItemClicked") {
|
||||
sendToScript(msg);
|
||||
if (msg.itemType === "app" && msg.isInstalled) {
|
||||
lightboxPopup.titleText = "Uninstall App";
|
||||
lightboxPopup.bodyText = "The app that you are trying to update is installed.<br><br>" +
|
||||
"If you proceed, the current version of the app will be uninstalled.";
|
||||
lightboxPopup.button1text = "CANCEL";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.button2text = "CONFIRM";
|
||||
lightboxPopup.button2method = function() {
|
||||
Commerce.uninstallApp(msg.itemHref);
|
||||
sendToScript(msg);
|
||||
};
|
||||
lightboxPopup.visible = true;
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
} else if (msg.method === "giftAsset") {
|
||||
sendAsset.assetName = msg.itemName;
|
||||
sendAsset.assetCertID = msg.certId;
|
||||
|
@ -765,14 +782,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onAtYEndChanged: {
|
||||
if (purchasesContentsList.atYEnd && !purchasesContentsList.atYBeginning) {
|
||||
console.log("User scrolled to the bottom of 'Purchases'.");
|
||||
purchasesModel.getNextPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -212,6 +212,7 @@ Item {
|
|||
HifiModels.PSFListModel {
|
||||
id: transactionHistoryModel;
|
||||
listModelName: "transaction history"; // For debugging. Alternatively, we could specify endpoint for that purpose, even though it's not used directly.
|
||||
listView: transactionHistory;
|
||||
itemsPerPage: 6;
|
||||
getPage: function () {
|
||||
console.debug('getPage', transactionHistoryModel.listModelName, transactionHistoryModel.currentPageToRetrieve);
|
||||
|
@ -346,12 +347,6 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
onAtYEndChanged: {
|
||||
if (transactionHistory.atYEnd && !transactionHistory.atYBeginning) {
|
||||
console.log("User scrolled to the bottom of 'Recent Activity'.");
|
||||
transactionHistoryModel.getNextPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
|
|
@ -33,7 +33,6 @@ ListModel {
|
|||
|
||||
// QML fires the following changed handlers even when first instantiating the Item. So we need a guard against firing them too early.
|
||||
property bool initialized: false;
|
||||
Component.onCompleted: initialized = true;
|
||||
onEndpointChanged: if (initialized) { getFirstPage('delayClear'); }
|
||||
onSortKeyChanged: if (initialized) { getFirstPage('delayClear'); }
|
||||
onSearchFilterChanged: if (initialized) { getFirstPage('delayClear'); }
|
||||
|
@ -60,7 +59,37 @@ ListModel {
|
|||
// Override to return one property of data, and/or to transform the elements. Must return an array of model elements.
|
||||
property var processPage: function (data) { return data; }
|
||||
|
||||
property var listView; // Optional. For debugging.
|
||||
property var listView; // Optional. For debugging, or for having the scroll handler automatically call getNextPage.
|
||||
property var flickable: listView && (listView.flickableItem || listView);
|
||||
// 2: get two pages before you need it (i.e. one full page before you reach the end).
|
||||
// 1: equivalent to paging when reaching end (and not before).
|
||||
// 0: don't getNextPage on scroll at all here. The application code will do it.
|
||||
property real pageAhead: 2.0;
|
||||
function needsEarlyYFetch() {
|
||||
return flickable
|
||||
&& !flickable.atYBeginning
|
||||
&& (flickable.contentY - flickable.originY) >= (flickable.contentHeight - (pageAhead * flickable.height));
|
||||
}
|
||||
function needsEarlyXFetch() {
|
||||
return flickable
|
||||
&& !flickable.atXBeginning
|
||||
&& (flickable.contentX - flickable.originX) >= (flickable.contentWidth - (pageAhead * flickable.width));
|
||||
}
|
||||
function getNextPageIfHorizontalScroll() {
|
||||
if (needsEarlyXFetch()) { getNextPage(); }
|
||||
}
|
||||
function getNextPageIfVerticalScroll() {
|
||||
if (needsEarlyYFetch()) { getNextPage(); }
|
||||
}
|
||||
Component.onCompleted: {
|
||||
initialized = true;
|
||||
if (flickable && pageAhead > 0.0) {
|
||||
// Pun: Scrollers are usually one direction or another, such that only one of the following will actually fire.
|
||||
flickable.contentXChanged.connect(getNextPageIfHorizontalScroll);
|
||||
flickable.contentYChanged.connect(getNextPageIfVerticalScroll);
|
||||
}
|
||||
}
|
||||
|
||||
property int totalPages: 0;
|
||||
property int totalEntries: 0;
|
||||
// Check consistency and call processPage.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include "AvatarLogging.h"
|
||||
|
||||
|
@ -668,3 +669,49 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
|
|||
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap AvatarManager::getPalData(const QList<QString> specificAvatarIdentifiers) {
|
||||
QJsonArray palData;
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.begin();
|
||||
while (itr != avatarMap.end()) {
|
||||
std::shared_ptr<Avatar> avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
QString currentSessionUUID = avatar->getSessionUUID().toString();
|
||||
if (specificAvatarIdentifiers.isEmpty() || specificAvatarIdentifiers.contains(currentSessionUUID)) {
|
||||
QJsonObject thisAvatarPalData;
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
if (currentSessionUUID == myAvatar->getSessionUUID().toString()) {
|
||||
currentSessionUUID = "";
|
||||
}
|
||||
|
||||
thisAvatarPalData.insert("sessionUUID", currentSessionUUID);
|
||||
thisAvatarPalData.insert("sessionDisplayName", avatar->getSessionDisplayName());
|
||||
thisAvatarPalData.insert("audioLoudness", avatar->getAudioLoudness());
|
||||
thisAvatarPalData.insert("isReplicated", avatar->getIsReplicated());
|
||||
|
||||
glm::vec3 position = avatar->getWorldPosition();
|
||||
QJsonObject jsonPosition;
|
||||
jsonPosition.insert("x", position.x);
|
||||
jsonPosition.insert("y", position.y);
|
||||
jsonPosition.insert("z", position.z);
|
||||
thisAvatarPalData.insert("position", jsonPosition);
|
||||
|
||||
float palOrbOffset = 0.2f;
|
||||
int headIndex = avatar->getJointIndex("Head");
|
||||
if (headIndex > 0) {
|
||||
glm::vec3 jointTranslation = avatar->getAbsoluteJointTranslationInObjectFrame(headIndex);
|
||||
palOrbOffset = jointTranslation.y / 2;
|
||||
}
|
||||
thisAvatarPalData.insert("palOrbOffset", palOrbOffset);
|
||||
|
||||
palData.append(thisAvatarPalData);
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
QJsonObject doc;
|
||||
doc.insert("data", palData);
|
||||
return doc.toVariantMap();
|
||||
}
|
||||
|
|
|
@ -157,6 +157,17 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
|
||||
|
||||
/**jsdoc
|
||||
* Used in the PAL for getting PAL-related data about avatars nearby. Using this method is faster
|
||||
* than iterating over each avatar and obtaining data about them in JavaScript, as that method
|
||||
* locks and unlocks each avatar's data structure potentially hundreds of times per update tick.
|
||||
* @function AvatarManager.getPalData
|
||||
* @param {string[]} specificAvatarIdentifiers - A list of specific Avatar Identifiers about
|
||||
* which you want to get PAL data
|
||||
* @returns {object}
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPalData(const QList<QString> specificAvatarIdentifiers = QList<QString>());
|
||||
|
||||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||
int getIdentityRequestsSent() const { return _identityRequestsSent; }
|
||||
|
||||
|
|
|
@ -262,6 +262,26 @@ void MyAvatar::setDominantHand(const QString& hand) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::requestDisableHandTouch() {
|
||||
std::lock_guard<std::mutex> guard(_disableHandTouchMutex);
|
||||
_disableHandTouchCount++;
|
||||
emit shouldDisableHandTouchChanged(_disableHandTouchCount > 0);
|
||||
}
|
||||
|
||||
void MyAvatar::requestEnableHandTouch() {
|
||||
std::lock_guard<std::mutex> guard(_disableHandTouchMutex);
|
||||
_disableHandTouchCount = std::max(_disableHandTouchCount - 1, 0);
|
||||
emit shouldDisableHandTouchChanged(_disableHandTouchCount > 0);
|
||||
}
|
||||
|
||||
void MyAvatar::disableHandTouchForID(const QUuid& entityID) {
|
||||
emit disableHandTouchForIDChanged(entityID, true);
|
||||
}
|
||||
|
||||
void MyAvatar::enableHandTouchForID(const QUuid& entityID) {
|
||||
emit disableHandTouchForIDChanged(entityID, false);
|
||||
}
|
||||
|
||||
void MyAvatar::registerMetaTypes(ScriptEnginePointer engine) {
|
||||
QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
engine->globalObject().setProperty("MyAvatar", value);
|
||||
|
@ -3551,6 +3571,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Rotation);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
||||
}
|
||||
if (myAvatar.getCenterOfGravityModelEnabled()) {
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
||||
|
@ -3568,6 +3589,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
} else {
|
||||
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||
activate(Rotation);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
||||
setForceActivateRotation(false);
|
||||
}
|
||||
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
||||
|
|
|
@ -505,6 +505,28 @@ public:
|
|||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; }
|
||||
/**jsdoc
|
||||
* Request to enable hand touch effect globally
|
||||
* @function MyAvatar.requestEnableHandTouch
|
||||
*/
|
||||
Q_INVOKABLE void requestEnableHandTouch();
|
||||
/**jsdoc
|
||||
* Request to disable hand touch effect globally
|
||||
* @function MyAvatar.requestDisableHandTouch
|
||||
*/
|
||||
Q_INVOKABLE void requestDisableHandTouch();
|
||||
/**jsdoc
|
||||
* Disables hand touch effect on a specific entity
|
||||
* @function MyAvatar.disableHandTouchForID
|
||||
* @param {Uuid} entityID - ID of the entity that will disable hand touch effect
|
||||
*/
|
||||
Q_INVOKABLE void disableHandTouchForID(const QUuid& entityID);
|
||||
/**jsdoc
|
||||
* Enables hand touch effect on a specific entity
|
||||
* @function MyAvatar.enableHandTouchForID
|
||||
* @param {Uuid} entityID - ID of the entity that will enable hand touch effect
|
||||
*/
|
||||
Q_INVOKABLE void enableHandTouchForID(const QUuid& entityID);
|
||||
|
||||
bool useAdvancedMovementControls() const { return _useAdvancedMovementControls.get(); }
|
||||
void setUseAdvancedMovementControls(bool useAdvancedMovementControls)
|
||||
|
@ -883,6 +905,7 @@ public:
|
|||
virtual void rebuildCollisionShape() override;
|
||||
|
||||
const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; }
|
||||
void setHeadControllerFacingMovingAverage(glm::vec2 currentHeadControllerFacing) { _headControllerFacingMovingAverage = currentHeadControllerFacing; }
|
||||
float getCurrentStandingHeight() const { return _currentStandingHeight; }
|
||||
void setCurrentStandingHeight(float newMode) { _currentStandingHeight = newMode; }
|
||||
const glm::quat getAverageHeadRotation() const { return _averageHeadRotation; }
|
||||
|
@ -1391,6 +1414,23 @@ signals:
|
|||
*/
|
||||
void scaleChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when hand touch is globally enabled or disabled
|
||||
* @function MyAvatar.shouldDisableHandTouchChanged
|
||||
* @param {boolean} shouldDisable
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void shouldDisableHandTouchChanged(bool shouldDisable);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when hand touch is enabled or disabled for an specific entity
|
||||
* @function MyAvatar.disableHandTouchForIDChanged
|
||||
* @param {Uuid} entityID - ID of the entity that will enable hand touch effect
|
||||
* @param {boolean} disable
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void disableHandTouchForIDChanged(const QUuid& entityID, bool disable);
|
||||
|
||||
private slots:
|
||||
void leaveDomain();
|
||||
|
||||
|
@ -1627,6 +1667,7 @@ private:
|
|||
// all poses are in sensor-frame
|
||||
std::map<controller::Action, controller::Pose> _controllerPoseMap;
|
||||
mutable std::mutex _controllerPoseMapMutex;
|
||||
mutable std::mutex _disableHandTouchMutex;
|
||||
|
||||
bool _centerOfGravityModelEnabled { true };
|
||||
bool _hmdLeanRecenterEnabled { true };
|
||||
|
@ -1667,6 +1708,7 @@ private:
|
|||
bool _shouldLoadScripts { false };
|
||||
|
||||
bool _haveReceivedHeightLimitsFromDomain { false };
|
||||
int _disableHandTouchCount { 0 };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -172,11 +172,13 @@ void LoginDialog::openUrl(const QString& url) const {
|
|||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
newObject->setProperty("url", url);
|
||||
});
|
||||
LoginDialog::hide();
|
||||
} else {
|
||||
if (!hmd->getShouldShowTablet() && !qApp->isHMDMode()) {
|
||||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
newObject->setProperty("url", url);
|
||||
});
|
||||
LoginDialog::hide();
|
||||
} else {
|
||||
tablet->gotoWebScreen(url);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,12 @@ ModelOverlay::ModelOverlay()
|
|||
{
|
||||
_model->setLoadingPriority(_loadPriority);
|
||||
_isLoaded = false;
|
||||
|
||||
// Don't show overlay until textures have loaded
|
||||
_visible = false;
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
_model->setVisibleInScene(false, scene);
|
||||
}
|
||||
|
||||
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||
|
@ -101,10 +107,11 @@ void ModelOverlay::update(float deltatime) {
|
|||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->modelAddedToScene(getID(), NestableType::Overlay, _model);
|
||||
}
|
||||
bool metaDirty = false;
|
||||
if (_visibleDirty) {
|
||||
if (_visibleDirty && _texturesLoaded) {
|
||||
_visibleDirty = false;
|
||||
// don't show overlays in mirrors or spectator-cam unless _isVisibleInSecondaryCamera is true
|
||||
uint8_t modelRenderTagMask = (_isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW);
|
||||
|
||||
_model->setTagMask(modelRenderTagMask, scene);
|
||||
_model->setVisibleInScene(getVisible(), scene);
|
||||
metaDirty = true;
|
||||
|
@ -134,6 +141,8 @@ void ModelOverlay::update(float deltatime) {
|
|||
if (!_modelTextures.isEmpty()) {
|
||||
_model->setTextures(_modelTextures);
|
||||
}
|
||||
|
||||
_model->setVisibleInScene(getVisible(), scene);
|
||||
_model->updateRenderItems();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,11 +138,8 @@ void Web3DOverlay::destroyWebSurface() {
|
|||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem) {
|
||||
QObject* obj = rootItem->findChild<QObject*>("webEngineView");
|
||||
if (obj) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(obj, "stop");
|
||||
}
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
_webSurface->pause();
|
||||
|
@ -152,6 +149,11 @@ void Web3DOverlay::destroyWebSurface() {
|
|||
|
||||
// If the web surface was fetched out of the cache, release it back into the cache
|
||||
if (_cachedWebSurface) {
|
||||
// If it's going back into the cache make sure to explicitly set the URL to a blank page
|
||||
// in order to stop any resource consumption or audio related to the page.
|
||||
if (rootItem) {
|
||||
rootItem->setProperty("url", "about:blank");
|
||||
}
|
||||
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
// FIXME prevents crash on shutdown, but we shoudln't have to do this check
|
||||
if (offscreenCache) {
|
||||
|
|
|
@ -381,7 +381,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Get the rotation of the left palm in world coordinates.
|
||||
* @function MyAvatar.getLeftPalmRotation
|
||||
* @returns {Vec3} The rotation of the left palm in world coordinates.
|
||||
* @returns {Quat} The rotation of the left palm in world coordinates.
|
||||
* @example <caption>Report the rotation of your avatar's left palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getLeftPalmRotation()));
|
||||
*/
|
||||
|
@ -398,7 +398,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Get the rotation of the right palm in world coordinates.
|
||||
* @function MyAvatar.getRightPalmRotation
|
||||
* @returns {Vec3} The rotation of the right palm in world coordinates.
|
||||
* @returns {Quat} The rotation of the right palm in world coordinates.
|
||||
* @example <caption>Report the rotation of your avatar's right palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getRightPalmRotation()));
|
||||
*/
|
||||
|
|
|
@ -48,6 +48,13 @@ void HTTPManager::incomingConnection(qintptr socketDescriptor) {
|
|||
}
|
||||
|
||||
bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
|
||||
// Reject paths with embedded NULs
|
||||
if (url.path().contains(QChar(0x00))) {
|
||||
connection->respond(HTTPConnection::StatusCode400, "Embedded NULs not allowed in requests");
|
||||
qCWarning(embeddedwebserver) << "Received a request with embedded NULs";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!skipSubHandler && requestHandledByRequestHandler(connection, url)) {
|
||||
// this request was handled by our request handler object
|
||||
// so we don't need to attempt to do so in the document root
|
||||
|
@ -57,17 +64,27 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
if (!_documentRoot.isEmpty()) {
|
||||
// check to see if there is a file to serve from the document root for this path
|
||||
QString subPath = url.path();
|
||||
|
||||
|
||||
// remove any slash at the beginning of the path
|
||||
if (subPath.startsWith('/')) {
|
||||
subPath.remove(0, 1);
|
||||
}
|
||||
|
||||
|
||||
QString absoluteDocumentRoot { QFileInfo(_documentRoot).absolutePath() };
|
||||
QString filePath;
|
||||
|
||||
if (QFileInfo(_documentRoot + subPath).isFile()) {
|
||||
filePath = _documentRoot + subPath;
|
||||
} else if (subPath.size() > 0 && !subPath.endsWith('/')) {
|
||||
QFileInfo pathFileInfo { _documentRoot + subPath };
|
||||
QString absoluteFilePath { pathFileInfo.absoluteFilePath() };
|
||||
|
||||
// The absolute path for this file isn't under the document root
|
||||
if (absoluteFilePath.indexOf(absoluteDocumentRoot) != 0) {
|
||||
qCWarning(embeddedwebserver) << absoluteFilePath << "is outside the document root";
|
||||
connection->respond(HTTPConnection::StatusCode400, "Requested path outside document root");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pathFileInfo.isFile()) {
|
||||
filePath = absoluteFilePath;
|
||||
} else if (subPath.size() > 0 && !subPath.endsWith('/') && pathFileInfo.isDir()) {
|
||||
// this could be a directory with a trailing slash
|
||||
// send a redirect to the path with a slash so we can
|
||||
QString redirectLocation = '/' + subPath + '/';
|
||||
|
@ -80,6 +97,7 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8());
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode302, "", HTTPConnection::DefaultContentType, redirectHeader);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the last thing is a trailing slash then we want to look for index file
|
||||
|
@ -87,8 +105,8 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml";
|
||||
|
||||
foreach (const QString& possibleIndexFilename, possibleIndexFiles) {
|
||||
if (QFileInfo(_documentRoot + subPath + possibleIndexFilename).exists()) {
|
||||
filePath = _documentRoot + subPath + possibleIndexFilename;
|
||||
if (QFileInfo(absoluteFilePath + possibleIndexFilename).exists()) {
|
||||
filePath = absoluteFilePath + possibleIndexFilename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -363,12 +363,18 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
|
|||
return false;
|
||||
}
|
||||
|
||||
void EntityRenderer::updateModelTransform() {
|
||||
void EntityRenderer::updateModelTransformAndBound() {
|
||||
bool success = false;
|
||||
auto newModelTransform = _entity->getTransformToCenter(success);
|
||||
if (success) {
|
||||
_modelTransform = newModelTransform;
|
||||
}
|
||||
|
||||
success = false;
|
||||
auto bound = _entity->getAABox(success);
|
||||
if (success) {
|
||||
_bound = bound;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) {
|
||||
|
@ -380,15 +386,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
}
|
||||
_prevIsTransparent = transparent;
|
||||
|
||||
bool success = false;
|
||||
auto bound = entity->getAABox(success);
|
||||
if (success) {
|
||||
_bound = bound;
|
||||
}
|
||||
auto newModelTransform = entity->getTransformToCenter(success);
|
||||
if (success) {
|
||||
_modelTransform = newModelTransform;
|
||||
}
|
||||
updateModelTransformAndBound();
|
||||
|
||||
_moving = entity->isMovingRelativeToParent();
|
||||
_visible = entity->getVisible();
|
||||
|
|
|
@ -97,7 +97,7 @@ protected:
|
|||
virtual void doRender(RenderArgs* args) = 0;
|
||||
|
||||
bool isFading() const { return _isFading; }
|
||||
void updateModelTransform();
|
||||
void updateModelTransformAndBound();
|
||||
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
|
||||
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
|
||||
withWriteLock([&] {
|
||||
updateModelTransform();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -106,10 +106,8 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_position = entity->getWorldPosition();
|
||||
_dimensions = entity->getScaledDimensions();
|
||||
_orientation = entity->getWorldOrientation();
|
||||
bool success = false;
|
||||
auto newModelTransform = entity->getTransformToCenter(success);
|
||||
_renderTransform = success ? newModelTransform : getModelTransform();
|
||||
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
if (_shape == entity::Sphere) {
|
||||
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
|
||||
withWriteLock([&] {
|
||||
_dimensions = entity->getScaledDimensions();
|
||||
updateModelTransform();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -206,7 +206,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransform();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
|
|
|
@ -21,14 +21,17 @@
|
|||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <qfile.h>
|
||||
#include <qfileinfo.h>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <ResourceManager.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
|
||||
|
@ -786,13 +789,18 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) {
|
|||
QVector<glm::vec3> raw_vertices;
|
||||
QVector<glm::vec3> raw_normals;
|
||||
|
||||
addArrayOfType(indicesBuffer.blob,
|
||||
bool success = addArrayOfType(indicesBuffer.blob,
|
||||
indicesBufferview.byteOffset + indicesAccBoffset,
|
||||
indicesBufferview.byteLength,
|
||||
indicesAccessor.count,
|
||||
part.triangleIndices,
|
||||
indicesAccessor.type,
|
||||
indicesAccessor.componentType);
|
||||
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
|
||||
QList<QString> keys = primitive.attributes.values.keys();
|
||||
|
||||
foreach(auto &key, keys) {
|
||||
|
@ -805,44 +813,60 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) {
|
|||
int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
|
||||
if (key == "POSITION") {
|
||||
QVector<float> vertices;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength, vertices,
|
||||
accessor.count, vertices,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < vertices.size(); n = n + 3) {
|
||||
mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2]));
|
||||
}
|
||||
} else if (key == "NORMAL") {
|
||||
QVector<float> normals;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength,
|
||||
accessor.count,
|
||||
normals,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < normals.size(); n = n + 3) {
|
||||
mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
|
||||
}
|
||||
} else if (key == "TEXCOORD_0") {
|
||||
QVector<float> texcoords;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength,
|
||||
accessor.count,
|
||||
texcoords,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < texcoords.size(); n = n + 2) {
|
||||
mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
|
||||
}
|
||||
} else if (key == "TEXCOORD_1") {
|
||||
QVector<float> texcoords;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength,
|
||||
accessor.count,
|
||||
texcoords,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < texcoords.size(); n = n + 2) {
|
||||
mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
|
||||
}
|
||||
|
@ -888,8 +912,16 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) {
|
|||
|
||||
FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping,
|
||||
const QUrl& url, bool loadLightmaps, float lightmapLevel) {
|
||||
|
||||
_url = url;
|
||||
|
||||
// Normalize url for local files
|
||||
QUrl normalizeUrl = DependencyManager::get<ResourceManager>()->normalizeURL(url);
|
||||
if (normalizeUrl.scheme().isEmpty() || (normalizeUrl.scheme() == "file")) {
|
||||
QString localFileName = PathUtils::expandToLocalDataAbsolutePath(normalizeUrl).toLocalFile();
|
||||
_url = QUrl(QFileInfo(localFileName).absoluteFilePath());
|
||||
}
|
||||
|
||||
parseGLTF(model);
|
||||
//_file.dump();
|
||||
FBXGeometry* geometryPtr = new FBXGeometry();
|
||||
|
@ -904,6 +936,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping
|
|||
|
||||
bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) {
|
||||
QUrl binaryUrl = _url.resolved(QUrl(url).fileName());
|
||||
|
||||
qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url;
|
||||
bool success;
|
||||
std::tie<bool, QByteArray>(success, outdata) = requestData(binaryUrl);
|
||||
|
@ -1018,13 +1051,12 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia
|
|||
fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]);
|
||||
fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]);
|
||||
fbxmat.useAlbedoMap = true;
|
||||
fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]);
|
||||
fbxmat.useMetallicMap = true;
|
||||
}
|
||||
if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) {
|
||||
fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]);
|
||||
fbxmat.useRoughnessMap = true;
|
||||
|
||||
fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]);
|
||||
fbxmat.useMetallicMap = true;
|
||||
}
|
||||
if (material.pbrMetallicRoughness.defined["roughnessFactor"]) {
|
||||
fbxmat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor);
|
||||
|
@ -1043,7 +1075,7 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia
|
|||
}
|
||||
|
||||
template<typename T, typename L>
|
||||
bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<L>& outarray, int accessorType) {
|
||||
|
||||
QDataStream blobstream(bin);
|
||||
|
@ -1051,142 +1083,77 @@ bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength
|
|||
blobstream.setVersion(QDataStream::Qt_5_9);
|
||||
blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision);
|
||||
|
||||
int vsize = byteLength / sizeof(T);
|
||||
|
||||
qCDebug(modelformat) << "size1: " << vsize;
|
||||
qCDebug(modelformat) << "size1: " << count;
|
||||
int dataskipped = blobstream.skipRawData(byteOffset);
|
||||
qCDebug(modelformat) << "dataskipped: " << dataskipped;
|
||||
|
||||
|
||||
while (outarray.size() < vsize) {
|
||||
|
||||
T value1, value2, value3, value4,
|
||||
value5, value6, value7, value8,
|
||||
value9, value10, value11, value12,
|
||||
value13, value14, value15, value16;
|
||||
|
||||
if (accessorType == GLTFAccessorType::SCALAR) {
|
||||
|
||||
blobstream >> value1;
|
||||
|
||||
outarray.push_back(value1);
|
||||
} else if (accessorType == GLTFAccessorType::VEC2) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
} else if (accessorType == GLTFAccessorType::VEC3) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
} else if (accessorType == GLTFAccessorType::VEC4 || accessorType == GLTFAccessorType::MAT2) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
blobstream >> value4;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
outarray.push_back(value4);
|
||||
} else if (accessorType == GLTFAccessorType::MAT3) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
blobstream >> value4;
|
||||
blobstream >> value5;
|
||||
blobstream >> value6;
|
||||
blobstream >> value7;
|
||||
blobstream >> value8;
|
||||
blobstream >> value9;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
outarray.push_back(value4);
|
||||
outarray.push_back(value5);
|
||||
outarray.push_back(value6);
|
||||
outarray.push_back(value7);
|
||||
outarray.push_back(value8);
|
||||
outarray.push_back(value9);
|
||||
} else if (accessorType == GLTFAccessorType::MAT4) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
blobstream >> value4;
|
||||
blobstream >> value5;
|
||||
blobstream >> value6;
|
||||
blobstream >> value7;
|
||||
blobstream >> value8;
|
||||
blobstream >> value9;
|
||||
blobstream >> value10;
|
||||
blobstream >> value11;
|
||||
blobstream >> value12;
|
||||
blobstream >> value13;
|
||||
blobstream >> value14;
|
||||
blobstream >> value15;
|
||||
blobstream >> value16;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
outarray.push_back(value4);
|
||||
outarray.push_back(value5);
|
||||
outarray.push_back(value6);
|
||||
outarray.push_back(value7);
|
||||
outarray.push_back(value8);
|
||||
outarray.push_back(value9);
|
||||
outarray.push_back(value10);
|
||||
outarray.push_back(value11);
|
||||
outarray.push_back(value12);
|
||||
outarray.push_back(value13);
|
||||
outarray.push_back(value14);
|
||||
outarray.push_back(value15);
|
||||
outarray.push_back(value16);
|
||||
|
||||
int bufferCount = 0;
|
||||
switch (accessorType) {
|
||||
case GLTFAccessorType::SCALAR:
|
||||
bufferCount = 1;
|
||||
break;
|
||||
case GLTFAccessorType::VEC2:
|
||||
bufferCount = 2;
|
||||
break;
|
||||
case GLTFAccessorType::VEC3:
|
||||
bufferCount = 3;
|
||||
break;
|
||||
case GLTFAccessorType::VEC4:
|
||||
bufferCount = 4;
|
||||
break;
|
||||
case GLTFAccessorType::MAT2:
|
||||
bufferCount = 4;
|
||||
break;
|
||||
case GLTFAccessorType::MAT3:
|
||||
bufferCount = 9;
|
||||
break;
|
||||
case GLTFAccessorType::MAT4:
|
||||
bufferCount = 16;
|
||||
break;
|
||||
default:
|
||||
qWarning(modelformat) << "Unknown accessorType: " << accessorType;
|
||||
blobstream.unsetDevice();
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (int j = 0; j < bufferCount; j++) {
|
||||
if (!blobstream.atEnd()) {
|
||||
T value;
|
||||
blobstream >> value;
|
||||
outarray.push_back(value);
|
||||
} else {
|
||||
blobstream.unsetDevice();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blobstream.unsetDevice();
|
||||
return true;
|
||||
}
|
||||
template<typename T>
|
||||
bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<T>& outarray, int accessorType, int componentType) {
|
||||
|
||||
switch (componentType) {
|
||||
case GLTFAccessorComponentType::BYTE: {}
|
||||
case GLTFAccessorComponentType::UNSIGNED_BYTE: {
|
||||
readArray<uchar>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<uchar>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::SHORT: {
|
||||
readArray<short>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<short>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::UNSIGNED_INT: {
|
||||
readArray<quint32>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<uint>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::UNSIGNED_SHORT: {
|
||||
readArray<ushort>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<ushort>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::FLOAT: {
|
||||
readArray<float>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<float>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLTFReader::retriangulate(const QVector<int>& inIndices, const QVector<glm::vec3>& in_vertices,
|
||||
|
|
|
@ -762,11 +762,11 @@ private:
|
|||
bool readBinary(const QString& url, QByteArray& outdata);
|
||||
|
||||
template<typename T, typename L>
|
||||
bool readArray(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool readArray(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<L>& outarray, int accessorType);
|
||||
|
||||
template<typename T>
|
||||
bool addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool addArrayOfType(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<T>& outarray, int accessorType, int componentType);
|
||||
|
||||
void retriangulate(const QVector<int>& in_indices, const QVector<glm::vec3>& in_vertices,
|
||||
|
|
187
scripts/modules/appUi.js
Normal file
187
scripts/modules/appUi.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
"use strict";
|
||||
/*global Tablet, Script*/
|
||||
//
|
||||
// libraries/appUi.js
|
||||
//
|
||||
// Created by Howard Stearns on 3/20/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function AppUi(properties) {
|
||||
/* Example development order:
|
||||
1. var AppUi = Script.require('appUi');
|
||||
2. Put appname-i.svg, appname-a.svg in graphicsDirectory (where non-default graphicsDirectory can be added in #3).
|
||||
3. ui = new AppUi({buttonName: "APPNAME", home: "qml-or-html-path"});
|
||||
(And if converting an existing app,
|
||||
define var tablet = ui.tablet, button = ui.button; as needed.
|
||||
remove button.clicked.[dis]connect and tablet.remove(button).)
|
||||
4. Define onOpened and onClosed behavior in #3, if any.
|
||||
(And if converting an existing app, remove screenChanged.[dis]connect.)
|
||||
5. Define onMessage and sendMessage in #3, if any. onMessage is wired/unwired on open/close. If you
|
||||
want a handler to be "always on", connect it yourself at script startup.
|
||||
(And if converting an existing app, remove code that [un]wires that message handling such as
|
||||
fromQml/sendToQml or webEventReceived/emitScriptEvent.)
|
||||
6. (If converting an existing app, cleanup stuff that is no longer necessary, like references to button, tablet,
|
||||
and use isOpen, open(), and close() as needed.)
|
||||
7. lint!
|
||||
*/
|
||||
var that = this;
|
||||
function defaultButton(name, suffix) {
|
||||
var base = that[name] || (that.buttonPrefix + suffix);
|
||||
that[name] = (base.indexOf('/') >= 0) ? base : (that.graphicsDirectory + base); //poor man's merge
|
||||
}
|
||||
|
||||
// Defaults:
|
||||
that.tabletName = "com.highfidelity.interface.tablet.system";
|
||||
that.inject = "";
|
||||
that.graphicsDirectory = "icons/tablet-icons/"; // Where to look for button svgs. See below.
|
||||
that.checkIsOpen = function checkIsOpen(type, tabletUrl) { // Are we active? Value used to set isOpen.
|
||||
return (type === that.type) && that.currentUrl && (tabletUrl.indexOf(that.currentUrl) >= 0); // Actual url may have prefix or suffix.
|
||||
};
|
||||
that.setCurrentData = function setCurrentData(url) {
|
||||
that.currentUrl = url;
|
||||
that.type = /.qml$/.test(url) ? 'QML' : 'Web';
|
||||
}
|
||||
that.open = function open(optionalUrl) { // How to open the app.
|
||||
var url = optionalUrl || that.home;
|
||||
that.setCurrentData(url);
|
||||
if (that.isQML()) {
|
||||
that.tablet.loadQMLSource(url);
|
||||
} else {
|
||||
that.tablet.gotoWebScreen(url, that.inject);
|
||||
}
|
||||
};
|
||||
that.close = function close() { // How to close the app.
|
||||
that.currentUrl = "";
|
||||
// for toolbar-mode: go back to home screen, this will close the window.
|
||||
that.tablet.gotoHomeScreen();
|
||||
};
|
||||
that.buttonActive = function buttonActive(isActive) { // How to make the button active (white).
|
||||
that.button.editProperties({isActive: isActive});
|
||||
};
|
||||
that.messagesWaiting = function messagesWaiting(isWaiting) { // How to indicate a message light on button.
|
||||
// Note that waitingButton doesn't have to exist unless someone explicitly calls this with isWaiting true.
|
||||
that.button.editProperties({
|
||||
icon: isWaiting ? that.normalMessagesButton : that.normalButton,
|
||||
activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton
|
||||
});
|
||||
};
|
||||
that.isQML = function isQML() { // We set type property in onClick.
|
||||
return that.type === 'QML';
|
||||
};
|
||||
that.eventSignal = function eventSignal() { // What signal to hook onMessage to.
|
||||
return that.isQML() ? that.tablet.fromQml : that.tablet.webEventReceived;
|
||||
};
|
||||
|
||||
// Overwrite with the given properties:
|
||||
Object.keys(properties).forEach(function (key) { that[key] = properties[key]; });
|
||||
|
||||
// Properties:
|
||||
that.tablet = Tablet.getTablet(that.tabletName);
|
||||
// Must be after we gather properties.
|
||||
that.buttonPrefix = that.buttonPrefix || that.buttonName.toLowerCase() + "-";
|
||||
defaultButton('normalButton', 'i.svg');
|
||||
defaultButton('activeButton', 'a.svg');
|
||||
defaultButton('normalMessagesButton', 'i-msg.svg');
|
||||
defaultButton('activeMessagesButton', 'a-msg.svg');
|
||||
that.button = that.tablet.addButton({
|
||||
icon: that.normalButton,
|
||||
activeIcon: that.activeButton,
|
||||
text: that.buttonName,
|
||||
sortOrder: that.sortOrder
|
||||
});
|
||||
that.ignore = function ignore() { };
|
||||
|
||||
// Handlers
|
||||
that.onScreenChanged = function onScreenChanged(type, url) {
|
||||
// Set isOpen, wireEventBridge, set buttonActive as appropriate,
|
||||
// and finally call onOpened() or onClosed() IFF defined.
|
||||
console.debug(that.buttonName, 'onScreenChanged', type, url, that.isOpen);
|
||||
if (that.checkIsOpen(type, url)) {
|
||||
if (!that.isOpen) {
|
||||
that.wireEventBridge(true);
|
||||
that.buttonActive(true);
|
||||
if (that.onOpened) {
|
||||
that.onOpened();
|
||||
}
|
||||
that.isOpen = true;
|
||||
}
|
||||
|
||||
} else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden?
|
||||
if (that.isOpen) {
|
||||
that.wireEventBridge(false);
|
||||
that.buttonActive(false);
|
||||
if (that.onClosed) {
|
||||
that.onClosed();
|
||||
}
|
||||
that.isOpen = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
that.hasEventBridge = false;
|
||||
// HTML event bridge uses strings, not objects. Here we abstract over that.
|
||||
// (Although injected javascript still has to use JSON.stringify/JSON.parse.)
|
||||
that.sendToHtml = function (messageObject) { that.tablet.emitScriptEvent(JSON.stringify(messageObject)); };
|
||||
that.fromHtml = function (messageString) { that.onMessage(JSON.parse(messageString)); };
|
||||
that.wireEventBridge = function wireEventBridge(on) {
|
||||
// Uniquivocally sets that.sendMessage(messageObject) to do the right thing.
|
||||
// Sets hasEventBridge and wires onMessage to eventSignal as appropriate, IFF onMessage defined.
|
||||
var handler, isQml = that.isQML();
|
||||
// Outbound (always, regardless of whether there is an inbound handler).
|
||||
if (on) {
|
||||
that.sendMessage = isQml ? that.tablet.sendToQml : that.sendToHtml;
|
||||
} else {
|
||||
that.sendMessage = that.ignore;
|
||||
}
|
||||
|
||||
if (!that.onMessage) { return; }
|
||||
|
||||
// Inbound
|
||||
handler = isQml ? that.onMessage : that.fromHtml;
|
||||
if (on) {
|
||||
if (!that.hasEventBridge) {
|
||||
console.debug(that.buttonName, 'connecting', that.eventSignal());
|
||||
that.eventSignal().connect(handler);
|
||||
that.hasEventBridge = true;
|
||||
}
|
||||
} else {
|
||||
if (that.hasEventBridge) {
|
||||
console.debug(that.buttonName, 'disconnecting', that.eventSignal());
|
||||
that.eventSignal().disconnect(handler);
|
||||
that.hasEventBridge = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
that.isOpen = false;
|
||||
// To facilitate incremental development, only wire onClicked to do something when "home" is defined in properties.
|
||||
that.onClicked = that.home
|
||||
? function onClicked() {
|
||||
// Call open() or close(), and reset type based on current home property.
|
||||
if (that.isOpen) {
|
||||
that.close();
|
||||
} else {
|
||||
that.open();
|
||||
}
|
||||
} : that.ignore;
|
||||
that.onScriptEnding = function onScriptEnding() {
|
||||
// Close if necessary, clean up any remaining handlers, and remove the button.
|
||||
if (that.isOpen) {
|
||||
that.close();
|
||||
}
|
||||
that.tablet.screenChanged.disconnect(that.onScreenChanged);
|
||||
if (that.button) {
|
||||
if (that.onClicked) {
|
||||
that.button.clicked.disconnect(that.onClicked);
|
||||
}
|
||||
that.tablet.removeButton(that.button);
|
||||
}
|
||||
};
|
||||
// Set up the handlers.
|
||||
that.tablet.screenChanged.connect(that.onScreenChanged);
|
||||
that.button.clicked.connect(that.onClicked);
|
||||
Script.scriptEnding.connect(that.onScriptEnding);
|
||||
}
|
||||
module.exports = AppUi;
|
|
@ -159,6 +159,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
|
||||
for(var bookmarkName in message.data.bookmarks) {
|
||||
var bookmark = message.data.bookmarks[bookmarkName];
|
||||
if (!bookmark.avatarEntites) { // ensure avatarEntites always exist
|
||||
bookmark.avatarEntites = [];
|
||||
}
|
||||
|
||||
bookmark.avatarEntites.forEach(function(avatarEntity) {
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation)
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS,
|
||||
findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH,
|
||||
HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME,
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity,
|
||||
distanceBetweenEntityLocalPositionAndBoundingBox
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
|
@ -172,12 +173,9 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * TEAR_AWAY_CHECK_TIME) {
|
||||
this.lastUnequipCheckTime = now;
|
||||
if (props.parentID === MyAvatar.SELF_ID) {
|
||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
||||
var handPosition = controllerData.controllerLocations[this.hand].position;
|
||||
var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props);
|
||||
var distance = Vec3.distance(props.position, handPosition);
|
||||
if ((dist > TEAR_AWAY_DISTANCE) ||
|
||||
(distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) {
|
||||
var tearAwayDistance = TEAR_AWAY_DISTANCE * MyAvatar.sensorToWorldScale;
|
||||
var distance = distanceBetweenEntityLocalPositionAndBoundingBox(props);
|
||||
if (distance > tearAwayDistance) {
|
||||
this.autoUnequipCounter++;
|
||||
} else {
|
||||
this.autoUnequipCounter = 0;
|
||||
|
|
|
@ -14,64 +14,64 @@
|
|||
/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var MSECONDS_AFTER_LOAD = 2000;
|
||||
(function () {
|
||||
|
||||
var handTouchEnabled = true;
|
||||
var MSECONDS_AFTER_LOAD = 2000;
|
||||
var updateFingerWithIndex = 0;
|
||||
var untouchableEntities = [];
|
||||
|
||||
|
||||
// Keys to access finger data
|
||||
// Keys to access finger data
|
||||
var fingerKeys = ["pinky", "ring", "middle", "index", "thumb"];
|
||||
|
||||
// Additionally close the hands to achieve a grabbing effect
|
||||
|
||||
// Additionally close the hands to achieve a grabbing effect
|
||||
var grabPercent = { left: 0, right: 0 };
|
||||
|
||||
|
||||
var Palm = function() {
|
||||
this.position = {x: 0, y: 0, z: 0};
|
||||
this.perpendicular = {x: 0, y: 0, z: 0};
|
||||
this.distance = 0;
|
||||
this.fingers = {
|
||||
pinky: {x: 0, y: 0, z: 0},
|
||||
middle: {x: 0, y: 0, z: 0},
|
||||
ring: {x: 0, y: 0, z: 0},
|
||||
thumb: {x: 0, y: 0, z: 0},
|
||||
pinky: {x: 0, y: 0, z: 0},
|
||||
middle: {x: 0, y: 0, z: 0},
|
||||
ring: {x: 0, y: 0, z: 0},
|
||||
thumb: {x: 0, y: 0, z: 0},
|
||||
index: {x: 0, y: 0, z: 0}
|
||||
};
|
||||
this.set = false;
|
||||
};
|
||||
|
||||
|
||||
var palmData = {
|
||||
left: new Palm(),
|
||||
right: new Palm()
|
||||
};
|
||||
|
||||
var handJointNames = {left: "LeftHand", right: "RightHand"};
|
||||
|
||||
// Store which fingers are touching - if all false restate the default poses
|
||||
|
||||
// Store which fingers are touching - if all false restate the default poses
|
||||
var isTouching = {
|
||||
left: {
|
||||
pinky: false,
|
||||
middle: false,
|
||||
ring: false,
|
||||
thumb: false,
|
||||
index: false
|
||||
pinky: false,
|
||||
middle: false,
|
||||
ring: false,
|
||||
thumb: false,
|
||||
index: false
|
||||
}, right: {
|
||||
pinky: false,
|
||||
middle: false,
|
||||
ring: false,
|
||||
thumb: false,
|
||||
pinky: false,
|
||||
middle: false,
|
||||
ring: false,
|
||||
thumb: false,
|
||||
index: false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// frame count for transition to default pose
|
||||
|
||||
|
||||
var countToDefault = {
|
||||
left: 0,
|
||||
right: 0
|
||||
};
|
||||
|
||||
|
||||
// joint data for open pose
|
||||
var dataOpen = {
|
||||
left: {
|
||||
|
@ -128,7 +128,7 @@
|
|||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// joint data for close pose
|
||||
var dataClose = {
|
||||
left: {
|
||||
|
@ -185,78 +185,78 @@
|
|||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// snapshot for the default pose
|
||||
var dataDefault = {
|
||||
left: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
set: false
|
||||
},
|
||||
right: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
set: false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// joint data for the current frame
|
||||
var dataCurrent = {
|
||||
left: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}]
|
||||
},
|
||||
right: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}]
|
||||
}
|
||||
};
|
||||
|
||||
// interpolated values on joint data to smooth movement
|
||||
|
||||
// interpolated values on joint data to smooth movement
|
||||
var dataDelta = {
|
||||
left: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}]
|
||||
},
|
||||
right: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Acquire an updated value per hand every 5 frames when finger is touching (faster in)
|
||||
var touchAnimationSteps = 5;
|
||||
|
||||
// Acquire an updated value per hand every 20 frames when finger is returning to default position (slower out)
|
||||
|
||||
// Acquire an updated value per hand every 20 frames when finger is returning to default position (slower out)
|
||||
var defaultAnimationSteps = 10;
|
||||
|
||||
|
||||
// Debugging info
|
||||
var showSphere = false;
|
||||
var showLines = false;
|
||||
|
||||
|
||||
// This get setup on creation
|
||||
var linesCreated = false;
|
||||
var sphereCreated = false;
|
||||
|
||||
// Register object with API Debugger
|
||||
|
||||
// Register object with API Debugger
|
||||
var varsToDebug = {
|
||||
scriptLoaded: false,
|
||||
toggleDebugSphere: function() {
|
||||
|
@ -275,17 +275,17 @@
|
|||
},
|
||||
fingerPercent: {
|
||||
left: {
|
||||
pinky: 0.38,
|
||||
middle: 0.38,
|
||||
ring: 0.38,
|
||||
thumb: 0.38,
|
||||
pinky: 0.38,
|
||||
middle: 0.38,
|
||||
ring: 0.38,
|
||||
thumb: 0.38,
|
||||
index: 0.38
|
||||
} ,
|
||||
} ,
|
||||
right: {
|
||||
pinky: 0.38,
|
||||
middle: 0.38,
|
||||
ring: 0.38,
|
||||
thumb: 0.38,
|
||||
pinky: 0.38,
|
||||
middle: 0.38,
|
||||
ring: 0.38,
|
||||
thumb: 0.38,
|
||||
index: 0.38
|
||||
}
|
||||
},
|
||||
|
@ -300,12 +300,11 @@
|
|||
palmData: {
|
||||
left: new Palm(),
|
||||
right: new Palm()
|
||||
},
|
||||
},
|
||||
offset: {x: 0, y: 0, z: 0},
|
||||
avatarLoaded: false
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Add/Subtract the joint data - per finger joint
|
||||
function addVals(val1, val2, sign) {
|
||||
var val = [];
|
||||
|
@ -321,7 +320,7 @@
|
|||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
// Multiply/Divide the joint data - per finger joint
|
||||
function multiplyValsBy(val1, num) {
|
||||
var val = [];
|
||||
|
@ -334,7 +333,7 @@
|
|||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
// Calculate the finger lengths by adding its joint lengths
|
||||
function getJointDistances(jointNamesArray) {
|
||||
var result = {distances: [], totalDistance: 0};
|
||||
|
@ -349,13 +348,12 @@
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function dataRelativeToWorld(side, dataIn, dataOut) {
|
||||
|
||||
function dataRelativeToWorld(side, dataIn, dataOut) {
|
||||
var handJoint = handJointNames[side];
|
||||
var jointIndex = MyAvatar.getJointIndex(handJoint);
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint({x: 0, y: 0, z: 0}, jointIndex);
|
||||
|
||||
|
||||
dataOut.position = MyAvatar.jointToWorldPoint(dataIn.position, jointIndex);
|
||||
var localPerpendicular = side === "right" ? {x: 0.2, y: 0, z: 1} : {x: -0.2, y: 0, z: 1};
|
||||
dataOut.perpendicular = Vec3.normalize(
|
||||
|
@ -365,15 +363,14 @@
|
|||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
dataOut.fingers[finger] = MyAvatar.jointToWorldPoint(dataIn.fingers[finger], jointIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dataRelativeToHandJoint(side, dataIn, dataOut) {
|
||||
|
||||
function dataRelativeToHandJoint(side, dataIn, dataOut) {
|
||||
var handJoint = handJointNames[side];
|
||||
var jointIndex = MyAvatar.getJointIndex(handJoint);
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint({x: 0, y: 0, z: 0}, jointIndex);
|
||||
|
||||
|
||||
dataOut.position = MyAvatar.worldToJointPoint(dataIn.position, jointIndex);
|
||||
dataOut.perpendicular = MyAvatar.worldToJointPoint(Vec3.sum(worldPosHand, dataIn.perpendicular), jointIndex);
|
||||
dataOut.distance = dataIn.distance;
|
||||
|
@ -382,46 +379,44 @@
|
|||
dataOut.fingers[finger] = MyAvatar.worldToJointPoint(dataIn.fingers[finger], jointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate touch field; Sphere at the center of the palm,
|
||||
|
||||
// Calculate touch field; Sphere at the center of the palm,
|
||||
// perpendicular vector from the palm plane and origin of the the finger rays
|
||||
|
||||
function estimatePalmData(side) {
|
||||
// Return data object
|
||||
var data = new Palm();
|
||||
|
||||
var jointOffset = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var data = new Palm();
|
||||
|
||||
var jointOffset = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var upperSide = side[0].toUpperCase() + side.substring(1);
|
||||
var jointIndexHand = MyAvatar.getJointIndex(upperSide + "Hand");
|
||||
|
||||
|
||||
// Store position of the hand joint
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint(jointOffset, jointIndexHand);
|
||||
var minusWorldPosHand = {x: -worldPosHand.x, y: -worldPosHand.y, z: -worldPosHand.z};
|
||||
|
||||
|
||||
// Data for finger rays
|
||||
var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined};
|
||||
var positions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined};
|
||||
|
||||
|
||||
var thumbLength = 0;
|
||||
var weightCount = 0;
|
||||
|
||||
|
||||
// Calculate palm center
|
||||
|
||||
var handJointWeight = 1;
|
||||
var fingerJointWeight = 2;
|
||||
|
||||
|
||||
var palmCenter = {x: 0, y: 0, z: 0};
|
||||
palmCenter = Vec3.sum(worldPosHand, palmCenter);
|
||||
|
||||
|
||||
weightCount += handJointWeight;
|
||||
|
||||
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
var jointSuffixes = 4; // Get 4 joint names with suffix numbers (0, 1, 2, 3)
|
||||
var jointNames = getJointNames(side, finger, jointSuffixes);
|
||||
var fingerLength = getJointDistances(jointNames).totalDistance;
|
||||
|
||||
|
||||
var jointIndex = MyAvatar.getJointIndex(jointNames[0]);
|
||||
positions[finger] = MyAvatar.jointToWorldPoint(jointOffset, jointIndex);
|
||||
directions[finger] = Vec3.normalize(Vec3.sum(positions[finger], minusWorldPosHand));
|
||||
|
@ -429,66 +424,63 @@
|
|||
if (finger !== "thumb") {
|
||||
// finger joints have double the weight than the hand joint
|
||||
// This would better position the palm estimation
|
||||
|
||||
palmCenter = Vec3.sum(Vec3.multiply(fingerJointWeight, positions[finger]), palmCenter);
|
||||
|
||||
palmCenter = Vec3.sum(Vec3.multiply(fingerJointWeight, positions[finger]), palmCenter);
|
||||
weightCount += fingerJointWeight;
|
||||
} else {
|
||||
thumbLength = fingerLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// perpendicular change direction depending on the side
|
||||
data.perpendicular = (side === "right") ?
|
||||
Vec3.normalize(Vec3.cross(directions.index, directions.pinky)):
|
||||
data.perpendicular = (side === "right") ?
|
||||
Vec3.normalize(Vec3.cross(directions.index, directions.pinky)):
|
||||
Vec3.normalize(Vec3.cross(directions.pinky, directions.index));
|
||||
|
||||
|
||||
data.position = Vec3.multiply(1.0/weightCount, palmCenter);
|
||||
|
||||
|
||||
if (side === "right") {
|
||||
varsToDebug.offset = MyAvatar.worldToJointPoint(worldPosHand, jointIndexHand);
|
||||
}
|
||||
|
||||
|
||||
var palmDistanceMultiplier = 1.55; // 1.55 based on test/error for the sphere radius that best fits the hand
|
||||
data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions.index);
|
||||
data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions.index);
|
||||
|
||||
// move back thumb ray origin
|
||||
var thumbBackMultiplier = 0.2;
|
||||
data.fingers.thumb = Vec3.sum(
|
||||
data.fingers.thumb, Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular));
|
||||
|
||||
|
||||
// return getDataRelativeToHandJoint(side, data);
|
||||
dataRelativeToHandJoint(side, data, palmData[side]);
|
||||
palmData[side].set = true;
|
||||
// return palmData[side];
|
||||
}
|
||||
|
||||
|
||||
// Register GlobalDebugger for API Debugger
|
||||
Script.registerValue("GlobalDebugger", varsToDebug);
|
||||
|
||||
// store the rays for the fingers - only for debug purposes
|
||||
var fingerRays = {
|
||||
var fingerRays = {
|
||||
left: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
},
|
||||
},
|
||||
right: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Create debug overlays - finger rays + palm rays + spheres
|
||||
|
||||
var palmRay, sphereHand;
|
||||
|
||||
|
||||
function createDebugLines() {
|
||||
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
fingerRays.left[fingerKeys[i]] = Overlays.addOverlay("line3d", {
|
||||
color: { red: 0, green: 0, blue: 255 },
|
||||
|
@ -503,7 +495,7 @@
|
|||
visible: showLines
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
palmRay = {
|
||||
left: Overlays.addOverlay("line3d", {
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
|
@ -520,9 +512,8 @@
|
|||
};
|
||||
linesCreated = true;
|
||||
}
|
||||
|
||||
|
||||
function createDebugSphere() {
|
||||
|
||||
sphereHand = {
|
||||
right: Overlays.addOverlay("sphere", {
|
||||
position: MyAvatar.position,
|
||||
|
@ -536,10 +527,10 @@
|
|||
scale: { x: 0.01, y: 0.01, z: 0.01 },
|
||||
visible: showSphere
|
||||
})
|
||||
};
|
||||
};
|
||||
sphereCreated = true;
|
||||
}
|
||||
|
||||
|
||||
function acquireDefaultPose(side) {
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
|
@ -553,86 +544,87 @@
|
|||
}
|
||||
dataDefault[side].set = true;
|
||||
}
|
||||
|
||||
var rayPicks = {
|
||||
left: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
|
||||
var rayPicks = {
|
||||
left: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
},
|
||||
right: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
right: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
}
|
||||
};
|
||||
|
||||
var dataFailed = {
|
||||
left: {
|
||||
pinky: 0,
|
||||
middle: 0,
|
||||
ring: 0,
|
||||
thumb: 0,
|
||||
|
||||
var dataFailed = {
|
||||
left: {
|
||||
pinky: 0,
|
||||
middle: 0,
|
||||
ring: 0,
|
||||
thumb: 0,
|
||||
index: 0
|
||||
},
|
||||
right: {
|
||||
pinky: 0,
|
||||
middle: 0,
|
||||
ring: 0,
|
||||
thumb: 0,
|
||||
right: {
|
||||
pinky: 0,
|
||||
middle: 0,
|
||||
ring: 0,
|
||||
thumb: 0,
|
||||
index: 0
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function clearRayPicks(side) {
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
if (rayPicks[side][finger] !== undefined) {
|
||||
RayPick.removeRayPick(rayPicks[side][finger]);
|
||||
rayPicks[side][finger] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createRayPicks(side) {
|
||||
var data = palmData[side];
|
||||
clearRayPicks(side);
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
var finger = fingerKeys[i];
|
||||
var LOOKUP_DISTANCE_MULTIPLIER = 1.5;
|
||||
var dist = LOOKUP_DISTANCE_MULTIPLIER*data.distance;
|
||||
var checkOffset = {
|
||||
x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist
|
||||
var checkOffset = {
|
||||
x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist
|
||||
};
|
||||
|
||||
|
||||
var checkPoint = Vec3.sum(data.position, Vec3.multiply(2, checkOffset));
|
||||
var sensorToWorldScale = MyAvatar.getSensorToWorldScale();
|
||||
|
||||
|
||||
var origin = data.fingers[finger];
|
||||
|
||||
|
||||
var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin));
|
||||
|
||||
|
||||
origin = Vec3.multiply(1/sensorToWorldScale, origin);
|
||||
|
||||
|
||||
rayPicks[side][finger] = RayPick.createRayPick(
|
||||
{
|
||||
"enabled": false,
|
||||
{
|
||||
"enabled": false,
|
||||
"joint": handJointNames[side],
|
||||
"posOffset": origin,
|
||||
"dirOffset": direction,
|
||||
"filter": RayPick.PICK_ENTITIES
|
||||
}
|
||||
);
|
||||
|
||||
RayPick.setPrecisionPicking(rayPicks[side][finger], true);
|
||||
}
|
||||
);
|
||||
|
||||
RayPick.setPrecisionPicking(rayPicks[side][finger], true);
|
||||
}
|
||||
}
|
||||
|
||||
function activateNextRay(side, index) {
|
||||
var nextIndex = (index < fingerKeys.length-1) ? index + 1 : 0;
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
|
@ -641,46 +633,44 @@
|
|||
RayPick.enableRayPick(rayPicks[side][finger]);
|
||||
} else {
|
||||
RayPick.disableRayPick(rayPicks[side][finger]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateSphereHand(side) {
|
||||
|
||||
function updateSphereHand(side) {
|
||||
var data = new Palm();
|
||||
dataRelativeToWorld(side, palmData[side], data);
|
||||
varsToDebug.palmData[side] = palmData[side];
|
||||
|
||||
|
||||
var palmPoint = data.position;
|
||||
var LOOKUP_DISTANCE_MULTIPLIER = 1.5;
|
||||
var dist = LOOKUP_DISTANCE_MULTIPLIER*data.distance;
|
||||
|
||||
// Situate the debugging overlays
|
||||
|
||||
var checkOffset = {
|
||||
x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist
|
||||
|
||||
// Situate the debugging overlays
|
||||
var checkOffset = {
|
||||
x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist
|
||||
};
|
||||
|
||||
|
||||
var spherePos = Vec3.sum(palmPoint, checkOffset);
|
||||
var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset));
|
||||
|
||||
|
||||
if (showLines) {
|
||||
Overlays.editOverlay(palmRay[side], {
|
||||
start: palmPoint,
|
||||
end: checkPoint,
|
||||
visible: showLines
|
||||
});
|
||||
});
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
Overlays.editOverlay(fingerRays[side][fingerKeys[i]], {
|
||||
start: data.fingers[fingerKeys[i]],
|
||||
end: checkPoint,
|
||||
visible: showLines
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (showSphere) {
|
||||
Overlays.editOverlay(sphereHand[side], {
|
||||
position: spherePos,
|
||||
|
@ -690,16 +680,16 @@
|
|||
z: 2*dist
|
||||
},
|
||||
visible: showSphere
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Update the intersection of only one finger at a time
|
||||
|
||||
var finger = fingerKeys[updateFingerWithIndex];
|
||||
|
||||
|
||||
var grabbables = Entities.findEntities(spherePos, dist);
|
||||
|
||||
var finger = fingerKeys[updateFingerWithIndex];
|
||||
var nearbyEntities = Entities.findEntities(spherePos, dist);
|
||||
// Filter the entities that are allowed to be touched
|
||||
var touchableEntities = nearbyEntities.filter(function (id) {
|
||||
return untouchableEntities.indexOf(id) == -1;
|
||||
});
|
||||
var intersection;
|
||||
if (rayPicks[side][finger] !== undefined) {
|
||||
intersection = RayPick.getPrevRayPickResult(rayPicks[side][finger]);
|
||||
|
@ -708,11 +698,10 @@
|
|||
var animationSteps = defaultAnimationSteps;
|
||||
var newFingerData = dataDefault[side][finger];
|
||||
var isAbleToGrab = false;
|
||||
if (grabbables.length > 0) {
|
||||
|
||||
RayPick.setIncludeItems(rayPicks[side][finger], grabbables);
|
||||
if (touchableEntities.length > 0) {
|
||||
RayPick.setIncludeItems(rayPicks[side][finger], touchableEntities);
|
||||
|
||||
if (intersection === undefined) {
|
||||
if (intersection === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -725,28 +714,27 @@
|
|||
// Store if this finger is touching something
|
||||
isTouching[side][finger] = isAbleToGrab;
|
||||
if (isAbleToGrab) {
|
||||
// update the open/close percentage for this finger
|
||||
|
||||
// update the open/close percentage for this finger
|
||||
var FINGER_REACT_MULTIPLIER = 2.8;
|
||||
|
||||
|
||||
percent = intersection.distance/(FINGER_REACT_MULTIPLIER*dist);
|
||||
|
||||
|
||||
var THUMB_FACTOR = 0.2;
|
||||
var FINGER_FACTOR = 0.05;
|
||||
|
||||
|
||||
// Amount of grab coefficient added to the fingers - thumb is higher
|
||||
var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR;
|
||||
var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR;
|
||||
percent += grabMultiplier * grabPercent[side];
|
||||
|
||||
|
||||
// Calculate new interpolation data
|
||||
var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1);
|
||||
// Assign close/open ratio to finger to simulate touch
|
||||
newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1);
|
||||
newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1);
|
||||
animationSteps = touchAnimationSteps;
|
||||
}
|
||||
}
|
||||
varsToDebug.fingerPercent[side][finger] = percent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (!isAbleToGrab) {
|
||||
dataFailed[side][finger] = dataFailed[side][finger] === 0 ? 1 : 2;
|
||||
} else {
|
||||
|
@ -755,13 +743,12 @@
|
|||
// If it only fails once it will not update increments
|
||||
if (dataFailed[side][finger] !== 1) {
|
||||
// Calculate animation increments
|
||||
dataDelta[side][finger] =
|
||||
multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps);
|
||||
dataDelta[side][finger] =
|
||||
multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Recreate the finger joint names
|
||||
|
||||
function getJointNames(side, finger, count) {
|
||||
var names = [];
|
||||
for (var i = 1; i < count+1; i++) {
|
||||
|
@ -772,30 +759,34 @@
|
|||
}
|
||||
|
||||
// Capture the controller values
|
||||
|
||||
var leftTriggerPress = function (value) {
|
||||
varsToDebug.triggerValues.leftTriggerValue = value;
|
||||
// the value for the trigger increments the hand-close percentage
|
||||
// the value for the trigger increments the hand-close percentage
|
||||
grabPercent.left = value;
|
||||
};
|
||||
|
||||
var leftTriggerClick = function (value) {
|
||||
varsToDebug.triggerValues.leftTriggerClicked = value;
|
||||
};
|
||||
|
||||
var rightTriggerPress = function (value) {
|
||||
varsToDebug.triggerValues.rightTriggerValue = value;
|
||||
// the value for the trigger increments the hand-close percentage
|
||||
// the value for the trigger increments the hand-close percentage
|
||||
grabPercent.right = value;
|
||||
};
|
||||
|
||||
var rightTriggerClick = function (value) {
|
||||
varsToDebug.triggerValues.rightTriggerClicked = value;
|
||||
};
|
||||
|
||||
var leftSecondaryPress = function (value) {
|
||||
varsToDebug.triggerValues.leftSecondaryValue = value;
|
||||
};
|
||||
|
||||
var rightSecondaryPress = function (value) {
|
||||
varsToDebug.triggerValues.rightSecondaryValue = value;
|
||||
};
|
||||
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.handTouch";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from([Controller.Standard.RT]).peek().to(rightTriggerPress);
|
||||
|
@ -809,16 +800,17 @@
|
|||
mapping.from([Controller.Standard.RightGrip]).peek().to(rightSecondaryPress);
|
||||
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
|
||||
|
||||
if (showLines && !linesCreated) {
|
||||
createDebugLines();
|
||||
linesCreated = true;
|
||||
}
|
||||
|
||||
if (showSphere && !sphereCreated) {
|
||||
createDebugSphere();
|
||||
sphereCreated = true;
|
||||
}
|
||||
|
||||
|
||||
function getTouching(side) {
|
||||
var animating = false;
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
|
@ -827,19 +819,70 @@
|
|||
}
|
||||
return animating; // return false only if none of the fingers are touching
|
||||
}
|
||||
|
||||
|
||||
function reEstimatePalmData() {
|
||||
["right", "left"].forEach(function(side) {
|
||||
estimatePalmData(side);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function recreateRayPicks() {
|
||||
["right", "left"].forEach(function(side) {
|
||||
createRayPicks(side);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function cleanUp() {
|
||||
["right", "left"].forEach(function (side) {
|
||||
if (linesCreated) {
|
||||
Overlays.deleteOverlay(palmRay[side]);
|
||||
}
|
||||
if (sphereCreated) {
|
||||
Overlays.deleteOverlay(sphereHand[side]);
|
||||
}
|
||||
clearRayPicks(side);
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
var jointSuffixes = 3; // We need to clear the joints 0, 1 and 2 joints
|
||||
var names = getJointNames(side, finger, jointSuffixes);
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
var index = MyAvatar.getJointIndex(names[j]);
|
||||
MyAvatar.clearJointData(index);
|
||||
}
|
||||
if (linesCreated) {
|
||||
Overlays.deleteOverlay(fingerRays[side][finger]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MyAvatar.shouldDisableHandTouchChanged.connect(function (shouldDisable) {
|
||||
if (shouldDisable) {
|
||||
if (handTouchEnabled) {
|
||||
cleanUp();
|
||||
}
|
||||
} else {
|
||||
if (!handTouchEnabled) {
|
||||
reEstimatePalmData();
|
||||
recreateRayPicks();
|
||||
}
|
||||
}
|
||||
handTouchEnabled = !shouldDisable;
|
||||
});
|
||||
|
||||
MyAvatar.disableHandTouchForIDChanged.connect(function (entityID, disable) {
|
||||
var entityIndex = untouchableEntities.indexOf(entityID);
|
||||
if (disable) {
|
||||
if (entityIndex == -1) {
|
||||
untouchableEntities.push(entityID);
|
||||
}
|
||||
} else {
|
||||
if (entityIndex != -1) {
|
||||
untouchableEntities.splice(entityIndex, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MyAvatar.onLoadComplete.connect(function () {
|
||||
// Sometimes the rig is not ready when this signal is trigger
|
||||
console.log("avatar loaded");
|
||||
|
@ -848,78 +891,55 @@
|
|||
recreateRayPicks();
|
||||
}, MSECONDS_AFTER_LOAD);
|
||||
});
|
||||
|
||||
|
||||
MyAvatar.sensorToWorldScaleChanged.connect(function() {
|
||||
reEstimatePalmData();
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
["right", "left"].forEach(function(side) {
|
||||
if (linesCreated) {
|
||||
Overlays.deleteOverlay(palmRay[side]);
|
||||
}
|
||||
if (sphereCreated) {
|
||||
Overlays.deleteOverlay(sphereHand[side]);
|
||||
}
|
||||
clearRayPicks(side);
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
|
||||
var finger = fingerKeys[i];
|
||||
var jointSuffixes = 3; // We need to clear the joints 0, 1 and 2 joints
|
||||
var names = getJointNames(side, finger, jointSuffixes);
|
||||
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
var index = MyAvatar.getJointIndex(names[j]);
|
||||
MyAvatar.clearJointData(index);
|
||||
}
|
||||
|
||||
if (linesCreated) {
|
||||
Overlays.deleteOverlay(fingerRays[side][finger]);
|
||||
}
|
||||
}
|
||||
});
|
||||
Script.scriptEnding.connect(function () {
|
||||
cleanUp();
|
||||
});
|
||||
|
||||
Script.update.connect(function() {
|
||||
|
||||
|
||||
Script.update.connect(function () {
|
||||
|
||||
if (!handTouchEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// index of the finger that needs to be updated this frame
|
||||
|
||||
updateFingerWithIndex = (updateFingerWithIndex < fingerKeys.length-1) ? updateFingerWithIndex + 1 : 0;
|
||||
|
||||
|
||||
["right", "left"].forEach(function(side) {
|
||||
|
||||
|
||||
if (!palmData[side].set) {
|
||||
reEstimatePalmData();
|
||||
recreateRayPicks();
|
||||
}
|
||||
|
||||
|
||||
// recalculate the base data
|
||||
updateSphereHand(side);
|
||||
activateNextRay(side, updateFingerWithIndex);
|
||||
|
||||
|
||||
// this vars manage the transition to default pose
|
||||
var isHandTouching = getTouching(side);
|
||||
countToDefault[side] = isHandTouching ? 0 : countToDefault[side] + 1;
|
||||
|
||||
|
||||
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
var jointSuffixes = 3; // We need to update rotation of the 0, 1 and 2 joints
|
||||
var names = getJointNames(side, finger, jointSuffixes);
|
||||
|
||||
var names = getJointNames(side, finger, jointSuffixes);
|
||||
|
||||
// Add the animation increments
|
||||
|
||||
dataCurrent[side][finger] = addVals(dataCurrent[side][finger], dataDelta[side][finger], 1);
|
||||
|
||||
dataCurrent[side][finger] = addVals(dataCurrent[side][finger], dataDelta[side][finger], 1);
|
||||
|
||||
// update every finger joint
|
||||
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
var index = MyAvatar.getJointIndex(names[j]);
|
||||
// if no finger is touching restate the default poses
|
||||
if (isHandTouching || (dataDefault[side].set &&
|
||||
if (isHandTouching || (dataDefault[side].set &&
|
||||
countToDefault[side] < fingerKeys.length*touchAnimationSteps)) {
|
||||
var quatRot = dataCurrent[side][finger][j];
|
||||
MyAvatar.setJointRotation(index, quatRot);
|
||||
MyAvatar.setJointRotation(index, quatRot);
|
||||
} else {
|
||||
MyAvatar.clearJointData(index);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = {
|
|||
dimensions: viveNaturalDimensions,
|
||||
|
||||
parts: {
|
||||
// DISABLED FOR NOW
|
||||
/*
|
||||
tips: {
|
||||
type: "static",
|
||||
modelURL: viveTipsModelURL,
|
||||
|
@ -103,6 +105,7 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = {
|
|||
}
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
// The touchpad type draws a dot indicating the current touch/thumb position
|
||||
// and swaps in textures based on the thumb position.
|
||||
|
@ -215,6 +218,8 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = {
|
|||
},
|
||||
|
||||
parts: {
|
||||
// DISABLED FOR NOW
|
||||
/*
|
||||
tips: {
|
||||
type: "static",
|
||||
modelURL: viveTipsModelURL,
|
||||
|
@ -242,6 +247,7 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = {
|
|||
}
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
// The touchpad type draws a dot indicating the current touch/thumb position
|
||||
// and swaps in textures based on the thumb position.
|
||||
|
|
|
@ -63,6 +63,15 @@ var createToolsWindow = new CreateWindow(
|
|||
false
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Returns true in case we should use the tablet version of the CreateApp
|
||||
* @returns boolean
|
||||
*/
|
||||
var shouldUseEditTabletApp = function() {
|
||||
return HMD.active || (!HMD.active && !Settings.getValue("desktopTabletBecomesToolbar", true));
|
||||
};
|
||||
|
||||
|
||||
var selectionDisplay = SelectionDisplay;
|
||||
var selectionManager = SelectionManager;
|
||||
|
||||
|
@ -88,11 +97,12 @@ var cameraManager = new CameraManager();
|
|||
var grid = new Grid();
|
||||
var gridTool = new GridTool({
|
||||
horizontalGrid: grid,
|
||||
createToolsWindow: createToolsWindow
|
||||
createToolsWindow: createToolsWindow,
|
||||
shouldUseEditTabletApp: shouldUseEditTabletApp
|
||||
});
|
||||
gridTool.setVisible(false);
|
||||
|
||||
var entityListTool = new EntityListTool();
|
||||
var entityListTool = new EntityListTool(shouldUseEditTabletApp);
|
||||
|
||||
selectionManager.addEventListener(function () {
|
||||
selectionDisplay.updateHandles();
|
||||
|
@ -578,7 +588,8 @@ var toolBar = (function () {
|
|||
});
|
||||
createButton = activeButton;
|
||||
tablet.screenChanged.connect(function (type, url) {
|
||||
var isGoingToHomescreenOnDesktop = (!HMD.active && (url === 'hifi/tablet/TabletHome.qml' || url === ''));
|
||||
var isGoingToHomescreenOnDesktop = (!shouldUseEditTabletApp() &&
|
||||
(url === 'hifi/tablet/TabletHome.qml' || url === ''));
|
||||
if (isActive && (type !== "QML" || url !== "hifi/tablet/Edit.qml") && !isGoingToHomescreenOnDesktop) {
|
||||
that.setActive(false);
|
||||
}
|
||||
|
@ -605,7 +616,7 @@ var toolBar = (function () {
|
|||
});
|
||||
function createNewEntityDialogButtonCallback(entityType) {
|
||||
return function() {
|
||||
if (HMD.active) {
|
||||
if (shouldUseEditTabletApp()) {
|
||||
// tablet version of new-model dialog
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.pushOntoStack("hifi/tablet/New" + entityType + "Dialog.qml");
|
||||
|
@ -837,7 +848,7 @@ var toolBar = (function () {
|
|||
selectionDisplay.triggerMapping.disable();
|
||||
tablet.landscape = false;
|
||||
} else {
|
||||
if (HMD.active) {
|
||||
if (shouldUseEditTabletApp()) {
|
||||
tablet.loadQMLSource("hifi/tablet/Edit.qml", true);
|
||||
} else {
|
||||
// make other apps inactive while in desktop mode
|
||||
|
@ -1075,15 +1086,19 @@ function mouseReleaseEvent(event) {
|
|||
}
|
||||
}
|
||||
|
||||
function wasTabletClicked(event) {
|
||||
function wasTabletOrEditHandleClicked(event) {
|
||||
var rayPick = Camera.computePickRay(event.x, event.y);
|
||||
var tabletIDs = getMainTabletIDs();
|
||||
if (tabletIDs.length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
var result = Overlays.findRayIntersection(rayPick, true, getMainTabletIDs());
|
||||
return result.intersects;
|
||||
var result = Overlays.findRayIntersection(rayPick, true);
|
||||
if (result.intersects) {
|
||||
var overlayID = result.overlayID;
|
||||
var tabletIDs = getMainTabletIDs();
|
||||
if (tabletIDs.indexOf(overlayID) >= 0) {
|
||||
return true;
|
||||
} else if (selectionDisplay.isEditHandle(overlayID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function mouseClickEvent(event) {
|
||||
|
@ -1091,8 +1106,8 @@ function mouseClickEvent(event) {
|
|||
var result, properties, tabletClicked;
|
||||
if (isActive && event.isLeftButton) {
|
||||
result = findClickedEntity(event);
|
||||
tabletClicked = wasTabletClicked(event);
|
||||
if (tabletClicked) {
|
||||
tabletOrEditHandleClicked = wasTabletOrEditHandleClicked(event);
|
||||
if (tabletOrEditHandleClicked) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1573,15 +1588,11 @@ function deleteSelectedEntities() {
|
|||
Entities.deleteEntity(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (savedProperties.length > 0) {
|
||||
SelectionManager.clearSelections();
|
||||
pushCommandForSelections([], savedProperties);
|
||||
|
||||
entityListTool.webView.emitScriptEvent(JSON.stringify({
|
||||
type: "deleted",
|
||||
ids: deletedIDs
|
||||
}));
|
||||
entityListTool.deleteEntities(deletedIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1853,13 +1864,7 @@ var keyReleaseEvent = function (event) {
|
|||
}
|
||||
} else if (event.text === 'g') {
|
||||
if (isActive && selectionManager.hasSelection()) {
|
||||
var newPosition = selectionManager.worldPosition;
|
||||
newPosition = Vec3.subtract(newPosition, {
|
||||
x: 0,
|
||||
y: selectionManager.worldDimensions.y * 0.5,
|
||||
z: 0
|
||||
});
|
||||
grid.setPosition(newPosition);
|
||||
grid.moveToSelection();
|
||||
}
|
||||
} else if (event.key === KEY_P && event.isControl && !event.isAutoRepeat ) {
|
||||
if (event.isShifted) {
|
||||
|
@ -1917,7 +1922,11 @@ function applyEntityProperties(data) {
|
|||
Entities.deleteEntity(entityID);
|
||||
}
|
||||
|
||||
selectionManager.setSelections(selectedEntityIDs);
|
||||
// We might be getting an undo while edit.js is disabled. If that is the case, don't set
|
||||
// our selections, causing the edit widgets to display.
|
||||
if (isActive) {
|
||||
selectionManager.setSelections(selectedEntityIDs);
|
||||
}
|
||||
}
|
||||
|
||||
// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the
|
||||
|
@ -1999,8 +2008,8 @@ var PropertiesTool = function (opts) {
|
|||
|
||||
that.setVisible = function (newVisible) {
|
||||
visible = newVisible;
|
||||
webView.setVisible(HMD.active && visible);
|
||||
createToolsWindow.setVisible(!HMD.active && visible);
|
||||
webView.setVisible(shouldUseEditTabletApp() && visible);
|
||||
createToolsWindow.setVisible(!shouldUseEditTabletApp() && visible);
|
||||
};
|
||||
|
||||
that.setVisible(false);
|
||||
|
@ -2426,7 +2435,7 @@ function selectParticleEntity(entityID) {
|
|||
|
||||
// Switch to particle explorer
|
||||
var selectTabMethod = { method: 'selectTab', params: { id: 'particle' } };
|
||||
if (HMD.active) {
|
||||
if (shouldUseEditTabletApp()) {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.sendToQml(selectTabMethod);
|
||||
} else {
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
highlightTargetEntity:true,
|
||||
clearHighlightedEntities:true,
|
||||
unhighlightTargetEntity:true
|
||||
distanceBetweenEntityLocalPositionAndBoundingBox: true
|
||||
*/
|
||||
|
||||
MSECS_PER_SEC = 1000.0;
|
||||
|
@ -130,7 +131,9 @@ DISPATCHER_PROPERTIES = [
|
|||
"type",
|
||||
"href",
|
||||
"cloneable",
|
||||
"cloneDynamic"
|
||||
"cloneDynamic",
|
||||
"localPosition",
|
||||
"localRotation"
|
||||
];
|
||||
|
||||
// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step
|
||||
|
@ -413,6 +416,25 @@ findHandChildEntities = function(hand) {
|
|||
});
|
||||
};
|
||||
|
||||
distanceBetweenEntityLocalPositionAndBoundingBox = function(entityProps) {
|
||||
var localPoint = entityProps.localPosition;
|
||||
var entityXform = new Xform(entityProps.rotation, entityProps.position);
|
||||
var minOffset = Vec3.multiplyVbyV(entityProps.registrationPoint, entityProps.dimensions);
|
||||
var maxOffset = Vec3.multiplyVbyV(Vec3.subtract(ONE_VEC, entityProps.registrationPoint), entityProps.dimensions);
|
||||
var localMin = Vec3.subtract(entityXform.trans, minOffset);
|
||||
var localMax = Vec3.sum(entityXform.trans, maxOffset);
|
||||
|
||||
var v = {x: localPoint.x, y: localPoint.y, z: localPoint.z};
|
||||
v.x = Math.max(v.x, localMin.x);
|
||||
v.x = Math.min(v.x, localMax.x);
|
||||
v.y = Math.max(v.y, localMin.y);
|
||||
v.y = Math.min(v.y, localMax.y);
|
||||
v.z = Math.max(v.z, localMin.z);
|
||||
v.z = Math.min(v.z, localMax.z);
|
||||
|
||||
return Vec3.distance(v, localPoint);
|
||||
};
|
||||
|
||||
distanceBetweenPointAndEntityBoundingBox = function(point, entityProps) {
|
||||
var entityXform = new Xform(entityProps.rotation, entityProps.position);
|
||||
var localPoint = entityXform.inv().xformPoint(point);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
|
||||
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */
|
||||
|
||||
EntityListTool = function() {
|
||||
EntityListTool = function(shouldUseEditTabletApp) {
|
||||
var that = {};
|
||||
|
||||
var CreateWindow = Script.require('../modules/createWindow.js');
|
||||
|
@ -55,8 +55,8 @@ EntityListTool = function() {
|
|||
|
||||
that.setVisible = function(newVisible) {
|
||||
visible = newVisible;
|
||||
webView.setVisible(HMD.active && visible);
|
||||
entityListWindow.setVisible(!HMD.active && visible);
|
||||
webView.setVisible(shouldUseEditTabletApp() && visible);
|
||||
entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
|
||||
};
|
||||
|
||||
that.setVisible(false);
|
||||
|
@ -93,12 +93,18 @@ EntityListTool = function() {
|
|||
};
|
||||
|
||||
that.removeEntities = function (deletedIDs, selectedIDs) {
|
||||
var data = {
|
||||
emitJSONScriptEvent({
|
||||
type: 'removeEntities',
|
||||
deletedIDs: deletedIDs,
|
||||
selectedIDs: selectedIDs
|
||||
};
|
||||
webView.emitScriptEvent(JSON.stringify(data));
|
||||
});
|
||||
};
|
||||
|
||||
that.deleteEntities = function (deletedIDs) {
|
||||
emitJSONScriptEvent({
|
||||
type: "deleted",
|
||||
ids: deletedIDs
|
||||
});
|
||||
};
|
||||
|
||||
function valueIfDefined(value) {
|
||||
|
|
|
@ -658,6 +658,7 @@ SelectionDisplay = (function() {
|
|||
selectionBox,
|
||||
iconSelectionBox
|
||||
];
|
||||
var maximumHandleInAllOverlays = handleCloner;
|
||||
|
||||
overlayNames[handleTranslateXCone] = "handleTranslateXCone";
|
||||
overlayNames[handleTranslateXCylinder] = "handleTranslateXCylinder";
|
||||
|
@ -781,6 +782,12 @@ SelectionDisplay = (function() {
|
|||
return Math.abs(position.x) <= box.dimensions.x / 2 && Math.abs(position.y) <= box.dimensions.y / 2
|
||||
&& Math.abs(position.z) <= box.dimensions.z / 2;
|
||||
}
|
||||
|
||||
that.isEditHandle = function(overlayID) {
|
||||
var overlayIndex = allOverlays.indexOf(overlayID);
|
||||
var maxHandleIndex = allOverlays.indexOf(maximumHandleInAllOverlays);
|
||||
return overlayIndex >= 0 && overlayIndex <= maxHandleIndex;
|
||||
};
|
||||
|
||||
// FUNCTION: MOUSE PRESS EVENT
|
||||
that.mousePressEvent = function (event) {
|
||||
|
@ -1186,10 +1193,17 @@ SelectionDisplay = (function() {
|
|||
var localRotationZ = Quat.fromPitchYawRollDegrees(rotationDegrees, 0, 0);
|
||||
var rotationZ = Quat.multiply(rotation, localRotationZ);
|
||||
worldRotationZ = rotationZ;
|
||||
|
||||
var selectionBoxGeometry = {
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
dimensions: dimensions
|
||||
};
|
||||
var isCameraInsideBox = isPointInsideBox(Camera.position, selectionBoxGeometry);
|
||||
|
||||
// in HMD we clamp the overlays to the bounding box for now so lasers can hit them
|
||||
// in HMD if outside the bounding box clamp the overlays to the bounding box for now so lasers can hit them
|
||||
var maxHandleDimension = 0;
|
||||
if (HMD.active) {
|
||||
if (HMD.active && !isCameraInsideBox) {
|
||||
maxHandleDimension = Math.max(dimensions.x, dimensions.y, dimensions.z);
|
||||
}
|
||||
|
||||
|
@ -1438,12 +1452,6 @@ SelectionDisplay = (function() {
|
|||
var inModeRotate = isActiveTool(handleRotatePitchRing) ||
|
||||
isActiveTool(handleRotateYawRing) ||
|
||||
isActiveTool(handleRotateRollRing);
|
||||
var selectionBoxGeometry = {
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
dimensions: dimensions
|
||||
};
|
||||
var isCameraInsideBox = isPointInsideBox(Camera.position, selectionBoxGeometry);
|
||||
selectionBoxGeometry.visible = !inModeRotate && !isCameraInsideBox;
|
||||
Overlays.editOverlay(selectionBox, selectionBoxGeometry);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html');
|
||||
|
||||
Grid = function(opts) {
|
||||
Grid = function() {
|
||||
var that = {};
|
||||
var gridColor = { red: 0, green: 0, blue: 0 };
|
||||
var gridAlpha = 0.6;
|
||||
|
@ -154,6 +154,12 @@ Grid = function(opts) {
|
|||
that.emitUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
that.moveToSelection = function() {
|
||||
var newPosition = SelectionManager.worldPosition;
|
||||
newPosition = Vec3.subtract(newPosition, { x: 0, y: SelectionManager.worldDimensions.y * 0.5, z: 0 });
|
||||
that.setPosition(newPosition);
|
||||
};
|
||||
|
||||
that.emitUpdate = function() {
|
||||
if (that.onUpdate) {
|
||||
|
@ -241,6 +247,7 @@ GridTool = function(opts) {
|
|||
var horizontalGrid = opts.horizontalGrid;
|
||||
var verticalGrid = opts.verticalGrid;
|
||||
var createToolsWindow = opts.createToolsWindow;
|
||||
var shouldUseEditTabletApp = opts.shouldUseEditTabletApp;
|
||||
var listeners = [];
|
||||
|
||||
var webView = null;
|
||||
|
@ -280,9 +287,7 @@ GridTool = function(opts) {
|
|||
}
|
||||
horizontalGrid.setPosition(position);
|
||||
} else if (action == "moveToSelection") {
|
||||
var newPosition = selectionManager.worldPosition;
|
||||
newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 });
|
||||
grid.setPosition(newPosition);
|
||||
horizontalGrid.moveToSelection();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -295,7 +300,7 @@ GridTool = function(opts) {
|
|||
};
|
||||
|
||||
that.setVisible = function(visible) {
|
||||
webView.setVisible(HMD.active && visible);
|
||||
webView.setVisible(shouldUseEditTabletApp() && visible);
|
||||
};
|
||||
|
||||
return that;
|
||||
|
|
|
@ -12,14 +12,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var request = Script.require('request').request;
|
||||
var request = Script.require('request').request;
|
||||
var AppUi = Script.require('appUi');
|
||||
|
||||
var populateNearbyUserList, color, textures, removeOverlays,
|
||||
controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged,
|
||||
controllerComputePickRay, off,
|
||||
receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL,
|
||||
createAudioInterval, tablet, CHANNEL, getConnectionData, findableByChanged,
|
||||
CHANNEL, getConnectionData, findableByChanged,
|
||||
avatarAdded, avatarRemoved, avatarSessionChanged; // forward references;
|
||||
|
||||
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
|
||||
|
@ -40,6 +41,7 @@ var HOVER_TEXTURES = {
|
|||
var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6};
|
||||
var SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29};
|
||||
var HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now
|
||||
var METAVERSE_BASE = Account.metaverseServerURL;
|
||||
|
||||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
|
@ -221,7 +223,7 @@ function convertDbToLinear(decibels) {
|
|||
return Math.pow(2, decibels / 10.0);
|
||||
}
|
||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
var data;
|
||||
var data, connectionUserName, friendUserName;
|
||||
switch (message.method) {
|
||||
case 'selected':
|
||||
selectedIds = message.params;
|
||||
|
@ -281,7 +283,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
}
|
||||
getConnectionData(false);
|
||||
});
|
||||
break
|
||||
break;
|
||||
|
||||
case 'removeFriend':
|
||||
friendUserName = message.params;
|
||||
|
@ -296,7 +298,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
}
|
||||
getConnectionData(friendUserName);
|
||||
});
|
||||
break
|
||||
break;
|
||||
case 'addFriend':
|
||||
friendUserName = message.params;
|
||||
print("Adding " + friendUserName + " to friends.");
|
||||
|
@ -307,24 +309,23 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
body: {
|
||||
username: friendUserName,
|
||||
}
|
||||
}, function (error, response) {
|
||||
if (error || (response.status !== 'success')) {
|
||||
print("Error: unable to friend " + friendUserName, error || response.status);
|
||||
return;
|
||||
}
|
||||
getConnectionData(friendUserName);
|
||||
}, function (error, response) {
|
||||
if (error || (response.status !== 'success')) {
|
||||
print("Error: unable to friend " + friendUserName, error || response.status);
|
||||
return;
|
||||
}
|
||||
);
|
||||
getConnectionData(friendUserName);
|
||||
});
|
||||
break;
|
||||
case 'http.request':
|
||||
break; // Handled by request-service.
|
||||
break; // Handled by request-service.
|
||||
default:
|
||||
print('Unrecognized message from Pal.qml:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
function sendToQml(message) {
|
||||
tablet.sendToQml(message);
|
||||
ui.sendMessage(message);
|
||||
}
|
||||
function updateUser(data) {
|
||||
print('PAL update:', JSON.stringify(data));
|
||||
|
@ -334,7 +335,6 @@ function updateUser(data) {
|
|||
// User management services
|
||||
//
|
||||
// These are prototype versions that will be changed when the back end changes.
|
||||
var METAVERSE_BASE = Account.metaverseServerURL;
|
||||
|
||||
function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise.
|
||||
request({
|
||||
|
@ -362,7 +362,7 @@ function getProfilePicture(username, callback) { // callback(url) if successfull
|
|||
});
|
||||
}
|
||||
function getAvailableConnections(domain, callback) { // callback([{usename, location}...]) if successfull. (Logs otherwise)
|
||||
url = METAVERSE_BASE + '/api/v1/users?per_page=400&'
|
||||
var url = METAVERSE_BASE + '/api/v1/users?per_page=400&';
|
||||
if (domain) {
|
||||
url += 'status=' + domain.slice(1, -1); // without curly braces
|
||||
} else {
|
||||
|
@ -373,7 +373,7 @@ function getAvailableConnections(domain, callback) { // callback([{usename, loca
|
|||
});
|
||||
}
|
||||
function getInfoAboutUser(specificUsername, callback) {
|
||||
url = METAVERSE_BASE + '/api/v1/users?filter=connections'
|
||||
var url = METAVERSE_BASE + '/api/v1/users?filter=connections';
|
||||
requestJSON(url, function (connectionsData) {
|
||||
for (user in connectionsData.users) {
|
||||
if (connectionsData.users[user].username === specificUsername) {
|
||||
|
@ -447,21 +447,24 @@ function populateNearbyUserList(selectData, oldAudioData) {
|
|||
verticalAngleNormal = filter && Quat.getRight(orientation),
|
||||
horizontalAngleNormal = filter && Quat.getUp(orientation);
|
||||
avatarsOfInterest = {};
|
||||
avatars.forEach(function (id) {
|
||||
var avatar = AvatarList.getAvatar(id);
|
||||
var name = avatar.sessionDisplayName;
|
||||
|
||||
var avatarData = AvatarList.getPalData().data;
|
||||
|
||||
avatarData.forEach(function (currentAvatarData) {
|
||||
var id = currentAvatarData.sessionUUID;
|
||||
var name = currentAvatarData.sessionDisplayName;
|
||||
if (!name) {
|
||||
// Either we got a data packet but no identity yet, or something is really messed up. In any case,
|
||||
// we won't be able to do anything with this user, so don't include them.
|
||||
// In normal circumstances, a refresh will bring in the new user, but if we're very heavily loaded,
|
||||
// we could be losing and gaining people randomly.
|
||||
print('No avatar identity data for', id);
|
||||
print('No avatar identity data for', currentAvatarData.sessionUUID);
|
||||
return;
|
||||
}
|
||||
if (id && myPosition && (Vec3.distance(avatar.position, myPosition) > filter.distance)) {
|
||||
if (id && myPosition && (Vec3.distance(currentAvatarData.position, myPosition) > filter.distance)) {
|
||||
return;
|
||||
}
|
||||
var normal = id && filter && Vec3.normalize(Vec3.subtract(avatar.position, myPosition));
|
||||
var normal = id && filter && Vec3.normalize(Vec3.subtract(currentAvatarData.position, myPosition));
|
||||
var horizontal = normal && angleBetweenVectorsInPlane(normal, forward, horizontalAngleNormal);
|
||||
var vertical = normal && angleBetweenVectorsInPlane(normal, forward, verticalAngleNormal);
|
||||
if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) {
|
||||
|
@ -480,11 +483,11 @@ function populateNearbyUserList(selectData, oldAudioData) {
|
|||
personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null
|
||||
ignore: !!id && Users.getIgnoreStatus(id), // ditto
|
||||
isPresent: true,
|
||||
isReplicated: avatar.isReplicated
|
||||
isReplicated: currentAvatarData.isReplicated
|
||||
};
|
||||
// Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin.
|
||||
Users.requestUsernameFromID(id);
|
||||
if (id) {
|
||||
if (id !== "") {
|
||||
addAvatarNode(id); // No overlay for ourselves
|
||||
avatarsOfInterest[id] = true;
|
||||
} else {
|
||||
|
@ -515,30 +518,63 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) {
|
|||
updateUser(data);
|
||||
}
|
||||
|
||||
function updateAudioLevel(avatarData) {
|
||||
// the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged
|
||||
// But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency
|
||||
// of updating (the latter for efficiency too).
|
||||
var audioLevel = 0.0;
|
||||
var avgAudioLevel = 0.0;
|
||||
|
||||
var data = avatarData.sessionUUID === "" ? myData : ExtendedOverlay.get(avatarData.sessionUUID);
|
||||
|
||||
if (data) {
|
||||
// we will do exponential moving average by taking some the last loudness and averaging
|
||||
data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.audioLoudness);
|
||||
|
||||
// add 1 to insure we don't go log() and hit -infinity. Math.log is
|
||||
// natural log, so to get log base 2, just divide by ln(2).
|
||||
audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2);
|
||||
|
||||
// decay avgAudioLevel
|
||||
avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (data.avgAudioLevel || 0), audioLevel);
|
||||
|
||||
data.avgAudioLevel = avgAudioLevel;
|
||||
data.audioLevel = audioLevel;
|
||||
|
||||
// now scale for the gain. Also, asked to boost the low end, so one simple way is
|
||||
// to take sqrt of the value. Lets try that, see how it feels.
|
||||
avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[avatarData.sessionUUID] || 0.75)));
|
||||
}
|
||||
|
||||
var param = {};
|
||||
var level = [audioLevel, avgAudioLevel];
|
||||
var userId = avatarData.sessionUUID;
|
||||
param[userId] = level;
|
||||
sendToQml({ method: 'updateAudioLevel', params: param });
|
||||
}
|
||||
|
||||
var pingPong = true;
|
||||
function updateOverlays() {
|
||||
var eye = Camera.position;
|
||||
AvatarList.getAvatarIdentifiers().forEach(function (id) {
|
||||
if (!id || !avatarsOfInterest[id]) {
|
||||
|
||||
var avatarData = AvatarList.getPalData().data;
|
||||
|
||||
avatarData.forEach(function (currentAvatarData) {
|
||||
|
||||
if (currentAvatarData.sessionUUID === "" || !avatarsOfInterest[currentAvatarData.sessionUUID]) {
|
||||
return; // don't update ourself, or avatars we're not interested in
|
||||
}
|
||||
var avatar = AvatarList.getAvatar(id);
|
||||
if (!avatar) {
|
||||
return; // will be deleted below if there had been an overlay.
|
||||
}
|
||||
var overlay = ExtendedOverlay.get(id);
|
||||
updateAudioLevel(currentAvatarData);
|
||||
var overlay = ExtendedOverlay.get(currentAvatarData.sessionUUID);
|
||||
if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back.
|
||||
print('Adding non-PAL avatar node', id);
|
||||
overlay = addAvatarNode(id);
|
||||
print('Adding non-PAL avatar node', currentAvatarData.sessionUUID);
|
||||
overlay = addAvatarNode(currentAvatarData.sessionUUID);
|
||||
}
|
||||
var target = avatar.position;
|
||||
|
||||
var target = currentAvatarData.position;
|
||||
var distance = Vec3.distance(target, eye);
|
||||
var offset = 0.2;
|
||||
var offset = currentAvatarData.palOrbOffset;
|
||||
var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position)
|
||||
var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can
|
||||
if (headIndex > 0) {
|
||||
offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2;
|
||||
}
|
||||
|
||||
// move a bit in front, towards the camera
|
||||
target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset));
|
||||
|
@ -548,7 +584,7 @@ function updateOverlays() {
|
|||
|
||||
overlay.ping = pingPong;
|
||||
overlay.editOverlay({
|
||||
color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel),
|
||||
color: color(ExtendedOverlay.isSelected(currentAvatarData.sessionUUID), overlay.hovering, overlay.audioLevel),
|
||||
position: target,
|
||||
dimensions: 0.032 * distance
|
||||
});
|
||||
|
@ -669,72 +705,41 @@ triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Cont
|
|||
triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand));
|
||||
triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand));
|
||||
|
||||
var ui;
|
||||
// Most apps can have people toggle the tablet closed and open again, and the app should remain "open" even while
|
||||
// the tablet is not shown. However, for the pal, we explicitly close the app and return the tablet to it's
|
||||
// home screen (so that the avatar highlighting goes away).
|
||||
function tabletVisibilityChanged() {
|
||||
if (!tablet.tabletShown && onPalScreen) {
|
||||
ContextOverlay.enabled = true;
|
||||
tablet.gotoHomeScreen();
|
||||
if (!ui.tablet.tabletShown && ui.isOpen) {
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
|
||||
var UPDATE_INTERVAL_MS = 100;
|
||||
var updateInterval;
|
||||
function createUpdateInterval() {
|
||||
return Script.setInterval(function () {
|
||||
updateOverlays();
|
||||
}, UPDATE_INTERVAL_MS);
|
||||
}
|
||||
|
||||
var wasOnPalScreen = false;
|
||||
var onPalScreen = false;
|
||||
var PAL_QML_SOURCE = "hifi/Pal.qml";
|
||||
function onTabletButtonClicked() {
|
||||
if (!tablet) {
|
||||
print("Warning in onTabletButtonClicked(): 'tablet' undefined!");
|
||||
return;
|
||||
}
|
||||
if (onPalScreen) {
|
||||
// In Toolbar Mode, `gotoHomeScreen` will close the app window.
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
tablet.loadQMLSource(PAL_QML_SOURCE);
|
||||
}
|
||||
}
|
||||
var hasEventBridge = false;
|
||||
function wireEventBridge(on) {
|
||||
if (on) {
|
||||
if (!hasEventBridge) {
|
||||
tablet.fromQml.connect(fromQml);
|
||||
hasEventBridge = true;
|
||||
}
|
||||
} else {
|
||||
if (hasEventBridge) {
|
||||
tablet.fromQml.disconnect(fromQml);
|
||||
hasEventBridge = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
var previousContextOverlay = ContextOverlay.enabled;
|
||||
var previousRequestsDomainListData = Users.requestsDomainListData;
|
||||
function on() {
|
||||
|
||||
function onTabletScreenChanged(type, url) {
|
||||
wasOnPalScreen = onPalScreen;
|
||||
onPalScreen = (type === "QML" && url === PAL_QML_SOURCE);
|
||||
wireEventBridge(onPalScreen);
|
||||
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
||||
button.editProperties({isActive: onPalScreen});
|
||||
previousContextOverlay = ContextOverlay.enabled;
|
||||
previousRequestsDomainListData = Users.requestsDomainListData
|
||||
ContextOverlay.enabled = false;
|
||||
Users.requestsDomainListData = true;
|
||||
|
||||
if (onPalScreen) {
|
||||
isWired = true;
|
||||
|
||||
ContextOverlay.enabled = false;
|
||||
Users.requestsDomainListData = true;
|
||||
|
||||
audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
|
||||
tablet.tabletShownChanged.connect(tabletVisibilityChanged);
|
||||
Script.update.connect(updateOverlays);
|
||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
|
||||
Users.usernameFromIDReply.connect(usernameFromIDReply);
|
||||
triggerMapping.enable();
|
||||
triggerPressMapping.enable();
|
||||
populateNearbyUserList();
|
||||
} else {
|
||||
off();
|
||||
if (wasOnPalScreen) {
|
||||
ContextOverlay.enabled = true;
|
||||
}
|
||||
}
|
||||
ui.tablet.tabletShownChanged.connect(tabletVisibilityChanged);
|
||||
updateInterval = createUpdateInterval();
|
||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
|
||||
Users.usernameFromIDReply.connect(usernameFromIDReply);
|
||||
triggerMapping.enable();
|
||||
triggerPressMapping.enable();
|
||||
populateNearbyUserList();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -748,8 +753,8 @@ function receiveMessage(channel, messageString, senderID) {
|
|||
var message = JSON.parse(messageString);
|
||||
switch (message.method) {
|
||||
case 'select':
|
||||
if (!onPalScreen) {
|
||||
tablet.loadQMLSource(PAL_QML_SOURCE);
|
||||
if (!ui.isOpen) {
|
||||
ui.open();
|
||||
Script.setTimeout(function () { sendToQml(message); }, 1000);
|
||||
} else {
|
||||
sendToQml(message); // Accepts objects, not just strings.
|
||||
|
@ -778,50 +783,6 @@ function scaleAudio(val) {
|
|||
return audioLevel;
|
||||
}
|
||||
|
||||
function getAudioLevel(id) {
|
||||
// the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged
|
||||
// But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency
|
||||
// of updating (the latter for efficiency too).
|
||||
var avatar = AvatarList.getAvatar(id);
|
||||
var audioLevel = 0.0;
|
||||
var avgAudioLevel = 0.0;
|
||||
var data = id ? ExtendedOverlay.get(id) : myData;
|
||||
if (data) {
|
||||
|
||||
// we will do exponential moving average by taking some the last loudness and averaging
|
||||
data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness);
|
||||
|
||||
// add 1 to insure we don't go log() and hit -infinity. Math.log is
|
||||
// natural log, so to get log base 2, just divide by ln(2).
|
||||
audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2);
|
||||
|
||||
// decay avgAudioLevel
|
||||
avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (data.avgAudioLevel || 0), audioLevel);
|
||||
|
||||
data.avgAudioLevel = avgAudioLevel;
|
||||
data.audioLevel = audioLevel;
|
||||
|
||||
// now scale for the gain. Also, asked to boost the low end, so one simple way is
|
||||
// to take sqrt of the value. Lets try that, see how it feels.
|
||||
avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[id] || 0.75)));
|
||||
}
|
||||
return [audioLevel, avgAudioLevel];
|
||||
}
|
||||
|
||||
function createAudioInterval(interval) {
|
||||
// we will update the audioLevels periodically
|
||||
// TODO: tune for efficiency - expecially with large numbers of avatars
|
||||
return Script.setInterval(function () {
|
||||
var param = {};
|
||||
AvatarList.getAvatarIdentifiers().forEach(function (id) {
|
||||
var level = getAudioLevel(id),
|
||||
userId = id || 0; // qml didn't like an object with null/empty string for a key, so...
|
||||
param[userId] = level;
|
||||
});
|
||||
sendToQml({method: 'updateAudioLevel', params: param});
|
||||
}, interval);
|
||||
}
|
||||
|
||||
function avatarDisconnected(nodeID) {
|
||||
// remove from the pal list
|
||||
sendToQml({method: 'avatarDisconnected', params: [nodeID]});
|
||||
|
@ -829,9 +790,8 @@ function avatarDisconnected(nodeID) {
|
|||
|
||||
function clearLocalQMLDataAndClosePAL() {
|
||||
sendToQml({ method: 'clearLocalQMLData' });
|
||||
if (onPalScreen) {
|
||||
ContextOverlay.enabled = true;
|
||||
tablet.gotoHomeScreen();
|
||||
if (ui.isOpen) {
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -847,20 +807,15 @@ function avatarSessionChanged(avatarID) {
|
|||
sendToQml({ method: 'palIsStale', params: [avatarID, 'avatarSessionChanged'] });
|
||||
}
|
||||
|
||||
|
||||
var button;
|
||||
var buttonName = "PEOPLE";
|
||||
var tablet = null;
|
||||
function startup() {
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
text: buttonName,
|
||||
icon: "icons/tablet-icons/people-i.svg",
|
||||
activeIcon: "icons/tablet-icons/people-a.svg",
|
||||
sortOrder: 7
|
||||
ui = new AppUi({
|
||||
buttonName: "PEOPLE",
|
||||
sortOrder: 7,
|
||||
home: "hifi/Pal.qml",
|
||||
onOpened: on,
|
||||
onClosed: off,
|
||||
onMessage: fromQml
|
||||
});
|
||||
button.clicked.connect(onTabletButtonClicked);
|
||||
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||
Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
|
||||
Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);
|
||||
Messages.subscribe(CHANNEL);
|
||||
|
@ -872,39 +827,25 @@ function startup() {
|
|||
}
|
||||
startup();
|
||||
|
||||
|
||||
var isWired = false;
|
||||
var audioTimer;
|
||||
var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too)
|
||||
function off() {
|
||||
if (isWired) {
|
||||
Script.update.disconnect(updateOverlays);
|
||||
if (ui.isOpen) { // i.e., only when connected
|
||||
if (updateInterval) {
|
||||
Script.clearInterval(updateInterval);
|
||||
}
|
||||
Controller.mousePressEvent.disconnect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
|
||||
tablet.tabletShownChanged.disconnect(tabletVisibilityChanged);
|
||||
ui.tablet.tabletShownChanged.disconnect(tabletVisibilityChanged);
|
||||
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
|
||||
ContextOverlay.enabled = true
|
||||
triggerMapping.disable();
|
||||
triggerPressMapping.disable();
|
||||
Users.requestsDomainListData = false;
|
||||
|
||||
isWired = false;
|
||||
|
||||
if (audioTimer) {
|
||||
Script.clearInterval(audioTimer);
|
||||
}
|
||||
}
|
||||
|
||||
removeOverlays();
|
||||
ContextOverlay.enabled = previousContextOverlay;
|
||||
Users.requestsDomainListData = previousRequestsDomainListData;
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
if (onPalScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onTabletButtonClicked);
|
||||
tablet.removeButton(button);
|
||||
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||
Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL);
|
||||
Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL);
|
||||
Messages.subscribe(CHANNEL);
|
||||
|
|
|
@ -31,8 +31,8 @@ BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) {
|
|||
void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& type) {
|
||||
|
||||
// if the URL doesn't have a scheme, assume it is a local file
|
||||
if (inputUrl.scheme() != "http" && inputUrl.scheme() != "https" && inputUrl.scheme() != "ftp") {
|
||||
inputUrl.setScheme("file");
|
||||
if (inputUrl.scheme() != "http" && inputUrl.scheme() != "https" && inputUrl.scheme() != "ftp" && inputUrl.scheme() != "file") {
|
||||
inputUrl = QUrl::fromLocalFile(inputUrl.toString());
|
||||
}
|
||||
|
||||
qDebug() << "Baking file type: " << type;
|
||||
|
|
Loading…
Reference in a new issue