Merge branch 'master' of github.com:highfidelity/hifi into tablet-imporvments

This commit is contained in:
Dante Ruiz 2018-06-11 09:13:14 -07:00
commit ba86c9753d
116 changed files with 1909 additions and 977 deletions

View file

@ -22,7 +22,7 @@ android {
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
'-DRELEASE_TYPE=' + RELEASE_TYPE,
'-DBUILD_BRANCH=' + BUILD_BRANCH,
'-DSTABLE_BUILD=' + STABLE_BUILD,
'-DDISABLE_QML=OFF',
'-DDISABLE_KTX_CACHE=OFF'
}
@ -128,4 +128,3 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}

View file

@ -64,7 +64,11 @@ public class HomeFragment extends Fragment {
mDomainsView.setLayoutManager(gridLayoutMgr);
mDomainAdapter = new DomainAdapter(getContext(), HifiUtils.getInstance().protocolVersionSignature(), nativeGetLastLocation());
mDomainAdapter.setClickListener((view, position, domain) -> {
new Handler(getActivity().getMainLooper()).postDelayed(() -> mListener.onSelectedDomain(domain.url), 400); // a delay so the ripple effect can be seen
new Handler(getActivity().getMainLooper()).postDelayed(() -> {
if (mListener != null) {
mListener.onSelectedDomain(domain.url);
}
}, 400); // a delay so the ripple effect can be seen
});
mDomainAdapter.setListener(new DomainAdapter.AdapterListener() {
@Override
@ -116,7 +120,9 @@ public class HomeFragment extends Fragment {
if (!urlString.trim().isEmpty()) {
urlString = HifiUtils.getInstance().sanitizeHifiUrl(urlString);
}
mListener.onSelectedDomain(urlString);
if (mListener != null) {
mListener.onSelectedDomain(urlString);
}
return true;
}
return false;

View file

@ -1,10 +1,11 @@
package io.highfidelity.hifiinterface.provider;
import android.util.Log;
import android.util.MutableInt;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import io.highfidelity.hifiinterface.HifiUtils;
import io.highfidelity.hifiinterface.view.DomainAdapter;
@ -47,24 +48,42 @@ public class UserStoryDomainProvider implements DomainProvider {
suggestions = new ArrayList<>();
}
@Override
public synchronized void retrieve(String filterText, DomainCallback domainCallback) {
if (!startedToGetFromAPI) {
startedToGetFromAPI = true;
fillDestinations(filterText, domainCallback);
} else {
filterChoicesByText(filterText, domainCallback);
}
}
private void fillDestinations(String filterText, DomainCallback domainCallback) {
StoriesFilter filter = new StoriesFilter(filterText);
final MutableInt counter = new MutableInt(0);
allStories.clear();
getUserStoryPage(1,
List<UserStory> taggedStories = new ArrayList<>();
Set<String> taggedStoriesIds = new HashSet<>();
getUserStoryPage(1, taggedStories, TAGS_TO_SEARCH,
e -> {
allStories.subList(counter.value, allStories.size()).forEach(userStory -> {
filter.filterOrAdd(userStory);
});
if (domainCallback != null) {
domainCallback.retrieveOk(suggestions); //ended
}
},
a -> {
allStories.forEach(userStory -> {
counter.value++;
filter.filterOrAdd(userStory);
taggedStories.forEach(userStory -> {
taggedStoriesIds.add(userStory.id);
});
allStories.clear();
getUserStoryPage(1, allStories, null,
ex -> {
allStories.forEach(userStory -> {
if (taggedStoriesIds.contains(userStory.id)) {
userStory.tagFound = true;
}
filter.filterOrAdd(userStory);
});
if (domainCallback != null) {
domainCallback.retrieveOk(suggestions); //ended
}
}
);
}
);
}
@ -73,25 +92,22 @@ public class UserStoryDomainProvider implements DomainProvider {
restOfPagesCallback.callback(new Exception("Error accessing url [" + url + "]", t));
}
private void getUserStoryPage(int pageNumber, Callback<Exception> restOfPagesCallback, Callback<Void> firstPageCallback) {
private void getUserStoryPage(int pageNumber, List<UserStory> userStoriesList, String tagsFilter, Callback<Exception> restOfPagesCallback) {
Call<UserStories> userStories = mUserStoryDomainProviderService.getUserStories(
INCLUDE_ACTIONS_FOR_PLACES,
"open",
true,
mProtocol,
TAGS_TO_SEARCH,
tagsFilter,
pageNumber);
Log.d("API-USER-STORY-DOMAINS", "Protocol [" + mProtocol + "] include_actions [" + INCLUDE_ACTIONS_FOR_PLACES + "]");
userStories.enqueue(new retrofit2.Callback<UserStories>() {
@Override
public void onResponse(Call<UserStories> call, Response<UserStories> response) {
UserStories data = response.body();
allStories.addAll(data.user_stories);
userStoriesList.addAll(data.user_stories);
if (data.current_page < data.total_pages && data.current_page <= MAX_PAGES_TO_GET) {
if (pageNumber == 1 && firstPageCallback != null) {
firstPageCallback.callback(null);
}
getUserStoryPage(pageNumber + 1, restOfPagesCallback, null);
getUserStoryPage(pageNumber + 1, userStoriesList, tagsFilter, restOfPagesCallback);
return;
}
restOfPagesCallback.callback(null);
@ -107,12 +123,16 @@ public class UserStoryDomainProvider implements DomainProvider {
private class StoriesFilter {
String[] mWords = new String[]{};
public StoriesFilter(String filterText) {
mWords = filterText.toUpperCase().split("\\s+");
mWords = filterText.trim().toUpperCase().split("\\s+");
if (mWords.length == 1 && (mWords[0] == null || mWords[0].length() <= 0 ) ) {
mWords = null;
}
}
private boolean matches(UserStory story) {
if (mWords.length <= 0) {
return true;
if (mWords == null || mWords.length <= 0) {
// No text filter? So filter by tag
return story.tagFound;
}
for (String word : mWords) {
@ -128,6 +148,9 @@ public class UserStoryDomainProvider implements DomainProvider {
suggestions.add(story.toDomain());
}
/**
* if the story matches this filter criteria it's added into suggestions
* */
public void filterOrAdd(UserStory story) {
if (matches(story)) {
addToSuggestions(story);
@ -144,16 +167,6 @@ public class UserStoryDomainProvider implements DomainProvider {
domainCallback.retrieveOk(suggestions);
}
@Override
public synchronized void retrieve(String filterText, DomainCallback domainCallback) {
if (!startedToGetFromAPI) {
startedToGetFromAPI = true;
fillDestinations(filterText, domainCallback);
} else {
filterChoicesByText(filterText, domainCallback);
}
}
public interface UserStoryDomainProviderService {
@GET("api/v1/user_stories")
Call<UserStories> getUserStories(@Query("include_actions") String includeActions,
@ -166,12 +179,14 @@ public class UserStoryDomainProvider implements DomainProvider {
class UserStory {
public UserStory() {}
String id;
String place_name;
String path;
String thumbnail_url;
String place_id;
String domain_id;
private String searchText;
private boolean tagFound; // Locally used
// New fields? tags, description

View file

@ -54,27 +54,10 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
@Override
public void retrieveOk(List<Domain> domain) {
if (filterText.length() == 0) {
Domain lastVisitedDomain = new Domain(mContext.getString(R.string.your_last_location), mLastLocation, DEFAULT_THUMBNAIL_PLACE);
if (!mLastLocation.isEmpty() && mLastLocation.contains("://")) {
int startIndex = mLastLocation.indexOf("://");
int endIndex = mLastLocation.indexOf("/", startIndex + 3);
String toSearch = mLastLocation.substring(0, endIndex + 1).toLowerCase();
for (Domain d : domain) {
if (d.url.toLowerCase().startsWith(toSearch)) {
lastVisitedDomain.thumbnail = d.thumbnail;
}
}
}
domain.add(0, lastVisitedDomain);
addLastLocation(domain);
}
for (Domain d : domain) {
// we override the default picture added in the server by an android specific version
if (d.thumbnail != null &&
d.thumbnail.endsWith("assets/places/thumbnail-default-place-e5a3f33e773ab699495774990a562f9f7693dc48ef90d8be6985c645a0280f75.png")) {
d.thumbnail = DEFAULT_THUMBNAIL_PLACE;
}
}
overrideDefaultThumbnails(domain);
mDomains = new Domain[domain.size()];
mDomains = domain.toArray(mDomains);
@ -96,6 +79,31 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
});
}
private void overrideDefaultThumbnails(List<Domain> domain) {
for (Domain d : domain) {
// we override the default picture added in the server by an android specific version
if (d.thumbnail != null &&
d.thumbnail.endsWith("assets/places/thumbnail-default-place-e5a3f33e773ab699495774990a562f9f7693dc48ef90d8be6985c645a0280f75.png")) {
d.thumbnail = DEFAULT_THUMBNAIL_PLACE;
}
}
}
private void addLastLocation(List<Domain> domain) {
Domain lastVisitedDomain = new Domain(mContext.getString(R.string.your_last_location), mLastLocation, DEFAULT_THUMBNAIL_PLACE);
if (!mLastLocation.isEmpty() && mLastLocation.contains("://")) {
int startIndex = mLastLocation.indexOf("://");
int endIndex = mLastLocation.indexOf("/", startIndex + 3);
String toSearch = mLastLocation.substring(0, endIndex + 1).toLowerCase();
for (Domain d : domain) {
if (d.url.toLowerCase().startsWith(toSearch)) {
lastVisitedDomain.thumbnail = d.thumbnail;
}
}
}
domain.add(0, lastVisitedDomain);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.domain_view, parent, false);

View file

@ -37,7 +37,7 @@ task clean(type: Delete) {
ext {
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : ''
STABLE_BUILD = project.hasProperty('STABLE_BUILD') ? project.getProperty('STABLE_BUILD') : '0'
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
QT5_DEPS = [
'Qt5Concurrent',
@ -66,19 +66,19 @@ ext {
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = ''
def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
def qtFile='https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt'
def qtVersionId='8QbCma4ryEPgBYn_8kgYgB10IvNx9I1W'
if (Os.isFamily(Os.FAMILY_MAC)) {
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9'
qtVersionId='2gfgoYCggJGyXxKiazaPGsMs1Gn9j4og'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtChecksum='c3e25db64002d0f43cf565e0ef708911'
qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT'
qtVersionId='xKIteC6HO0xrmcWeMmhQcmKyPEsnUrcZ'
}
def packages = [
@ -88,79 +88,84 @@ def packages = [
checksum: qtChecksum,
],
bullet: [
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/bullet-2.88_armv8-libcpp.tgz',
file: 'bullet-2.88_armv8-libcpp.tgz',
versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
checksum: '81642779ccb110f8c7338e8739ac38a0',
],
draco: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/draco_armv8-libcpp.tgz',
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
file: 'draco_armv8-libcpp.tgz',
versionId: '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
],
glad: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/glad_armv8-libcpp.zip',
versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE',
file: 'glad_armv8-libcpp.zip',
versionId: 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
],
glm: [
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/glm-0.9.8.5-patched.tgz',
file: 'glm-0.9.8.5-patched.tgz',
versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ',
checksum: '067b5fe16b220b5b1a1039ba51b062ae',
],
gvr: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/gvrsdk_v1.101.0.tgz',
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY',
file: 'gvrsdk_v1.101.0.tgz',
versionId: 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r',
checksum: '57fd02baa069176ba18597a29b6b4fc7',
],
nvtt: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/nvtt_armv8-libcpp.zip',
versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An',
file: 'nvtt_armv8-libcpp.zip',
versionId: 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO',
checksum: 'eb46d0b683e66987190ed124aabf8910',
sharedLibFolder: 'lib',
includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'],
],
openssl: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/openssl-1.1.0g_armv8.tgz',
versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9',
file: 'openssl-1.1.0g_armv8.tgz',
versionId: 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
checksum: 'cabb681fbccd79594f65fcc266e02f32',
],
polyvox: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/polyvox_armv8-libcpp.tgz',
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3',
checksum: '349ad5b72aaf2749ca95d847e60c5314',
file: 'polyvox_armv8-libcpp.tgz',
versionId: 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92',
checksum: 'dba88b3a098747af4bb169e9eb9af57e',
sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
],
tbb: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF',
file: 'tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB',
checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
],
hifiAC: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/libplugins_libhifiCodec.zip',
versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G',
file: 'libplugins_libhifiCodec.zip',
versionId: 'i31pW.qNbvFOXRxbyiJUxg3sphaFNmZU',
checksum: '9412a8e12c88a4096c1fc843bb9fe52d',
sharedLibFolder: '',
includeLibs: ['libplugins_libhifiCodec.so']
],
etc2comp: [
file: 'etc2comp-patched-armv8-libcpp.tgz',
versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
checksum: '14b02795d774457a33bbc60e00a786bc'
]
]
def scribeLocalFile='scribe' + EXEC_SUFFIX
def scribeFile='https://hifi-public.s3.amazonaws.com/austin/android/scribe_linux_x86_64'
def scribeFile='scribe_linux_x86_64'
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO'
def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G'
if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_osx_x86_64'
scribeFile = 'scribe_osx_x86_64'
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6'
scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_win32_x86_64.exe'
scribeFile = 'scribe_win32_x86_64.exe'
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC'
scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7'
}
def options = [
@ -361,6 +366,7 @@ task verifyOpenSSL(type: Verify) { def p = packages['openssl']; src new File(bas
task verifyPolyvox(type: Verify) { def p = packages['polyvox']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyTBB(type: Verify) { def p = packages['tbb']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyHifiAC(type: Verify) { def p = packages['hifiAC']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyEtc2Comp(type: Verify) { def p = packages['etc2comp']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyDependencyDownloads(dependsOn: downloadDependencies) { }
verifyDependencyDownloads.dependsOn verifyQt
@ -371,6 +377,7 @@ verifyDependencyDownloads.dependsOn verifyOpenSSL
verifyDependencyDownloads.dependsOn verifyPolyvox
verifyDependencyDownloads.dependsOn verifyTBB
verifyDependencyDownloads.dependsOn verifyHifiAC
verifyDependencyDownloads.dependsOn verifyEtc2Comp
task extractDependencies(dependsOn: verifyDependencyDownloads) {
doLast {
@ -535,7 +542,7 @@ task cleanDependencies(type: Delete) {
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above
/*
// FIXME derive the path from the gradle environment
@ -608,4 +615,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') {
}
}
}
*/
*/

View file

@ -233,7 +233,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
_argc(0),
_argv(NULL),
_parsedArgV(NULL),
_httpManager(NULL),
_statusPort(0),
_packetsPerClientPerInterval(10),
_packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL),
@ -285,7 +284,7 @@ void OctreeServer::initHTTPManager(int port) {
QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath());
// setup an httpManager with us as the request handler and the parent
_httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this);
_httpManager.reset(new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this));
}
bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {

View file

@ -183,7 +183,7 @@ protected:
bool _isShuttingDown = false;
HTTPManager* _httpManager;
std::unique_ptr<HTTPManager> _httpManager;
int _statusPort;
QString _statusHost;

View file

@ -17,8 +17,8 @@ if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://static.oculus.com/sdk-downloads/1.11.0/Public/1486063832/ovr_sdk_win_1.11.0_public.zip
URL_MD5 ea484403757cbfdfa743b6577fb1f9d2
URL http://hifi-public.s3.amazonaws.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
LOG_DOWNLOAD 1

View file

@ -4,7 +4,7 @@ project(LibOVR)
include_directories(LibOVR/Include LibOVR/Src)
file(GLOB HEADER_FILES LibOVR/Include/*.h)
file(GLOB EXTRA_HEADER_FILES LibOVR/Include/Extras/*.h)
file(GLOB_RECURSE SOURCE_FILES LibOVR/Src/*.c LibOVR/Src/*.cpp)
file(GLOB_RECURSE SOURCE_FILES LibOVR/Shim/*.c LibOVR/Shim/*.cpp)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DOVR_BUILD_DEBUG")
add_library(LibOVR STATIC ${SOURCE_FILES} ${HEADER_FILES} ${EXTRA_HEADER_FILES})
set_target_properties(LibOVR PROPERTIES DEBUG_POSTFIX "d")

55
cmake/externals/etc2comp/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,55 @@
set(EXTERNAL_NAME etc2comp)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
if (APPLE)
set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++)
endif ()
include(ExternalProject)
# We use a patched version of etc2comp that properly generates all the necessary mips
# See https://github.com/google/etc2comp/pull/29
# We also use part of https://github.com/google/etc2comp/pull/1, which fixes a bug
# that would override CMAKE_CXX_FLAGS
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip
URL_MD5 4c96153eb179acbe619e3d99d3330595
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS}
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp debug library")
# use generator expression to ensure the correct library is found when building different configurations in VS
set(_LIB_FOLDER "$<$<CONFIG:RelWithDebInfo>:build/EtcLib/RelWithDebInfo>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<CONFIG:MinSizeRel>:build/EtcLib/MinSizeRel>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<OR:$<CONFIG:Release>,$<CONFIG:Debug>>:build/EtcLib/Release>")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp release library")
elseif (APPLE)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/libEtcLib.a CACHE FILEPATH "Path to EtcLib debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/Release/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library")
else ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to EtcLib debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library")
endif ()
set(ETC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/Etc CACHE FILEPATH "Path to Etc2Comp/Etc include directory")
set(ETCCODEC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/EtcCodec CACHE FILEPATH "Path to Etc2Comp/EtcCodec include directory")
# ETC2COMP_INCLUDE_DIRS will be set later by FindEtc2Comp

View file

@ -17,14 +17,12 @@ macro(SET_PACKAGING_PARAMETERS)
set(DEV_BUILD 0)
set(BUILD_GLOBAL_SERVICES "DEVELOPMENT")
set(USE_STABLE_GLOBAL_SERVICES 0)
set(BUILD_NUMBER 0)
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
set_from_env(BUILD_BRANCH BRANCH "")
string(TOLOWER "${BUILD_BRANCH}" BUILD_BRANCH)
set_from_env(STABLE_BUILD STABLE_BUILD 0)
message(STATUS "The BUILD_BRANCH variable is: ${BUILD_BRANCH}")
message(STATUS "The BRANCH environment variable is: $ENV{BRANCH}")
message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}")
# setup component categories for installer
@ -46,17 +44,17 @@ macro(SET_PACKAGING_PARAMETERS)
# if the build is a PRODUCTION_BUILD from the "stable" branch
# then use the STABLE gobal services
if (BUILD_BRANCH STREQUAL "stable")
message(STATUS "The RELEASE_TYPE is PRODUCTION and the BUILD_BRANCH is stable...")
if (STABLE_BUILD)
message(STATUS "The RELEASE_TYPE is PRODUCTION and STABLE_BUILD is 1")
set(BUILD_GLOBAL_SERVICES "STABLE")
set(USE_STABLE_GLOBAL_SERVICES 1)
endif()
endif ()
elseif (RELEASE_TYPE STREQUAL "PR")
set(DEPLOY_PACKAGE TRUE)
set(PR_BUILD 1)
set(BUILD_VERSION "PR${RELEASE_NUMBER}")
set(BUILD_ORGANIZATION "High Fidelity - ${BUILD_VERSION}")
set(BUILD_ORGANIZATION "High Fidelity - PR${RELEASE_NUMBER}")
set(INTERFACE_BUNDLE_NAME "Interface")
set(INTERFACE_ICON_PREFIX "interface-beta")
@ -75,6 +73,54 @@ macro(SET_PACKAGING_PARAMETERS)
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
# DEV_BUILD and PR_BUILD must be 0
if (STABLE_BUILD)
if ((NOT PRODUCTION_BUILD) OR PR_BUILD OR DEV_BUILD)
message(FATAL_ERROR "Cannot produce STABLE_BUILD without PRODUCTION_BUILD")
endif ()
endif ()
if ((PRODUCTION_BUILD OR PR_BUILD) AND NOT STABLE_BUILD)
# append the abbreviated commit SHA to the build version
# since this is a PR build or master/nightly builds
# for PR_BUILDS, we need to grab the abbreviated SHA
# for the second parent of HEAD (not HEAD) since that is the
# SHA of the commit merged to master for the build
if (PR_BUILD)
set(_GIT_LOG_FORMAT "%p")
else ()
set(_GIT_LOG_FORMAT "%h")
endif ()
execute_process(
COMMAND git log -1 --format=${_GIT_LOG_FORMAT}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE _GIT_LOG_OUTPUT
ERROR_VARIABLE _GIT_LOG_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (PR_BUILD)
separate_arguments(_COMMIT_PARENTS UNIX_COMMAND ${_GIT_LOG_OUTPUT})
list(GET _COMMIT_PARENTS 1 GIT_COMMIT_HASH)
else ()
set(GIT_COMMIT_HASH ${_GIT_LOG_OUTPUT})
endif ()
if (_GIT_LOG_ERROR OR NOT GIT_COMMIT_HASH)
message(FATAL_ERROR "Could not retreive abbreviated SHA for PR or production master build")
endif ()
set(BUILD_VERSION_NO_SHA ${BUILD_VERSION})
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_COMMIT_HASH}")
# pass along a release number without the SHA in case somebody
# wants to compare master or PR builds as integers
set(BUILD_NUMBER ${RELEASE_NUMBER})
endif ()
if (DEPLOY_PACKAGE)
# for deployed packages always grab the serverless content
set(DOWNLOAD_SERVERLESS_CONTENT ON)
@ -127,8 +173,8 @@ macro(SET_PACKAGING_PARAMETERS)
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface")
set(CONSOLE_SHORTCUT_NAME "Sandbox")
else ()
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface - ${BUILD_VERSION}")
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION}")
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface - ${BUILD_VERSION_NO_SHA}")
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}")
endif ()
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")

View file

@ -0,0 +1,22 @@
#
# Copyright 2018 High Fidelity, Inc.
# Created by Sam Gondelman on 5/2/2018
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_ETC2COMP)
if (ANDROID)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/etc2comp)
set(ETC2COMP_INCLUDE_DIRS "${INSTALL_DIR}/include/Etc" "${INSTALL_DIR}/include/EtcCodec")
set(ETC2COMP_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libEtcLib.a)
set(ETC2COMP_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libEtcLib.a)
select_library_configurations(ETC2COMP)
else()
add_dependency_external_projects(etc2comp)
find_package(Etc2Comp REQUIRED)
endif()
target_include_directories(${TARGET_NAME} PRIVATE ${ETC2COMP_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ETC2COMP_LIBRARIES})
endmacro()

View file

@ -0,0 +1,37 @@
#
# FindEtc2Comp.cmake
#
# Try to find the Etc2Comp compression library.
#
# Once done this will define
#
# ETC2COMP_FOUND - system found Etc2Comp
# ETC2COMP_INCLUDE_DIRS - the Etc2Comp include directory
# ETC2COMP_LIBRARIES - link to this to use Etc2Comp
#
# Created on 5/2/2018 by Sam Gondelman
# Copyright 2018 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("etc2comp")
find_path(ETC_INCLUDE_DIR NAMES Etc.h HINTS ${ETC2COMP_SEARCH_DIRS})
find_path(ETCCODEC_INCLUDE_DIR NAMES EtcBlock4x4.h HINTS ${ETC2COMP_SEARCH_DIRS})
set(ETC2COMP_INCLUDE_DIRS "${ETC_INCLUDE_DIR}" "${ETCCODEC_INCLUDE_DIR}")
find_library(ETC2COMP_LIBRARY_DEBUG NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Debug HINTS ${ETC2COMP_SEARCH_DIRS})
find_library(ETC2COMP_LIBRARY_RELEASE NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Release EtcLib HINTS ${ETC2COMP_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(ETC2COMP)
set(ETC2COMP_LIBRARIES ${ETC2COMP_LIBRARY})
find_package_handle_standard_args(ETC2COMP "Could NOT find ETC2COMP, try to set the path to ETC2COMP root folder in the system variable ETC2COMP_ROOT_DIR or create a directory etc2comp in HIFI_LIB_DIR and paste the necessary files there"
ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES)
mark_as_advanced(ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES ETC2COMP_SEARCH_DIRS)

View file

@ -24,8 +24,26 @@ namespace BuildInfo {
const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@";
const QString ORGANIZATION_DOMAIN = "highfidelity.io";
const QString VERSION = "@BUILD_VERSION@";
const QString BUILD_BRANCH = "@BUILD_BRANCH@";
const QString BUILD_NUMBER = "@BUILD_NUMBER@";
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
const QString BUILD_TIME = "@BUILD_TIME@";
}
enum BuildType {
Dev,
PR,
Master,
Stable
};
#if defined(PR_BUILD)
const BuildType BUILD_TYPE = PR;
const QString BUILD_TYPE_STRING = "pr";
#elif defined(PRODUCTION_BUILD)
const BuildType BUILD_TYPE = @STABLE_BUILD@ ? Stable : Master;
const QString BUILD_TYPE_STRING = @STABLE_BUILD@ ? "stable" : "master";
#else
const BuildType BUILD_TYPE = Dev;
const QString BUILD_TYPE_STRING = "dev";
#endif
}

View file

@ -1,4 +1,5 @@
{
"releaseType": "@RELEASE_TYPE@",
"buildIdentifier": "@BUILD_VERSION@"
"buildIdentifier": "@BUILD_VERSION@",
"organization": "@BUILD_ORGANIZATION@"
}

View file

@ -16,6 +16,8 @@
#include <openssl/x509.h>
#include <random>
#include <QDataStream>
#include <AccountManager.h>
#include <Assignment.h>

View file

@ -149,7 +149,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_gatekeeper(this),
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
_httpsManager(NULL),
_allAssignments(),
_unfulfilledAssignments(),
_isUsingDTLS(false),
@ -177,7 +176,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qDebug() << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
qDebug() << "[VERSION] MODIFIED_ORGANIZATION:" << BuildInfo::MODIFIED_ORGANIZATION;
qDebug() << "[VERSION] VERSION:" << BuildInfo::VERSION;
qDebug() << "[VERSION] BUILD_BRANCH:" << BuildInfo::BUILD_BRANCH;
qDebug() << "[VERSION] BUILD_TYPE_STRING:" << BuildInfo::BUILD_TYPE_STRING;
qDebug() << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES;
qDebug() << "[VERSION] We will be using this name to find ICE servers:" << _iceServerAddr;
@ -385,6 +384,8 @@ DomainServer::~DomainServer() {
_contentManager->terminate();
}
DependencyManager::destroy<AccountManager>();
// cleanup the AssetClient thread
DependencyManager::destroy<AssetClient>();
_assetClientThread.quit();
@ -439,7 +440,7 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
QSslCertificate sslCertificate(&certFile);
QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8());
_httpsManager = new HTTPSManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this, this);
_httpsManager.reset(new HTTPSManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this));
qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT;
@ -1121,7 +1122,7 @@ void DomainServer::handleConnectedNode(SharedNodePointer newNode) {
}
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) {
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID +
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID +
NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID + 4;
// setup the extended header for the domain list packets
@ -2683,7 +2684,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
QString settingsPassword = settingsPasswordVariant.isValid() ? settingsPasswordVariant.toString() : "";
QString hexHeaderPassword = headerPassword.isEmpty() ?
"" : QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) {
return true;
}

View file

@ -220,7 +220,7 @@ private:
DomainGatekeeper _gatekeeper;
HTTPManager _httpManager;
HTTPSManager* _httpsManager;
std::unique_ptr<HTTPSManager> _httpsManager;
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;

View file

@ -13,6 +13,7 @@
#include <openssl/x509.h>
#include <QtCore/QDataStream>
#include <QtCore/QJsonDocument>
#include <QtCore/QTimer>
#include <QtNetwork/QNetworkReply>

View file

@ -133,13 +133,7 @@ if (APPLE)
# set where in the bundle to put the resources file
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(DISCOVERED_RESOURCES "")
# use the add_resources_to_os_x_bundle macro to recurse into resources
add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources")
# append the discovered resources to our list of interface sources
list(APPEND INTERFACE_SRCS ${DISCOVERED_RESOURCES})
list(APPEND INTERFACE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME})
endif()
@ -316,18 +310,27 @@ if (APPLE)
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources")
set(RESOURCES_DEV_DIR "$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources")
# copy script files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
# copy script files beside the executable
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/scripts"
"${RESOURCES_DEV_DIR}/scripts"
)
# copy JSDoc files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
"${CMAKE_SOURCE_DIR}/scripts"
"${RESOURCES_DEV_DIR}/scripts"
# copy JSDoc files beside the executable
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
"${RESOURCES_DEV_DIR}/jsdoc"
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
"${RESOURCES_DEV_DIR}/jsdoc"
# copy the resources files beside the executable
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${RESOURCES_RCC}"
"${RESOURCES_DEV_DIR}"
# FIXME, the edit script code loads HTML from the scripts folder
# which in turn relies on CSS that refers to the fonts. In theory
# we should be able to modify the CSS to reference the QRC path to
# the ttf files, but doing so generates a CORS policy violation,
# so we have to retain a copy of the fonts outside of the resources binary
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${PROJECT_SOURCE_DIR}/resources/fonts"
"${RESOURCES_DEV_DIR}/fonts"
)
# call the fixup_interface macro to add required bundling commands for installation
@ -356,13 +359,10 @@ else()
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json"
"${RESOURCES_DEV_DIR}/serverless/tutorial.json"
)
# copy JSDoc files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
# copy JSDoc files beside the executable
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
"${INTERFACE_EXEC_DIR}/jsdoc"
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
"${INTERFACE_EXEC_DIR}/jsdoc"
)
# link target to external libraries

View file

@ -2,88 +2,88 @@ name = mannequin
type = body+head
scale = 1
filename = mannequin/mannequin.baked.fbx
joint = jointRoot = Hips
joint = jointNeck = Neck
joint = jointLean = Spine
joint = jointLeftHand = LeftHand
joint = jointHead = Head
joint = jointEyeLeft = LeftEye
joint = jointEyeRight = RightEye
joint = jointRoot = Hips
joint = jointLeftHand = LeftHand
joint = jointRightHand = RightHand
joint = jointNeck = Neck
joint = jointHead = Head
freeJoint = LeftArm
freeJoint = LeftForeArm
freeJoint = RightArm
freeJoint = RightForeArm
bs = EyeBlink_L = blink = 1
bs = JawOpen = mouth_Open = 1
bs = LipsFunnel = Oo = 1
bs = BrowsU_L = brow_Up = 1
jointIndex = RightHandPinky2 = 19
jointIndex = LeftHandMiddle4 = 61
jointIndex = LeftHand = 41
jointIndex = LeftHandRing4 = 49
jointIndex = RightHandMiddle3 = 36
jointIndex = LeftHandThumb4 = 57
jointIndex = RightToe_End = 10
jointIndex = LeftHandRing1 = 46
jointIndex = LeftForeArm = 40
jointIndex = RightHandIndex4 = 29
jointIndex = LeftShoulder = 38
jointIndex = RightHandMiddle4 = 37
jointIndex = RightShoulder = 14
jointIndex = LeftLeg = 2
jointIndex = LeftToe_End = 5
jointIndex = Hips = 0
jointIndex = RightFoot = 8
jointIndex = RightHandThumb2 = 31
jointIndex = LeftHandMiddle3 = 60
jointIndex = RightHandThumb1 = 30
jointIndex = Neck = 62
jointIndex = Spine = 11
jointIndex = RightHandThumb4 = 33
jointIndex = RightHandMiddle1 = 34
jointIndex = LeftHandIndex4 = 53
jointIndex = face = 68
jointIndex = RightHandRing3 = 24
jointIndex = LeftHandPinky4 = 45
jointIndex = LeftHandMiddle2 = 59
jointIndex = RightHandThumb3 = 32
bs = EyeBlink_L = blink = 1
jointIndex = LeftHandPinky3 = 44
jointIndex = HeadTop_End = 66
jointIndex = Spine1 = 12
jointIndex = LeftHandRing3 = 48
jointIndex = mannequin1 = 67
jointIndex = RightEye = 65
jointIndex = RightHandRing4 = 25
jointIndex = RightHandPinky4 = 21
jointIndex = LeftHandRing2 = 47
jointIndex = RightHandIndex3 = 28
jointIndex = RightUpLeg = 6
jointIndex = LeftArm = 39
jointIndex = LeftHandThumb3 = 56
jointIndex = RightHandIndex2 = 27
jointIndex = RightForeArm = 16
jointIndex = RightArm = 15
jointIndex = RightHandRing2 = 23
jointIndex = LeftHandMiddle1 = 58
jointIndex = Spine2 = 13
jointIndex = LeftHandThumb2 = 55
jointIndex = RightHandMiddle2 = 35
jointIndex = RightHandPinky1 = 18
jointIndex = LeftUpLeg = 1
jointIndex = RightLeg = 7
jointIndex = LeftHandIndex2 = 51
jointIndex = LeftHand = 41
jointIndex = RightHandMiddle1 = 34
jointIndex = LeftHandPinky4 = 45
jointIndex = RightHand = 17
jointIndex = LeftHandIndex3 = 52
jointIndex = LeftFoot = 3
jointIndex = RightHandPinky3 = 20
jointIndex = RightHandIndex1 = 26
jointIndex = LeftHandPinky1 = 42
jointIndex = RightToeBase = 9
jointIndex = LeftHandIndex1 = 50
jointIndex = LeftToeBase = 4
jointIndex = LeftHandPinky2 = 43
jointIndex = RightHandRing1 = 22
jointIndex = LeftHandThumb1 = 54
jointIndex = LeftEye = 64
jointIndex = LeftFoot = 3
jointIndex = Head = 63
jointIndex = Spine1 = 12
jointIndex = RightHandRing4 = 25
jointIndex = RightHandPinky1 = 18
jointIndex = LeftHandIndex1 = 50
jointIndex = RightHandIndex3 = 28
jointIndex = LeftHandIndex3 = 52
jointIndex = LeftToe_End = 5
jointIndex = RightArm = 15
jointIndex = RightHandRing3 = 24
jointIndex = RightHandThumb2 = 31
jointIndex = Spine2 = 13
jointIndex = HeadTop_End = 66
jointIndex = LeftToeBase = 4
jointIndex = RightUpLeg = 6
jointIndex = RightForeArm = 16
jointIndex = LeftHandMiddle1 = 58
jointIndex = LeftHandRing3 = 48
jointIndex = RightHandPinky4 = 21
jointIndex = RightHandIndex1 = 26
jointIndex = Hips = 0
jointIndex = RightEye = 65
jointIndex = RightHandPinky2 = 19
jointIndex = LeftHandMiddle2 = 59
jointIndex = LeftHandPinky1 = 42
jointIndex = LeftHandRing4 = 49
jointIndex = RightFoot = 8
jointIndex = RightHandIndex2 = 27
jointIndex = RightToe_End = 10
jointIndex = RightHandThumb3 = 32
jointIndex = LeftHandMiddle3 = 60
jointIndex = LeftHandThumb4 = 57
jointIndex = LeftHandMiddle4 = 61
jointIndex = LeftHandThumb1 = 54
jointIndex = LeftHandThumb3 = 56
jointIndex = body = 67
jointIndex = LeftArm = 39
jointIndex = RightToeBase = 9
jointIndex = LeftEye = 64
jointIndex = RightLeg = 7
jointIndex = face = 68
jointIndex = LeftForeArm = 40
jointIndex = RightHandThumb4 = 33
jointIndex = RightHandRing1 = 22
jointIndex = LeftUpLeg = 1
jointIndex = LeftHandPinky2 = 43
jointIndex = LeftLeg = 2
jointIndex = LeftHandIndex4 = 53
jointIndex = RightHandThumb1 = 30
jointIndex = LeftHandRing2 = 47
jointIndex = RightHandMiddle2 = 35
jointIndex = RightHandMiddle3 = 36
jointIndex = Spine = 11
jointIndex = RightHandMiddle4 = 37
jointIndex = LeftHandIndex2 = 51
jointIndex = RightHandRing2 = 23
jointIndex = LeftHandThumb2 = 55
jointIndex = LeftShoulder = 38
jointIndex = Neck = 62
jointIndex = RightHandIndex4 = 29
jointIndex = LeftHandRing1 = 46
jointIndex = RightShoulder = 14

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,002 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 KiB

View file

@ -331,6 +331,7 @@ ModalWindow {
}
onFolderChanged: {
d.clearSelection();
fileTableModel.update(); // Update once the data from the folder change is available.
}
@ -450,7 +451,7 @@ ModalWindow {
rows = 0,
i;
var newFilesModel = filesModelBuilder.createObject(root);
filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
@ -472,7 +473,7 @@ ModalWindow {
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
@ -481,7 +482,7 @@ ModalWindow {
}
}
newFilesModel.insert(lower, {
filesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
@ -492,9 +493,6 @@ ModalWindow {
rows++;
}
filesModel = newFilesModel;
d.clearSelection();
}
}

View file

@ -332,6 +332,7 @@ ModalWindow {
}
onFolderChanged: {
d.clearSelection();
fileTableModel.update(); // Update once the data from the folder change is available.
}
@ -451,7 +452,7 @@ ModalWindow {
rows = 0,
i;
var newFilesModel = filesModelBuilder.createObject(root);
filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
@ -473,7 +474,7 @@ ModalWindow {
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
@ -482,7 +483,7 @@ ModalWindow {
}
}
newFilesModel.insert(lower, {
filesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
@ -493,9 +494,6 @@ ModalWindow {
rows++;
}
filesModel = newFilesModel;
d.clearSelection();
}
}

View file

@ -295,7 +295,8 @@ TabletModalWindow {
}
onFolderChanged: {
fileTableModel.update()
d.clearSelection();
fileTableModel.update();
}
function getItem(index, field) {
@ -413,7 +414,7 @@ TabletModalWindow {
rows = 0,
i;
var newFilesModel = filesModelBuilder.createObject(root);
filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
@ -435,7 +436,7 @@ TabletModalWindow {
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
@ -444,7 +445,7 @@ TabletModalWindow {
}
}
newFilesModel.insert(lower, {
filesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
@ -455,9 +456,6 @@ TabletModalWindow {
rows++;
}
filesModel = newFilesModel;
d.clearSelection();
}
}

View file

@ -319,10 +319,10 @@ Item {
visible: thisNameCard.userName !== "";
// Size
width: parent.width
height: usernameTextPixelSize + 4
height: paintedHeight
// Anchors
anchors.top: isMyCard ? myDisplayName.bottom : pal.activeTab == "nearbyTab" ? displayNameContainer.bottom : undefined //(parent.height - displayNameTextPixelSize/2));
anchors.verticalCenter: pal.activeTab == "connectionsTab" && !isMyCard ? avatarImage.verticalCenter : undefined
anchors.top: isMyCard ? myDisplayName.bottom : pal.activeTab == "nearbyTab" ? displayNameContainer.bottom : avatarImage.top //(parent.height - displayNameTextPixelSize/2));
anchors.bottom: pal.activeTab === "connectionsTab" && !isMyCard ? avatarImage.bottom : undefined
anchors.left: avatarImage.right;
anchors.leftMargin: avatarImage.visible ? 5 : 0;
anchors.rightMargin: 5;

View file

@ -163,7 +163,6 @@ Item {
Rectangle {
id: contextCard;
z: 2;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.top: parent.top;
@ -337,7 +336,6 @@ Item {
Rectangle {
id: permissionExplanationCard;
z: 1;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.top: parent.top;
@ -596,8 +594,8 @@ Item {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
contextCard.z = 1;
permissionExplanationCard.z = 0;
contextCard.visible = true;
permissionExplanationCard.visible = false;
root.sendToPurchases({ method: 'flipCard' });
}
onEntered: {
@ -779,8 +777,8 @@ Item {
noPermissionGlyph.color = hifi.colors.redAccent;
}
onClicked: {
contextCard.z = 0;
permissionExplanationCard.z = 1;
contextCard.visible = false;
permissionExplanationCard.visible = true;
root.sendToPurchases({ method: 'flipCard' });
}
}

View file

@ -8,12 +8,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick 2.7
import Qt.labs.folderlistmodel 2.1
import Qt.labs.settings 1.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import QtQuick.Controls 1.4 as QQC1
import QtQuick.Controls 2.3
import ".."
import "../../../controls-uit"
@ -30,6 +30,8 @@ Rectangle {
color: hifi.colors.baseGray;
property var filesModel: ListModel { }
Settings {
category: "FileDialog"
property alias width: root.width
@ -149,7 +151,7 @@ Rectangle {
ComboBox {
id: pathSelector
anchors {
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: navControls.right
@ -247,7 +249,9 @@ Rectangle {
}
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
currentSelectionIsFolder = fileTableView.model.isFolder(row);
currentSelectionIsFolder = fileTableView.model !== filesModel ?
fileTableView.model.isFolder(row) :
fileTableModel.isFolder(row);
if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
} else {
@ -287,6 +291,7 @@ Rectangle {
}
onFolderChanged: {
d.clearSelection();
fileTableModel.update(); // Update once the data from the folder change is available.
}
@ -327,7 +332,12 @@ Rectangle {
}
}
ListModel {
Component {
id: filesModelBuilder
ListModel { }
}
QtObject {
id: fileTableModel
// FolderListModel has a couple of problems:
@ -379,7 +389,11 @@ Rectangle {
if (row === -1) {
return false;
}
return get(row).fileIsDir;
return filesModel.get(row).fileIsDir;
}
function get(row) {
return filesModel.get(row)
}
function update() {
@ -397,7 +411,7 @@ Rectangle {
rows = 0,
i;
clear();
filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
@ -419,7 +433,7 @@ Rectangle {
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, get(middle)[sortField])) {
if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
@ -428,7 +442,7 @@ Rectangle {
}
}
insert(lower, {
filesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
@ -439,8 +453,6 @@ Rectangle {
rows++;
}
d.clearSelection();
}
}
@ -465,12 +477,12 @@ Rectangle {
sortIndicatorOrder: Qt.AscendingOrder
sortIndicatorVisible: true
model: fileTableModel
model: filesModel
function updateSort() {
model.sortOrder = sortIndicatorOrder;
model.sortColumn = sortIndicatorColumn;
model.update();
fileTableModel.sortOrder = sortIndicatorOrder;
fileTableModel.sortColumn = sortIndicatorColumn;
fileTableModel.update();
}
onSortIndicatorColumnChanged: { updateSort(); }
@ -522,7 +534,7 @@ Rectangle {
}
}
TableViewColumn {
QQC1.TableViewColumn {
id: fileNameColumn
role: "fileName"
title: "Name"
@ -530,7 +542,7 @@ Rectangle {
movable: false
resizable: true
}
TableViewColumn {
QQC1.TableViewColumn {
id: fileMofifiedColumn
role: "fileModified"
title: "Date"
@ -539,7 +551,7 @@ Rectangle {
resizable: true
visible: !selectDirectory
}
TableViewColumn {
QQC1.TableViewColumn {
role: "fileSize"
title: "Size"
width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width
@ -554,11 +566,12 @@ Rectangle {
}
function navigateToCurrentRow() {
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
var row = fileTableView.currentRow
var isFolder = model.isFolder(row);
var file = model.get(row).filePath;
var isFolder = currentModel.isFolder(row);
var file = currentModel.get(row).filePath;
if (isFolder) {
fileTableView.model.folder = helper.pathToUrl(file);
currentModel.folder = helper.pathToUrl(file);
} else {
okAction.trigger();
}
@ -573,7 +586,8 @@ Rectangle {
var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) {
var name = model.get(i).fileName.toLowerCase();
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
filesModel.get(i).fileName.toLowerCase();
if (0 === name.indexOf(newPrefix)) {
matchedIndex = i;
break;

View file

@ -15,6 +15,17 @@
#include <QObject>
/**jsdoc
* @namespace HifiAbout
*
* @hifi-interface
* @hifi-client-entity
*
* @property {string} buildDate
* @property {string} buildVersion
* @property {string} qtVersion
*/
class AboutUtil : public QObject {
Q_OBJECT
@ -30,6 +41,11 @@ public:
QString getQtVersion() const;
public slots:
/**jsdoc
* @function HifiAbout.openUrl
* @param {string} url
*/
void openUrl(const QString &url) const;
private:
AboutUtil(QObject* parent = nullptr);

View file

@ -697,8 +697,8 @@ private:
};
/**jsdoc
* <p>The <code>Controller.Hardware.Application</code> object has properties representing Interface's state. The property
* values are integer IDs, uniquely identifying each output. <em>Read-only.</em> These can be mapped to actions or functions or
* <p>The <code>Controller.Hardware.Application</code> object has properties representing Interface's state. The property
* values are integer IDs, uniquely identifying each output. <em>Read-only.</em> These can be mapped to actions or functions or
* <code>Controller.Standard</code> items in a {@link RouteObject} mapping (e.g., using the {@link RouteObject#when} method).
* Each data value is either <code>1.0</code> for "true" or <code>0.0</code> for "false".</p>
* <table>
@ -780,7 +780,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
// Ignore any previous crashes if running from command line with a test script.
// Ignore any previous crashes if running from command line with a test script.
bool inTestMode { false };
for (int i = 0; i < argc; ++i) {
QString parameter(argv[i]);
@ -802,15 +802,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir);
}
// FIXME fix the OSX installer to install the resources.rcc binary instead of resource files and remove
// this conditional exclusion
#if !defined(Q_OS_OSX)
{
#if defined(Q_OS_ANDROID)
const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc";
#else
const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc";
#endif
const QString resourcesBinaryFile = PathUtils::getRccPath();
if (!QFile::exists(resourcesBinaryFile)) {
throw std::runtime_error("Unable to find primary resources");
}
@ -818,7 +811,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
throw std::runtime_error("Unable to load primary resources");
}
}
#endif
// Tell the plugin manager about our statically linked plugins
auto pluginManager = PluginManager::getInstance();
@ -1116,7 +1108,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
qCDebug(interfaceapp) << "[VERSION] MODIFIED_ORGANIZATION:" << BuildInfo::MODIFIED_ORGANIZATION;
qCDebug(interfaceapp) << "[VERSION] VERSION:" << BuildInfo::VERSION;
qCDebug(interfaceapp) << "[VERSION] BUILD_BRANCH:" << BuildInfo::BUILD_BRANCH;
qCDebug(interfaceapp) << "[VERSION] BUILD_TYPE_STRING:" << BuildInfo::BUILD_TYPE_STRING;
qCDebug(interfaceapp) << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES;
#if USE_STABLE_GLOBAL_SERVICES
qCDebug(interfaceapp) << "[VERSION] We will use STABLE global services.";
@ -1373,11 +1365,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
initializeGL();
qCDebug(interfaceapp, "Initialized GL");
// Initialize the display plugin architecture
// Initialize the display plugin architecture
initializeDisplayPlugins();
qCDebug(interfaceapp, "Initialized Display");
// Create the rendering engine. This can be slow on some machines due to lots of
// Create the rendering engine. This can be slow on some machines due to lots of
// GPU pipeline creation.
initializeRenderEngine();
qCDebug(interfaceapp, "Initialized Render Engine.");
@ -1421,7 +1413,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// In practice we shouldn't run across installs that don't have a known installer type.
// Client or Client+Server installs should always have the installer.ini next to their
// respective interface.exe, and Steam installs will be detected as such. If a user were
// to delete the installer.ini, though, and as an example, we won't know the context of the
// to delete the installer.ini, though, and as an example, we won't know the context of the
// original install.
constexpr auto INSTALLER_KEY_TYPE = "type";
constexpr auto INSTALLER_KEY_CAMPAIGN = "campaign";
@ -1469,6 +1461,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
{ "installer_campaign", installerCampaign },
{ "installer_type", installerType },
{ "build_type", BuildInfo::BUILD_TYPE_STRING },
{ "previousSessionCrashed", _previousSessionCrashed },
{ "previousSessionRuntime", sessionRunTime.get() },
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
@ -2186,7 +2179,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
if (testProperty.isValid()) {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
const auto testScript = property(hifi::properties::TEST).toUrl();
// Set last parameter to exit interface when the test script finishes, if so requested
scriptEngines->loadScript(testScript, false, false, false, false, quitWhenFinished);
@ -2403,7 +2396,7 @@ void Application::onAboutToQuit() {
}
}
// The active display plugin needs to be loaded before the menu system is active,
// The active display plugin needs to be loaded before the menu system is active,
// so its persisted explicitly here
Setting::Handle<QString>{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName());
@ -2638,7 +2631,7 @@ void Application::initializeGL() {
// Create the GPU backend
// Requires the window context, because that's what's used in the actual rendering
// and the GPU backend will make things like the VAO which cannot be shared across
// and the GPU backend will make things like the VAO which cannot be shared across
// contexts
_glWidget->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>();
@ -2661,7 +2654,7 @@ void Application::initializeDisplayPlugins() {
auto lastActiveDisplayPluginName = activeDisplayPluginSetting.get();
auto defaultDisplayPlugin = displayPlugins.at(0);
// Once time initialization code
// Once time initialization code
DisplayPluginPointer targetDisplayPlugin;
foreach(auto displayPlugin, displayPlugins) {
displayPlugin->setContext(_gpuContext);
@ -2674,7 +2667,7 @@ void Application::initializeDisplayPlugins() {
}
// The default display plugin needs to be activated first, otherwise the display plugin thread
// may be launched by an external plugin, which is bad
// may be launched by an external plugin, which is bad
setDisplayPlugin(defaultDisplayPlugin);
// Now set the desired plugin if it's not the same as the default plugin
@ -3674,9 +3667,21 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_1: {
Menu* menu = Menu::getInstance();
menu->triggerOption(MenuOption::FirstPerson);
break;
}
case Qt::Key_2: {
Menu* menu = Menu::getInstance();
menu->triggerOption(MenuOption::FullscreenMirror);
break;
}
case Qt::Key_3: {
Menu* menu = Menu::getInstance();
menu->triggerOption(MenuOption::ThirdPerson);
break;
}
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
@ -4187,7 +4192,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
static uint32_t _renderedFrameIndex { INVALID_FRAME };
bool Application::shouldPaint() const {
if (_aboutToQuit) {
if (_aboutToQuit || _window->isMinimized()) {
return false;
}
@ -4747,12 +4752,15 @@ void Application::loadSettings() {
// DONT CHECK IN
//DependencyManager::get<LODManager>()->setAutomaticLODAdjust(false);
Menu::getInstance()->loadSettings();
auto menu = Menu::getInstance();
menu->loadSettings();
// override the menu option show overlays to always be true on startup
menu->setIsOptionChecked(MenuOption::Overlays, true);
// If there is a preferred plugin, we probably messed it up with the menu settings, so fix it.
auto pluginManager = PluginManager::getInstance();
auto plugins = pluginManager->getPreferredDisplayPlugins();
auto menu = Menu::getInstance();
if (plugins.size() > 0) {
for (auto plugin : plugins) {
if (auto action = menu->getActionForOption(plugin->getName())) {
@ -5780,7 +5788,7 @@ void Application::update(float deltaTime) {
viewIsDifferentEnough = true;
}
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
static const std::chrono::seconds MIN_PERIOD_BETWEEN_QUERIES { 3 };
auto now = SteadyClock::now();
@ -6148,7 +6156,9 @@ void Application::updateWindowTitle() const {
auto nodeList = DependencyManager::get<NodeList>();
auto accountManager = DependencyManager::get<AccountManager>();
QString buildVersion = " (build " + applicationVersion() + ")";
QString buildVersion = " - "
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
+ " " + applicationVersion();
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
@ -7709,7 +7719,7 @@ void Application::sendLambdaEvent(const std::function<void()>& f) {
} else {
LambdaEvent event(f);
QCoreApplication::sendEvent(this, &event);
}
}
}
void Application::initPlugins(const QStringList& arguments) {
@ -7932,7 +7942,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
}
// FIXME don't have the application directly set the state of the UI,
// instead emit a signal that the display plugin is changing and let
// instead emit a signal that the display plugin is changing and let
// the desktop lock itself. Reduces coupling between the UI and display
// plugins
auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -8041,7 +8051,6 @@ void Application::switchDisplayMode() {
setActiveDisplayPlugin(DESKTOP_DISPLAY_PLUGIN_NAME);
startHMDStandBySession();
}
emit activeDisplayPluginChanged();
}
_previousHMDWornStatus = currentHMDWornStatus;
}

View file

@ -30,9 +30,6 @@ void Application::editRenderArgs(RenderArgsEditor editor) {
void Application::paintGL() {
// Some plugins process message events, allowing paintGL to be called reentrantly.
if (_aboutToQuit || _window->isMinimized()) {
return;
}
_renderFrameCount++;
_lastTimeRendered.start();

View file

@ -18,6 +18,7 @@
#if HAS_CRASHPAD
#include <mutex>
#include <string>
#include <QStandardPaths>
#include <QDir>
@ -69,6 +70,8 @@ bool startCrashHandler() {
annotations["token"] = BACKTRACE_TOKEN;
annotations["format"] = "minidump";
annotations["version"] = BuildInfo::VERSION.toStdString();
annotations["build_number"] = BuildInfo::BUILD_NUMBER.toStdString();
annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING.toStdString();
arguments.push_back("--no-rate-limit");

View file

@ -217,21 +217,21 @@ Menu::Menu() {
// View > First Person
auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FirstPerson, Qt::Key_1,
viewMenu, MenuOption::FirstPerson, 0,
true, qApp, SLOT(cameraMenuChanged())));
firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Third Person
auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::ThirdPerson, Qt::Key_3,
viewMenu, MenuOption::ThirdPerson, 0,
false, qApp, SLOT(cameraMenuChanged())));
thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Mirror
auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FullscreenMirror, Qt::Key_2,
viewMenu, MenuOption::FullscreenMirror, 0,
false, qApp, SLOT(cameraMenuChanged())));
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
@ -588,6 +588,10 @@ Menu::Menu() {
});
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ToggleHipsFollowing, 0, false,
avatar.get(), SLOT(setToggleHips(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBaseOfSupport, 0, false,
avatar.get(), SLOT(setEnableDebugDrawBaseOfSupport(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false,

View file

@ -30,6 +30,7 @@ namespace MenuOption {
const QString AddressBar = "Show Address Bar";
const QString Animations = "Animations...";
const QString AnimDebugDrawAnimPose = "Debug Draw Animation";
const QString AnimDebugDrawBaseOfSupport = "Debug Draw Base of Support";
const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose";
const QString AnimDebugDrawPosition= "Debug Draw Position";
const QString AskToResetSettings = "Ask To Reset Settings on Start";
@ -202,6 +203,7 @@ namespace MenuOption {
const QString ThirdPerson = "Third Person";
const QString ThreePointCalibration = "3 Point Calibration";
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
const QString ToggleHipsFollowing = "Toggle Hips Following";
const QString ToolWindow = "Tool Window";
const QString TransmitterDrive = "Transmitter Drive";
const QString TurnWithHead = "Turn using Head";

View file

@ -9,11 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include "SecondaryCamera.h"
#include <TextureCache.h>
#include <gpu/Context.h>
#include <glm/gtx/transform.hpp>
#include <gpu/Context.h>
#include <TextureCache.h>
#include "Application.h"
using RenderArgsPointer = std::shared_ptr<RenderArgs>;

View file

@ -52,6 +52,7 @@
#include "MyHead.h"
#include "MySkeletonModel.h"
#include "AnimUtil.h"
#include "Application.h"
#include "AvatarManager.h"
#include "AvatarActionHold.h"
@ -422,12 +423,12 @@ void MyAvatar::update(float deltaTime) {
}
#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE
glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getControllerPoseInAvatarFrame(controller::Pose::HEAD) *
glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y));
DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f));
p = transformPoint(getSensorToWorldMatrix(), getHMDSensorPosition() +
glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y));
DebugDraw::getInstance().addMarker("facing", getOrientation(), p, glm::vec4(1.0f));
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation());
glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y));
glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y));
DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
#endif
if (_goToPending) {
@ -654,8 +655,8 @@ void MyAvatar::simulate(float deltaTime) {
if (success) {
moveOperator.addEntityToMoveList(entity, newCube);
}
// send an edit packet to update the entity-server about the queryAABox. If it's an
// avatar-entity, don't.
// send an edit packet to update the entity-server about the queryAABox
// unless it is client-only
if (packetSender && !entity->getClientOnly()) {
EntityItemProperties properties = entity->getProperties();
properties.setQueryAACubeDirty();
@ -663,6 +664,17 @@ void MyAvatar::simulate(float deltaTime) {
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties);
entity->setLastBroadcast(usecTimestampNow());
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
if (!entityDescendant->getClientOnly() && descendant->updateQueryAACube()) {
EntityItemProperties descendantProperties;
descendantProperties.setQueryAACube(descendant->getQueryAACube());
descendantProperties.setLastEdited(now);
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityDescendant->getID(), descendantProperties);
entityDescendant->setLastBroadcast(now); // for debug/physics status icons
}
});
}
}
});
@ -701,7 +713,8 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorOrientation = glmExtractRotation(hmdSensorMatrix);
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
if (headPose.isValid()) {
_headControllerFacing = getFacingDir2D(headPose.rotation);
glm::quat bodyOrientation = computeBodyFacingFromHead(headPose.rotation, Vectors::UNIT_Y);
_headControllerFacing = getFacingDir2D(bodyOrientation);
} else {
_headControllerFacing = glm::vec2(1.0f, 0.0f);
}
@ -1068,6 +1081,22 @@ float loadSetting(Settings& settings, const QString& name, float defaultValue) {
return value;
}
void MyAvatar::setToggleHips(bool followHead) {
_follow.setToggleHipsFollowing(followHead);
}
void MyAvatar::FollowHelper::setToggleHipsFollowing(bool followHead) {
_toggleHipsFollowing = followHead;
}
bool MyAvatar::FollowHelper::getToggleHipsFollowing() const {
return _toggleHipsFollowing;
}
void MyAvatar::setEnableDebugDrawBaseOfSupport(bool isEnabled) {
_enableDebugDrawBaseOfSupport = isEnabled;
}
void MyAvatar::setEnableDebugDrawDefaultPose(bool isEnabled) {
_enableDebugDrawDefaultPose = isEnabled;
@ -1199,6 +1228,8 @@ void MyAvatar::loadData() {
settings.endGroup();
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
_follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing));
setEnableDebugDrawBaseOfSupport(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBaseOfSupport));
setEnableDebugDrawDefaultPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawDefaultPose));
setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose));
setEnableDebugDrawPosition(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawPosition));
@ -2808,6 +2839,7 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
if (headPose.isValid()) {
headPosition = headPose.translation;
// AJT: TODO: can remove this Y_180
headOrientation = headPose.rotation * Quaternions::Y_180;
}
const glm::quat headOrientationYawOnly = cancelOutRollAndPitch(headOrientation);
@ -2830,6 +2862,8 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
// eyeToNeck offset is relative full HMD orientation.
// while neckToRoot offset is only relative to HMDs yaw.
// Y_180 is necessary because rig is z forward and hmdOrientation is -z forward
// AJT: TODO: can remove this Y_180, if we remove the higher level one.
glm::vec3 headToNeck = headOrientation * Quaternions::Y_180 * (localNeck - localHead);
glm::vec3 neckToRoot = headOrientationYawOnly * Quaternions::Y_180 * -localNeck;
@ -2839,6 +2873,202 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos);
}
// ease in function for dampening cg movement
static float slope(float num) {
const float CURVE_CONSTANT = 1.0f;
float ret = 1.0f;
if (num > 0.0f) {
ret = 1.0f - (1.0f / (1.0f + CURVE_CONSTANT * num));
}
return ret;
}
// This function gives a soft clamp at the edge of the base of support
// dampenCgMovement returns the damped cg value in Avatar space.
// cgUnderHeadHandsAvatarSpace is also in Avatar space
// baseOfSupportScale is based on the height of the user
static glm::vec3 dampenCgMovement(glm::vec3 cgUnderHeadHandsAvatarSpace, float baseOfSupportScale) {
float distanceFromCenterZ = cgUnderHeadHandsAvatarSpace.z;
float distanceFromCenterX = cgUnderHeadHandsAvatarSpace.x;
// In the forward direction we need a different scale because forward is in
// the direction of the hip extensor joint, which means bending usually happens
// well before reaching the edge of the base of support.
const float clampFront = DEFAULT_AVATAR_SUPPORT_BASE_FRONT * DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR * baseOfSupportScale;
float clampBack = DEFAULT_AVATAR_SUPPORT_BASE_BACK * DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR * baseOfSupportScale;
float clampLeft = DEFAULT_AVATAR_SUPPORT_BASE_LEFT * DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR * baseOfSupportScale;
float clampRight = DEFAULT_AVATAR_SUPPORT_BASE_RIGHT * DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR * baseOfSupportScale;
glm::vec3 dampedCg(0.0f, 0.0f, 0.0f);
// find the damped z coord of the cg
if (cgUnderHeadHandsAvatarSpace.z < 0.0f) {
// forward displacement
dampedCg.z = slope(fabs(distanceFromCenterZ / clampFront)) * clampFront;
} else {
// backwards displacement
dampedCg.z = slope(fabs(distanceFromCenterZ / clampBack)) * clampBack;
}
// find the damped x coord of the cg
if (cgUnderHeadHandsAvatarSpace.x > 0.0f) {
// right of center
dampedCg.x = slope(fabs(distanceFromCenterX / clampRight)) * clampRight;
} else {
// left of center
dampedCg.x = slope(fabs(distanceFromCenterX / clampLeft)) * clampLeft;
}
return dampedCg;
}
// computeCounterBalance returns the center of gravity in Avatar space
glm::vec3 MyAvatar::computeCounterBalance() const {
struct JointMass {
QString name;
float weight;
glm::vec3 position;
JointMass() {};
JointMass(QString n, float w, glm::vec3 p) {
name = n;
weight = w;
position = p;
}
};
// init the body part weights
JointMass cgHeadMass(QString("Head"), DEFAULT_AVATAR_HEAD_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
JointMass cgLeftHandMass(QString("LeftHand"), DEFAULT_AVATAR_LEFTHAND_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
JointMass cgRightHandMass(QString("RightHand"), DEFAULT_AVATAR_RIGHTHAND_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
glm::vec3 tposeHead = DEFAULT_AVATAR_HEAD_POS;
glm::vec3 tposeHips = glm::vec3(0.0f, 0.0f, 0.0f);
if (_skeletonModel->getRig().indexOfJoint(cgHeadMass.name) != -1) {
cgHeadMass.position = getAbsoluteJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint(cgHeadMass.name));
tposeHead = getAbsoluteDefaultJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint(cgHeadMass.name));
}
if (_skeletonModel->getRig().indexOfJoint(cgLeftHandMass.name) != -1) {
cgLeftHandMass.position = getAbsoluteJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint(cgLeftHandMass.name));
} else {
cgLeftHandMass.position = DEFAULT_AVATAR_LEFTHAND_POS;
}
if (_skeletonModel->getRig().indexOfJoint(cgRightHandMass.name) != -1) {
cgRightHandMass.position = getAbsoluteJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint(cgRightHandMass.name));
} else {
cgRightHandMass.position = DEFAULT_AVATAR_RIGHTHAND_POS;
}
if (_skeletonModel->getRig().indexOfJoint("Hips") != -1) {
tposeHips = getAbsoluteDefaultJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint("Hips"));
}
// find the current center of gravity position based on head and hand moments
glm::vec3 sumOfMoments = (cgHeadMass.weight * cgHeadMass.position) + (cgLeftHandMass.weight * cgLeftHandMass.position) + (cgRightHandMass.weight * cgRightHandMass.position);
float totalMass = cgHeadMass.weight + cgLeftHandMass.weight + cgRightHandMass.weight;
glm::vec3 currentCg = (1.0f / totalMass) * sumOfMoments;
currentCg.y = 0.0f;
// dampening the center of gravity, in effect, limits the value to the perimeter of the base of support
float baseScale = 1.0f;
if (getUserEyeHeight() > 0.0f) {
baseScale = getUserEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT;
}
glm::vec3 desiredCg = dampenCgMovement(currentCg, baseScale);
// compute hips position to maintain desiredCg
glm::vec3 counterBalancedForHead = (totalMass + DEFAULT_AVATAR_HIPS_MASS) * desiredCg;
counterBalancedForHead -= sumOfMoments;
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
// find the height of the hips
glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z));
float headMinusHipXz = glm::length(xzDiff);
float headHipDefault = glm::length(tposeHead - tposeHips);
float hipHeight = 0.0f;
if (headHipDefault > headMinusHipXz) {
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
}
counterBalancedCg.y = (cgHeadMass.position.y - hipHeight);
// this is to be sure that the feet don't lift off the floor.
// add 5 centimeters to allow for going up on the toes.
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
// if the height is higher than default hips, clamp to default hips
counterBalancedCg.y = tposeHips.y + 0.05f;
}
return counterBalancedCg;
}
// this function matches the hips rotation to the new cghips-head axis
// headOrientation, headPosition and hipsPosition are in avatar space
// returns the matrix of the hips in Avatar space
static glm::mat4 computeNewHipsMatrix(glm::quat headOrientation, glm::vec3 headPosition, glm::vec3 hipsPosition) {
glm::quat bodyOrientation = computeBodyFacingFromHead(headOrientation, Vectors::UNIT_Y);
const float MIX_RATIO = 0.3f;
glm::quat hipsRot = safeLerp(Quaternions::IDENTITY, bodyOrientation, MIX_RATIO);
glm::vec3 hipsFacing = hipsRot * Vectors::UNIT_Z;
glm::vec3 spineVec = headPosition - hipsPosition;
glm::vec3 u, v, w;
generateBasisVectors(glm::normalize(spineVec), hipsFacing, u, v, w);
return glm::mat4(glm::vec4(w, 0.0f),
glm::vec4(u, 0.0f),
glm::vec4(v, 0.0f),
glm::vec4(hipsPosition, 1.0f));
}
static void drawBaseOfSupport(float baseOfSupportScale, float footLocal, glm::mat4 avatarToWorld) {
// scale the base of support based on user height
float clampFront = DEFAULT_AVATAR_SUPPORT_BASE_FRONT * baseOfSupportScale;
float clampBack = DEFAULT_AVATAR_SUPPORT_BASE_BACK * baseOfSupportScale;
float clampLeft = DEFAULT_AVATAR_SUPPORT_BASE_LEFT * baseOfSupportScale;
float clampRight = DEFAULT_AVATAR_SUPPORT_BASE_RIGHT * baseOfSupportScale;
float floor = footLocal + 0.05f;
// transform the base of support corners to world space
glm::vec3 frontRight = transformPoint(avatarToWorld, { clampRight, floor, clampFront });
glm::vec3 frontLeft = transformPoint(avatarToWorld, { clampLeft, floor, clampFront });
glm::vec3 backRight = transformPoint(avatarToWorld, { clampRight, floor, clampBack });
glm::vec3 backLeft = transformPoint(avatarToWorld, { clampLeft, floor, clampBack });
// draw the borders
const glm::vec4 rayColor = { 1.0f, 0.0f, 0.0f, 1.0f };
DebugDraw::getInstance().drawRay(backLeft, frontLeft, rayColor);
DebugDraw::getInstance().drawRay(backLeft, backRight, rayColor);
DebugDraw::getInstance().drawRay(backRight, frontRight, rayColor);
DebugDraw::getInstance().drawRay(frontLeft, frontRight, rayColor);
}
// this function finds the hips position using a center of gravity model that
// balances the head and hands with the hips over the base of support
// returns the rotation (-z forward) and position of the Avatar in Sensor space
glm::mat4 MyAvatar::deriveBodyUsingCgModel() const {
glm::mat4 sensorToWorldMat = getSensorToWorldMatrix();
glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat);
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
glm::mat4 sensorHeadMat = createMatFromQuatAndPos(headPose.rotation * Quaternions::Y_180, headPose.translation);
// convert into avatar space
glm::mat4 avatarToWorldMat = getTransform().getMatrix();
glm::mat4 avatarHeadMat = glm::inverse(avatarToWorldMat) * sensorToWorldMat * sensorHeadMat;
if (_enableDebugDrawBaseOfSupport) {
float scaleBaseOfSupport = getUserEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT;
glm::vec3 rightFootPositionLocal = getAbsoluteJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint("RightFoot"));
drawBaseOfSupport(scaleBaseOfSupport, rightFootPositionLocal.y, avatarToWorldMat);
}
// get the new center of gravity
const glm::vec3 cgHipsPosition = computeCounterBalance();
// find the new hips rotation using the new head-hips axis as the up axis
glm::mat4 avatarHipsMat = computeNewHipsMatrix(glmExtractRotation(avatarHeadMat), extractTranslation(avatarHeadMat), cgHipsPosition);
// convert hips from avatar to sensor space
// The Y_180 is to convert from z forward to -z forward.
return worldToSensorMat * avatarToWorldMat * avatarHipsMat;
}
float MyAvatar::getUserHeight() const {
return _userHeight.get();
}
@ -3003,9 +3233,7 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
@ -3076,11 +3304,19 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
AnimPose followWorldPose(currentWorldMatrix);
glm::quat currentHipsLocal = myAvatar.getAbsoluteJointRotationInObjectFrame(myAvatar.getJointIndex("Hips"));
const glm::quat hipsinWorldSpace = followWorldPose.rot() * (Quaternions::Y_180 * (currentHipsLocal));
const glm::vec3 avatarUpWorld = glm::normalize(followWorldPose.rot()*(Vectors::UP));
glm::quat resultingSwingInWorld;
glm::quat resultingTwistInWorld;
swingTwistDecomposition(hipsinWorldSpace, avatarUpWorld, resultingSwingInWorld, resultingTwistInWorld);
// remove scale present from sensorToWorldMatrix
followWorldPose.scale() = glm::vec3(1.0f);
if (isActive(Rotation)) {
followWorldPose.rot() = glmExtractRotation(desiredWorldMatrix);
//use the hmd reading for the hips follow
followWorldPose.rot() = glmExtractRotation(desiredWorldMatrix);
}
if (isActive(Horizontal)) {
glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix);
@ -3476,6 +3712,10 @@ void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose&
}
}
bool MyAvatar::isRecenteringHorizontally() const {
return _follow.isActive(FollowHelper::Horizontal);
}
const MyHead* MyAvatar::getMyHead() const {
return static_cast<const MyHead*>(getHead());
}

View file

@ -105,6 +105,9 @@ class MyAvatar : public Avatar {
* by 30cm. <em>Read-only.</em>
* @property {Pose} rightHandTipPose - The pose of the right hand as determined by the hand controllers, with the position
* by 30cm. <em>Read-only.</em>
* @property {boolean} centerOfGravityModelEnabled=true - If <code>true</code> then the avatar hips are placed according to the center of
* gravity model that balance the center of gravity over the base of support of the feet. Setting the value <code>false</code>
* will result in the default behaviour where the hips are placed under the head.
* @property {boolean} hmdLeanRecenterEnabled=true - If <code>true</code> then the avatar is re-centered to be under the
* head's position. In room-scale VR, this behavior is what causes your avatar to follow your HMD as you walk around
* the room. Setting the value <code>false</code> is useful if you want to pin the avatar to a fixed position.
@ -199,6 +202,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(float energy READ getEnergy WRITE setEnergy)
Q_PROPERTY(bool isAway READ getIsAway WRITE setAway)
Q_PROPERTY(bool centerOfGravityModelEnabled READ getCenterOfGravityModelEnabled WRITE setCenterOfGravityModelEnabled)
Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled)
Q_PROPERTY(bool collisionsEnabled READ getCollisionsEnabled WRITE setCollisionsEnabled)
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
@ -464,7 +468,7 @@ public:
Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; }
/**jsdoc
* @function MyAvatar.setClearOverlayWhenMoving
* @returns {boolean}
* @param {boolean} on
*/
Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; }
@ -480,7 +484,16 @@ public:
*/
Q_INVOKABLE QString getDominantHand() const { return _dominantHand; }
/**jsdoc
* @function MyAvatar.setCenterOfGravityModelEnabled
* @param {boolean} enabled
*/
Q_INVOKABLE void setCenterOfGravityModelEnabled(bool value) { _centerOfGravityModelEnabled = value; }
/**jsdoc
* @function MyAvatar.getCenterOfGravityModelEnabled
* @returns {boolean}
*/
Q_INVOKABLE bool getCenterOfGravityModelEnabled() const { return _centerOfGravityModelEnabled; }
/**jsdoc
* @function MyAvatar.setHMDLeanRecenterEnabled
* @param {boolean} enabled
@ -564,6 +577,13 @@ public:
*/
Q_INVOKABLE void triggerRotationRecenter();
/**jsdoc
*The isRecenteringHorizontally function returns true if MyAvatar
*is translating the root of the Avatar to keep the center of gravity under the head.
*isActive(Horizontal) is returned.
*@function MyAvatar.isRecenteringHorizontally
*/
Q_INVOKABLE bool isRecenteringHorizontally() const;
eyeContactTarget getEyeContactTarget();
@ -956,10 +976,18 @@ public:
void removeHoldAction(AvatarActionHold* holdAction); // thread-safe
void updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& postUpdatePose);
// derive avatar body position and orientation from the current HMD Sensor location.
// results are in HMD frame
// results are in sensor frame (-z forward)
glm::mat4 deriveBodyFromHMDSensor() const;
glm::vec3 computeCounterBalance() const;
// derive avatar body position and orientation from using the current HMD Sensor location in relation to the previous
// location of the base of support of the avatar.
// results are in sensor frame (-z foward)
glm::mat4 deriveBodyUsingCgModel() const;
/**jsdoc
* @function MyAvatar.isUp
* @param {Vec3} direction
@ -1107,7 +1135,16 @@ public slots:
*/
Q_INVOKABLE void updateMotionBehaviorFromMenu();
/**jsdoc
* @function MyAvatar.setToggleHips
* @param {boolean} enabled
*/
void setToggleHips(bool followHead);
/**jsdoc
* @function MyAvatar.setEnableDebugDrawBaseOfSupport
* @param {boolean} enabled
*/
void setEnableDebugDrawBaseOfSupport(bool isEnabled);
/**jsdoc
* @function MyAvatar.setEnableDebugDrawDefaultPose
* @param {boolean} enabled
@ -1458,8 +1495,8 @@ private:
glm::quat _hmdSensorOrientation;
glm::vec3 _hmdSensorPosition;
// cache head controller pose in sensor space
glm::vec2 _headControllerFacing; // facing vector in xz plane
glm::vec2 _headControllerFacingMovingAverage { 0, 0 }; // facing vector in xz plane
glm::vec2 _headControllerFacing; // facing vector in xz plane (sensor space)
glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space)
// cache of the current body position and orientation of the avatar's body,
// in sensor space.
@ -1495,9 +1532,12 @@ private:
void setForceActivateVertical(bool val);
bool getForceActivateHorizontal() const;
void setForceActivateHorizontal(bool val);
std::atomic<bool> _forceActivateRotation{ false };
std::atomic<bool> _forceActivateVertical{ false };
std::atomic<bool> _forceActivateHorizontal{ false };
bool getToggleHipsFollowing() const;
void setToggleHipsFollowing(bool followHead);
std::atomic<bool> _forceActivateRotation { false };
std::atomic<bool> _forceActivateVertical { false };
std::atomic<bool> _forceActivateHorizontal { false };
std::atomic<bool> _toggleHipsFollowing { true };
};
FollowHelper _follow;
@ -1510,6 +1550,7 @@ private:
bool _prevShouldDrawHead;
bool _rigEnabled { true };
bool _enableDebugDrawBaseOfSupport { false };
bool _enableDebugDrawDefaultPose { false };
bool _enableDebugDrawAnimPose { false };
bool _enableDebugDrawHandControllers { false };
@ -1532,6 +1573,7 @@ private:
std::map<controller::Action, controller::Pose> _controllerPoseMap;
mutable std::mutex _controllerPoseMapMutex;
bool _centerOfGravityModelEnabled { true };
bool _hmdLeanRecenterEnabled { true };
bool _sprint { false };

View file

@ -45,7 +45,14 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
return result;
}
glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor();
glm::mat4 hipsMat;
if (myAvatar->getCenterOfGravityModelEnabled()) {
// then we use center of gravity model
hipsMat = myAvatar->deriveBodyUsingCgModel();
} else {
// otherwise use the default of putting the hips under the head
hipsMat = myAvatar->deriveBodyFromHMDSensor();
}
glm::vec3 hipsPos = extractTranslation(hipsMat);
glm::quat hipsRot = glmExtractRotation(hipsMat);
@ -53,8 +60,11 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat;
// dampen hips rotation, by mixing it with the avatar orientation in sensor space
const float MIX_RATIO = 0.5f;
hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO);
// turning this off for center of gravity model because it is already mixed in there
if (!(myAvatar->getCenterOfGravityModelEnabled())) {
const float MIX_RATIO = 0.5f;
hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO);
}
if (isFlying) {
// rotate the hips back to match the flying animation.
@ -73,6 +83,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
hipsPos = headPos + tiltRot * (hipsPos - headPos);
}
// AJT: TODO can we remove this?
return AnimPose(hipsRot * Quaternions::Y_180, hipsPos);
}
@ -170,6 +181,15 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
}
}
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
if (isFlying != _prevIsFlying) {
const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f;
_flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME;
} else {
_flyIdleTimer -= deltaTime;
}
_prevIsFlying = isFlying;
// if hips are not under direct control, estimate the hips position.
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
@ -181,14 +201,28 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
// timescale in seconds
const float TRANS_HORIZ_TIMESCALE = 0.15f;
const float TRANS_VERT_TIMESCALE = 0.01f; // We want the vertical component of the hips to follow quickly to prevent spine squash/stretch.
const float ROT_TIMESCALE = 0.15f;
const float FLY_IDLE_TRANSITION_TIMESCALE = 0.25f;
float transHorizAlpha, transVertAlpha, rotAlpha;
if (_flyIdleTimer < 0.0f) {
transHorizAlpha = glm::min(deltaTime / TRANS_HORIZ_TIMESCALE, 1.0f);
transVertAlpha = glm::min(deltaTime / TRANS_VERT_TIMESCALE, 1.0f);
rotAlpha = glm::min(deltaTime / ROT_TIMESCALE, 1.0f);
} else {
transHorizAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f);
transVertAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f);
rotAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f);
}
// smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation.
const float ROT_ALPHA = 0.9f;
const float TRANS_HORIZ_ALPHA = 0.9f;
const float TRANS_VERT_ALPHA = 0.1f;
float hipsY = hips.trans().y;
hips.trans() = lerp(hips.trans(), _prevHips.trans(), TRANS_HORIZ_ALPHA);
hips.trans().y = lerp(hipsY, _prevHips.trans().y, TRANS_VERT_ALPHA);
hips.rot() = safeLerp(hips.rot(), _prevHips.rot(), ROT_ALPHA);
hips.trans() = lerp(_prevHips.trans(), hips.trans(), transHorizAlpha);
hips.trans().y = lerp(_prevHips.trans().y, hipsY, transVertAlpha);
hips.rot() = safeLerp(_prevHips.rot(), hips.rot(), rotAlpha);
_prevHips = hips;
_prevHipsValid = true;

View file

@ -28,6 +28,8 @@ private:
AnimPose _prevHips; // sensor frame
bool _prevHipsValid { false };
bool _prevIsFlying { false };
float _flyIdleTimer { 0.0f };
std::map<int, int> _jointRotationFrameOffsetMap;
};

View file

@ -314,6 +314,7 @@ Wallet::Wallet() {
auto nodeList = DependencyManager::get<NodeList>();
auto ledger = DependencyManager::get<Ledger>();
auto& packetReceiver = nodeList->getPacketReceiver();
_passphrase = new QString("");
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
@ -365,6 +366,10 @@ Wallet::~Wallet() {
if (_securityImage) {
delete _securityImage;
}
if (_passphrase) {
delete _passphrase;
}
}
bool Wallet::setPassphrase(const QString& passphrase) {
@ -531,7 +536,6 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
// be sure to add the public key so we don't do this over and over
_publicKeys.push_back(publicKey.toBase64());
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
return true;
}
}
@ -610,7 +614,11 @@ void Wallet::updateImageProvider() {
SecurityImageProvider* securityImageProvider;
// inform offscreenUI security image provider
QQmlEngine* engine = DependencyManager::get<OffscreenUi>()->getSurfaceContext()->engine();
auto offscreenUI = DependencyManager::get<OffscreenUi>();
if (!offscreenUI) {
return;
}
QQmlEngine* engine = offscreenUI->getSurfaceContext()->engine();
securityImageProvider = reinterpret_cast<SecurityImageProvider*>(engine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
securityImageProvider->setSecurityImage(_securityImage);

View file

@ -78,7 +78,7 @@ private:
QByteArray _salt;
QByteArray _iv;
QByteArray _ckey;
QString* _passphrase { new QString("") };
QString* _passphrase { nullptr };
bool _isOverridingServer { false };
bool writeWallet(const QString& newPassphrase = QString(""));

View file

@ -36,7 +36,7 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
* @typedef {object} Picks.RayPickProperties
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD.
* If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it
* exists on your current avatar.
@ -103,7 +103,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
* @property {number} [hand=-1] An integer. 0 == left, 1 == right. Invalid otherwise.
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
*/
unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap();

View file

@ -104,7 +104,7 @@ public:
* @property {number} type The intersection type.
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
* @property {float} distance The distance to the intersection point from the origin of the ray.
* @property {number} distance The distance to the intersection point from the origin of the ray.
* @property {Vec3} intersection The intersection point in world-space.
* @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD.
* @property {Variant} extraInfo Additional intersection details when available for Model objects.
@ -118,7 +118,7 @@ public:
* @property {number} type The intersection type.
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
* @property {float} distance The distance to the intersection point from the origin of the ray.
* @property {number} distance The distance to the intersection point from the origin of the ray.
* @property {Vec3} intersection The intersection point in world-space.
* @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD.
* @property {Variant} extraInfo Additional intersection details when available for Model objects.

View file

@ -136,7 +136,7 @@ public:
* Sets the length of this Pointer. No effect on Stylus Pointers.
* @function Pointers.setLength
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
* @param {float} length The desired length of the Pointer.
* @param {number} length The desired length of the Pointer.
*/
Q_INVOKABLE void setLength(unsigned int uid, float length) const { DependencyManager::get<PointerManager>()->setLength(uid, length); }

View file

@ -105,9 +105,6 @@ class ScriptEngine;
* <li>{@link Controller.getValue|getValue}</li>
* <li>{@link Controller.getAxisValue|getAxisValue}</li>
* <li>{@link Controller.getPoseValue|getgetPoseValue}</li>
* <li>{@link Controller.getButtonValue|getButtonValue} for a particular device</li>
* <li>{@link Controller.getAxisValue(0)|getAxisValue} for a particular device</li>
* <li>{@link Controller.getPoseValue(0)|getPoseValue} for a particular device</li>
* <li>{@link Controller.getActionValue|getActionValue}</li>
* </ul>
*

View file

@ -318,7 +318,6 @@ public slots:
* {@link Window.stillSnapshotTaken|stillSnapshotTaken} is emitted; when a still image plus moving images are captured,
* {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted}
* are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings >
* NOTE: to provide a non-default value - all previous parameters must be provided.
* General > Snapshots.
* @function Window.takeSnapshot
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
@ -328,7 +327,7 @@ public slots:
* @param {number} [aspectRatio=0] - The width/height ratio of the snapshot required. If the value is <code>0</code> the
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
* dimensions is adjusted in order to match the aspect ratio.
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as "hifi-snap-by-&lt;user name&gt-YYYY-MM-DD_HH-MM-SS".
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
*
@ -360,32 +359,28 @@ public slots:
/**jsdoc
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
* NOTE: to provide a non-default value - all previous parameters must be provided.
* @function Window.takeSecondaryCameraSnapshot
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
* signal.
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as "hifi-snap-by-&lt;user name&gt;-YYYY-MM-DD_HH-MM-SS".
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
*
* var filename = QString();
*/
void takeSecondaryCameraSnapshot(const bool& notify = true, const QString& filename = QString());
/**jsdoc
* Takes a 360 snapshot given a position of the secondary camera (which does not need to have been previously set up).
* @function Window.takeSecondaryCamera360Snapshot
* @param {vec3} [cameraPosition] - The (x, y, z) position of the camera for the 360 snapshot
* @param {boolean} [cubemapOutputFormat=false] - If <code>true</code> then the snapshot is saved as a cube map image,
* otherwise is saved as an equirectangular image.
* Takes a 360&deg; snapshot at a given position for the secondary camera. The secondary camera does not need to have been
* set up.
* @function Window.takeSecondaryCamera360Snapshot
* @param {Vec3} cameraPosition - The position of the camera for the snapshot.
* @param {boolean} [cubemapOutputFormat=false] - If <code>true</code> then the snapshot is saved as a cube map image,
* otherwise is saved as an equirectangular image.
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
* signal.
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
*
* var filename = QString();
*/
* @param {string} [filename=""] - If this parameter is not supplied, the image will be saved as "hifi-snap-by-&lt;user name&gt;-YYYY-MM-DD_HH-MM-SS".
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
*/
void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString());
/**jsdoc
@ -616,7 +611,8 @@ signals:
void stillSnapshotTaken(const QString& pathStillSnapshot, bool notify);
/**jsdoc
* Triggered when a still equirectangular snapshot has been taken by calling {@link Window.takeSecondaryCamera360Snapshot|takeSecondaryCamera360Snapshot}
* Triggered when a still 360&deg; snapshot has been taken by calling
* {@link Window.takeSecondaryCamera360Snapshot|takeSecondaryCamera360Snapshot}.
* @function Window.snapshot360Taken
* @param {string} pathStillSnapshot - The path and name of the snapshot image file.
* @param {boolean} notify - The value of the <code>notify</code> parameter that {@link Window.takeSecondaryCamera360Snapshot|takeSecondaryCamera360Snapshot}

View file

@ -349,3 +349,23 @@ void Base3DOverlay::setVisible(bool visible) {
Parent::setVisible(visible);
notifyRenderVariableChange();
}
render::ItemKey Base3DOverlay::getKey() {
auto builder = render::ItemKey::Builder(Overlay::getKey());
if (getDrawInFront()) {
builder.withLayer(render::hifi::LAYER_3D_FRONT);
} else if (getDrawHUDLayer()) {
builder.withLayer(render::hifi::LAYER_3D_HUD);
} else {
builder.withoutLayer();
}
builder.withoutViewSpace();
if (isTransparent()) {
builder.withTransparent();
}
return builder.build();
}

View file

@ -35,6 +35,7 @@ public:
// getters
virtual bool is3D() const override { return true; }
virtual render::ItemKey getKey() override;
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); }
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }

View file

@ -115,6 +115,10 @@ void ModelOverlay::update(float deltatime) {
_drawInHUDDirty = false;
_model->setLayeredInHUD(getDrawHUDLayer(), scene);
}
if (_groupCulledDirty) {
_groupCulledDirty = false;
_model->setGroupCulled(_isGroupCulled);
}
scene->enqueueTransaction(transaction);
if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) {
@ -149,13 +153,24 @@ void ModelOverlay::setVisible(bool visible) {
}
void ModelOverlay::setDrawInFront(bool drawInFront) {
Base3DOverlay::setDrawInFront(drawInFront);
_drawInFrontDirty = true;
if (drawInFront != getDrawInFront()) {
Base3DOverlay::setDrawInFront(drawInFront);
_drawInFrontDirty = true;
}
}
void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) {
Base3DOverlay::setDrawHUDLayer(drawHUDLayer);
_drawInHUDDirty = true;
if (drawHUDLayer != getDrawHUDLayer()) {
Base3DOverlay::setDrawHUDLayer(drawHUDLayer);
_drawInHUDDirty = true;
}
}
void ModelOverlay::setGroupCulled(bool groupCulled) {
if (groupCulled != _isGroupCulled) {
_isGroupCulled = groupCulled;
_groupCulledDirty = true;
}
}
void ModelOverlay::setProperties(const QVariantMap& properties) {
@ -210,6 +225,11 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
Q_ARG(const QVariantMap&, textureMap));
}
auto groupCulledValue = properties["isGroupCulled"];
if (groupCulledValue.isValid() && groupCulledValue.canConvert(QVariant::Bool)) {
setGroupCulled(groupCulledValue.toBool());
}
// jointNames is read-only.
// jointPositions is read-only.
// jointOrientations is read-only.
@ -347,6 +367,8 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
* @property {boolean} isGroupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
* If <code>false</code>, separate mesh parts will be LOD culled individually.
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
@ -711,3 +733,11 @@ scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() {
}
return result;
}
render::ItemKey ModelOverlay::getKey() {
auto builder = render::ItemKey::Builder(Base3DOverlay::getKey());
if (_isGroupCulled) {
builder.withMetaCullGroup();
}
return builder.build();
}

View file

@ -33,6 +33,7 @@ public:
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override;
render::ItemKey getKey() override;
void clearSubRenderItemIDs();
void setSubRenderItemIDs(const render::ItemIDs& ids);
@ -63,6 +64,7 @@ public:
void setVisible(bool visible) override;
void setDrawInFront(bool drawInFront) override;
void setDrawHUDLayer(bool drawHUDLayer) override;
void setGroupCulled(bool groupCulled);
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override;
@ -121,6 +123,8 @@ private:
bool _visibleDirty { true };
bool _drawInFrontDirty { false };
bool _drawInHUDDirty { false };
bool _isGroupCulled { false };
bool _groupCulledDirty { false };
void processMaterials();

View file

@ -244,4 +244,21 @@ void Overlay::addMaterial(graphics::MaterialLayer material, const std::string& p
void Overlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].remove(material);
}
render::ItemKey Overlay::getKey() {
auto builder = render::ItemKey::Builder().withTypeShape();
builder.withViewSpace();
builder.withLayer(render::hifi::LAYER_2D);
if (!getVisible()) {
builder.withInvisible();
}
// always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view
render::hifi::Tag viewTagBits = getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW;
builder.withTagBits(viewTagBits);
return builder.build();
}

View file

@ -40,6 +40,7 @@ public:
virtual void update(float deltatime) {}
virtual void render(RenderArgs* args) = 0;
virtual render::ItemKey getKey();
virtual AABox getBounds() const = 0;
virtual bool supportsGetProperty() const { return true; }

View file

@ -32,32 +32,7 @@
namespace render {
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
auto builder = ItemKey::Builder().withTypeShape();
if (overlay->is3D()) {
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
if (overlay3D->getDrawInFront()) {
builder.withLayer(render::hifi::LAYER_3D_FRONT);
} else if (overlay3D->getDrawHUDLayer()) {
builder.withLayer(render::hifi::LAYER_3D_HUD);
}
if (overlay->isTransparent()) {
builder.withTransparent();
}
} else {
builder.withViewSpace();
builder.withLayer(render::hifi::LAYER_2D);
}
if (!overlay->getVisible()) {
builder.withInvisible();
}
// always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view
render::hifi::Tag viewTagBits = overlay->getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW;
builder.withTagBits(viewTagBits);
return builder.build();
return overlay->getKey();
}
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) {
return overlay->getBounds();

View file

@ -9,7 +9,9 @@
//
#include "AnimUtil.h"
#include "GLMHelpers.h"
#include <GLMHelpers.h>
#include <NumericalConstants.h>
#include <DebugDraw.h>
// TODO: use restrict keyword
// TODO: excellent candidate for simd vectorization.
@ -107,3 +109,44 @@ AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone) {
glm::vec4(bone.trans(), 1.0f));
return AnimPose(lookAt);
}
// This will attempt to determine the proper body facing of a characters body
// assumes headRot is z-forward and y-up.
// and returns a bodyRot that is also z-forward and y-up
glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& up) {
glm::vec3 bodyUp = glm::normalize(up);
// initially take the body facing from the head.
glm::vec3 headUp = headRot * Vectors::UNIT_Y;
glm::vec3 headForward = headRot * Vectors::UNIT_Z;
glm::vec3 headLeft = headRot * Vectors::UNIT_X;
const float NOD_THRESHOLD = cosf(glm::radians(45.0f));
const float TILT_THRESHOLD = cosf(glm::radians(30.0f));
glm::vec3 bodyForward = headForward;
float nodDot = glm::dot(headForward, bodyUp);
float tiltDot = glm::dot(headLeft, bodyUp);
if (fabsf(tiltDot) < TILT_THRESHOLD) { // if we are not tilting too much
if (nodDot < -NOD_THRESHOLD) { // head is looking downward
// the body should face in the same direction as the top the head.
bodyForward = headUp;
} else if (nodDot > NOD_THRESHOLD) { // head is looking upward
// the body should face away from the top of the head.
bodyForward = -headUp;
}
}
// cancel out upward component
bodyForward = glm::normalize(bodyForward - nodDot * bodyUp);
glm::vec3 u, v, w;
generateBasisVectors(bodyForward, bodyUp, u, v, w);
// create matrix from orthogonal basis vectors
glm::mat4 bodyMat(glm::vec4(w, 0.0f), glm::vec4(v, 0.0f), glm::vec4(u, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
return glmExtractRotation(bodyMat);
}

View file

@ -33,4 +33,9 @@ inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone);
// This will attempt to determine the proper body facing of a characters body
// assumes headRot is z-forward and y-up.
// and returns a bodyRot that is also z-forward and y-up
glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& up);
#endif

View file

@ -11,6 +11,8 @@
#include "AutoUpdater.h"
#include <BuildInfo.h>
#include <NetworkAccessManager.h>
#include <SharedUtil.h>
#include <unordered_map>
@ -157,10 +159,8 @@ void AutoUpdater::parseLatestVersionData() {
}
void AutoUpdater::checkVersionAndNotify() {
if (QCoreApplication::applicationVersion() == "dev" ||
QCoreApplication::applicationVersion().contains("PR") ||
_builds.empty()) {
// No version checking is required in dev builds or when no build
if (BuildInfo::BUILD_TYPE != BuildInfo::BuildType::Stable || _builds.empty()) {
// No version checking is required in nightly/PR/dev builds or when no build
// data was found for the platform
return;
}
@ -196,4 +196,4 @@ void AutoUpdater::appendBuildData(int versionNumber,
thisBuildDetails.insert("pullRequestNumber", pullRequestNumber);
_builds.insert(versionNumber, thisBuildDetails);
}
}

View file

@ -92,28 +92,16 @@ namespace controller {
return userInputMapper->getValue(Input((uint32_t)source));
}
float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const {
return getValue(Input(device, source, ChannelType::BUTTON).getID());
}
float ScriptingInterface::getAxisValue(int source) const {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getValue(Input((uint32_t)source));
}
float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const {
return getValue(Input(device, source, ChannelType::AXIS).getID());
}
Pose ScriptingInterface::getPoseValue(const int& source) const {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getPose(Input((uint32_t)source));
}
Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const {
return getPoseValue(Input(device, source, ChannelType::POSE).getID());
}
QVector<Action> ScriptingInterface::getAllActions() {
return DependencyManager::get<UserInputMapper>()->getAllActions();
}

View file

@ -206,43 +206,6 @@ namespace controller {
*/
Q_INVOKABLE Pose getPoseValue(const int& source) const;
/**jsdoc
* Get the value of a button on a particular device.
* @function Controller.getButtonValue
* @param {StandardButtonChannel} source - The button to get the value of.
* @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of
* <code>0</code> corresponds to <code>Standard</code>.
* @returns {number} The current value of the button if the parameters are valid, otherwise <code>0</code>.
* @deprecated This function no longer works.
*/
// FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/14139
Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;
/**jsdoc
* Get the value of an axis control on a particular device.
* @function Controller.getAxisValue
* @variation 0
* @param {StandardAxisChannel} source - The axis to get the value of.
* @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of
* <code>0</code> corresponds to <code>Standard</code>.
* @returns {number} The current value of the axis if the parameters are valid, otherwise <code>0</code>.
* @deprecated This function no longer works.
*/
Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const;
/**jsdoc
* Get the value of an pose control on a particular device.
* @function Controller.getPoseValue
* @variation 0
* @param {StandardPoseChannel} source - The pose to get the value of.
* @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of
* <code>0</code> corresponds to <code>Standard</code>.
* @returns {Pose} The current value of the controller pose output if the parameters are valid, otherwise an invalid
* pose with <code>Pose.valid == false</code>.
* @deprecated This function no longer works.
*/
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
/**jsdoc
* Triggers a haptic pulse on connected and enabled devices that have the capability.
* @function Controller.triggerHapticPulse

View file

@ -11,6 +11,8 @@
#include "HTTPConnection.h"
#include <assert.h>
#include <QBuffer>
#include <QCryptographicHash>
#include <QTcpSocket>
@ -29,11 +31,92 @@ const char* HTTPConnection::StatusCode404 = "404 Not Found";
const char* HTTPConnection::StatusCode500 = "500 Internal server error";
const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1";
HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) :
class MemoryStorage : public HTTPConnection::Storage {
public:
static std::unique_ptr<MemoryStorage> make(qint64 size);
virtual ~MemoryStorage() = default;
const QByteArray& content() const override { return _array; }
qint64 bytesLeftToWrite() const override { return _array.size() - _bytesWritten; }
void write(const QByteArray& data) override;
private:
MemoryStorage(qint64 size) { _array.resize(size); }
QByteArray _array;
qint64 _bytesWritten { 0 };
};
std::unique_ptr<MemoryStorage> MemoryStorage::make(qint64 size) {
return std::unique_ptr<MemoryStorage>(new MemoryStorage(size));
}
void MemoryStorage::write(const QByteArray& data) {
assert(data.size() <= bytesLeftToWrite());
memcpy(_array.data() + _bytesWritten, data.data(), data.size());
_bytesWritten += data.size();
}
class FileStorage : public HTTPConnection::Storage {
public:
static std::unique_ptr<FileStorage> make(qint64 size);
virtual ~FileStorage();
const QByteArray& content() const override { return _wrapperArray; };
qint64 bytesLeftToWrite() const override { return _mappedMemorySize - _bytesWritten; }
void write(const QByteArray& data) override;
private:
FileStorage(std::unique_ptr<QTemporaryFile> file, uchar* mapped, qint64 size);
// Byte array is const because any edit will trigger a deep copy
// and pull all the data we want to keep on disk in memory.
const QByteArray _wrapperArray;
std::unique_ptr<QTemporaryFile> _file;
uchar* const _mappedMemoryAddress { nullptr };
const qint64 _mappedMemorySize { 0 };
qint64 _bytesWritten { 0 };
};
std::unique_ptr<FileStorage> FileStorage::make(qint64 size) {
auto file = std::unique_ptr<QTemporaryFile>(new QTemporaryFile());
file->open(); // Open for resize
file->resize(size);
auto mapped = file->map(0, size); // map the entire file
return std::unique_ptr<FileStorage>(new FileStorage(std::move(file), mapped, size));
}
// Use QByteArray::fromRawData to avoid a new allocation and access the already existing
// memory directly as long as all operations on the array are const.
FileStorage::FileStorage(std::unique_ptr<QTemporaryFile> file, uchar* mapped, qint64 size) :
_wrapperArray(QByteArray::fromRawData(reinterpret_cast<char*>(mapped), size)),
_file(std::move(file)),
_mappedMemoryAddress(mapped),
_mappedMemorySize(size)
{
}
FileStorage::~FileStorage() {
_file->unmap(_mappedMemoryAddress);
_file->close();
}
void FileStorage::write(const QByteArray& data) {
assert(data.size() <= bytesLeftToWrite());
// We write directly to the mapped memory
memcpy(_mappedMemoryAddress + _bytesWritten, data.data(), data.size());
_bytesWritten += data.size();
}
HTTPConnection::HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager) :
QObject(parentManager),
_parentManager(parentManager),
_socket(socket),
_stream(socket),
_address(socket->peerAddress())
{
// take over ownership of the socket
@ -62,7 +145,7 @@ QHash<QString, QString> HTTPConnection::parseUrlEncodedForm() {
return QHash<QString, QString>();
}
QUrlQuery form { _requestContent };
QUrlQuery form { _requestContent->content() };
QHash<QString, QString> pairs;
for (auto pair : form.queryItems()) {
auto key = QUrl::fromPercentEncoding(pair.first.toLatin1().replace('+', ' '));
@ -97,7 +180,7 @@ QList<FormData> HTTPConnection::parseFormData() const {
QByteArray end = "\r\n--" + boundary + "--\r\n";
QList<FormData> data;
QBuffer buffer(const_cast<QByteArray*>(&_requestContent));
QBuffer buffer(const_cast<QByteArray*>(&_requestContent->content()));
buffer.open(QIODevice::ReadOnly);
while (buffer.canReadLine()) {
QByteArray line = buffer.readLine().trimmed();
@ -107,12 +190,13 @@ QList<FormData> HTTPConnection::parseFormData() const {
QByteArray line = buffer.readLine().trimmed();
if (line.isEmpty()) {
// content starts after this line
int idx = _requestContent.indexOf(end, buffer.pos());
int idx = _requestContent->content().indexOf(end, buffer.pos());
if (idx == -1) {
qWarning() << "Missing end boundary." << _address;
return data;
}
datum.second = _requestContent.mid(buffer.pos(), idx - buffer.pos());
datum.second = QByteArray::fromRawData(_requestContent->content().data() + buffer.pos(),
idx - buffer.pos());
data.append(datum);
buffer.seek(idx + end.length());
@ -256,7 +340,24 @@ void HTTPConnection::readHeaders() {
_parentManager->handleHTTPRequest(this, _requestUrl);
} else {
_requestContent.resize(clength.toInt());
bool success = false;
auto length = clength.toInt(&success);
if (!success) {
qWarning() << "Invalid header." << _address << trimmed;
respond("400 Bad Request", "The header was malformed.");
return;
}
// Storing big requests in memory gets expensive, especially on servers
// with limited memory. So we store big requests in a temporary file on disk
// and map it to faster read/write access.
static const int MAX_CONTENT_SIZE_IN_MEMORY = 10 * 1000 * 1000;
if (length < MAX_CONTENT_SIZE_IN_MEMORY) {
_requestContent = MemoryStorage::make(length);
} else {
_requestContent = FileStorage::make(length);
}
connect(_socket, SIGNAL(readyRead()), SLOT(readContent()));
// read any content immediately available
@ -285,12 +386,13 @@ void HTTPConnection::readHeaders() {
}
void HTTPConnection::readContent() {
int size = _requestContent.size();
if (_socket->bytesAvailable() < size) {
return;
}
_socket->read(_requestContent.data(), size);
_socket->disconnect(this, SLOT(readContent()));
auto size = std::min(_socket->bytesAvailable(), _requestContent->bytesLeftToWrite());
_parentManager->handleHTTPRequest(this, _requestUrl.path());
_requestContent->write(_socket->read(size));
if (_requestContent->bytesLeftToWrite() == 0) {
_socket->disconnect(this, SLOT(readContent()));
_parentManager->handleHTTPRequest(this, _requestUrl.path());
}
}

View file

@ -16,14 +16,14 @@
#ifndef hifi_HTTPConnection_h
#define hifi_HTTPConnection_h
#include <QDataStream>
#include <QHash>
#include <QtNetwork/QHostAddress>
#include <QIODevice>
#include <QList>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QNetworkAccessManager>
#include <QObject>
#include <QPair>
#include <QTemporaryFile>
#include <QUrl>
#include <memory>
@ -57,52 +57,63 @@ public:
/// WebSocket close status codes.
enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 };
class Storage {
public:
Storage() = default;
virtual ~Storage() = default;
virtual const QByteArray& content() const = 0;
virtual qint64 bytesLeftToWrite() const = 0;
virtual void write(const QByteArray& data) = 0;
};
/// Initializes the connection.
HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager);
HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager);
/// Destroys the connection.
virtual ~HTTPConnection ();
virtual ~HTTPConnection();
/// Returns a pointer to the underlying socket, to which WebSocket message bodies should be written.
QTcpSocket* socket () const { return _socket; }
QTcpSocket* socket() const { return _socket; }
/// Returns the request operation.
QNetworkAccessManager::Operation requestOperation () const { return _requestOperation; }
QNetworkAccessManager::Operation requestOperation() const { return _requestOperation; }
/// Returns a reference to the request URL.
const QUrl& requestUrl () const { return _requestUrl; }
const QUrl& requestUrl() const { return _requestUrl; }
/// Returns a copy of the request header value. If it does not exist, it will return a default constructed QByteArray.
QByteArray requestHeader(const QString& key) const { return _requestHeaders.value(key.toLower().toLocal8Bit()); }
/// Returns a reference to the request content.
const QByteArray& requestContent () const { return _requestContent; }
const QByteArray& requestContent() const { return _requestContent->content(); }
/// Parses the request content as form data, returning a list of header/content pairs.
QList<FormData> parseFormData () const;
QList<FormData> parseFormData() const;
/// Parses the request content as a url encoded form, returning a hash of key/value pairs.
/// Duplicate keys are not supported.
QHash<QString, QString> parseUrlEncodedForm();
/// Sends a response and closes the connection.
void respond (const char* code, const QByteArray& content = QByteArray(),
void respond(const char* code, const QByteArray& content = QByteArray(),
const char* contentType = DefaultContentType,
const Headers& headers = Headers());
void respond (const char* code, std::unique_ptr<QIODevice> device,
void respond(const char* code, std::unique_ptr<QIODevice> device,
const char* contentType = DefaultContentType,
const Headers& headers = Headers());
protected slots:
/// Reads the request line.
void readRequest ();
void readRequest();
/// Reads the headers.
void readHeaders ();
void readHeaders();
/// Reads the content.
void readContent ();
void readContent();
protected:
void respondWithStatusAndHeaders(const char* code, const char* contentType, const Headers& headers, qint64 size);
@ -112,9 +123,6 @@ protected:
/// The underlying socket.
QTcpSocket* _socket;
/// The data stream for writing to the socket.
QDataStream _stream;
/// The stored address.
QHostAddress _address;
@ -132,7 +140,7 @@ protected:
QByteArray _lastRequestHeader;
/// The content of the request.
QByteArray _requestContent;
std::unique_ptr<Storage> _requestContent;
/// Response content
std::unique_ptr<QIODevice> _responseDevice;

View file

@ -24,8 +24,7 @@
const int SOCKET_ERROR_EXIT_CODE = 2;
const int SOCKET_CHECK_INTERVAL_IN_MS = 30000;
HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) :
QTcpServer(parent),
HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler) :
_listenAddress(listenAddress),
_documentRoot(documentRoot),
_requestHandler(requestHandler),

View file

@ -33,7 +33,7 @@ class HTTPManager : public QTcpServer, public HTTPRequestHandler {
Q_OBJECT
public:
/// Initializes the manager.
HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0);
HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = nullptr);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;

View file

@ -23,4 +23,4 @@ protected slots:
void handleSSLErrors(const QList<QSslError>& errors);
};
#endif // hifi_HTTPSConnection_h
#endif // hifi_HTTPSConnection_h

View file

@ -16,8 +16,8 @@
#include "HTTPSConnection.h"
HTTPSManager::HTTPSManager(QHostAddress listenAddress, quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey,
const QString& documentRoot, HTTPSRequestHandler* requestHandler, QObject* parent) :
HTTPManager(listenAddress, port, documentRoot, requestHandler, parent),
const QString& documentRoot, HTTPSRequestHandler* requestHandler) :
HTTPManager(listenAddress, port, documentRoot, requestHandler),
_certificate(certificate),
_privateKey(privateKey),
_sslRequestHandler(requestHandler)

View file

@ -31,7 +31,7 @@ public:
const QSslCertificate& certificate,
const QSslKey& privateKey,
const QString& documentRoot,
HTTPSRequestHandler* requestHandler = NULL, QObject* parent = 0);
HTTPSRequestHandler* requestHandler = nullptr);
void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; }
void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; }
@ -48,4 +48,4 @@ private:
HTTPSRequestHandler* _sslRequestHandler;
};
#endif // hifi_HTTPSManager_h
#endif // hifi_HTTPSManager_h

View file

@ -206,7 +206,7 @@ private:
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
bool _didLastVisualGeometryRequestSucceed { false };
bool _didLastVisualGeometryRequestSucceed { true };
void processMaterials();
};

View file

@ -597,6 +597,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only
* provided for <code>Model</code> entities. <em>Read-only.</em>
*
* @property {boolean} cloneable=false - If <code>true</code> then the entity can be cloned via {@link Entities.cloneEntity}.
* @property {number} cloneLifetime=300 - The entity lifetime for clones created from this entity.
* @property {number} cloneLimit=0 - The total number of clones of this entity that can exist in the domain at any given time.
* @property {boolean} cloneDynamic=false - If <code>true</code> then clones created from this entity will have their
* <code>dynamic</code> property set to <code>true</code>.
* @property {boolean} cloneAvatarEntity=false - If <code>true</code> then clones created from this entity will be created as
* avatar entities: their <code>clientOnly</code> property will be set to <code>true</code>.
* @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from.
*
* @property {string} itemName="" - Certifiable name of the Marketplace item.
* @property {string} itemDescription="" - Certifiable description of the Marketplace item.
* @property {string} itemCategories="" - Certifiable category of the Marketplace item.

View file

@ -225,12 +225,12 @@ public slots:
bool collisionless, const glm::vec3& position, const glm::vec3& gravity);
/**jsdoc
* Request a clone of an entity. Only entities that have been marked as 'cloneable' will be able to be cloned using this method.
* A cloned entity has most of the properties of the orignal entity, and can be requested from clients that do not have rez permissions.
* The client requests a clone from the entity server, which returns back the entityID of a valid clone if the operation was allowed.
* Create a clone of an entity. A clone can be created by a client that doesn't have rez permissions in the current domain.
* The entity must have its <code>cloneable</code> property set to <code>true</code>. The clone has a modified name, other
* properties set per its clone related-properties, and its clone-related properties are set to defaults.
* @function Entities.cloneEntity
* @param {Uuid} entityIDToClone - the ID of the entity to clone
* @returns {Entities.EntityID} The ID of the newly created clone
* @param {Uuid} entityID - The ID of the entity to clone.
* @returns {Uuid} The ID of the new entity if successfully cloned, otherwise {@link Uuid|Uuid.NULL}.
*/
Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone);

View file

@ -684,9 +684,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");
#ifndef USE_GLES
auto mipFormat = cubeTexture.getStoredMipFormat();
std::function<glm::vec3(uint32)> unpackFunc;
switch (mipFormat.getSemantic()) {
case gpu::R11G11B10:
unpackFunc = glm::unpackF2x11_1x10;
@ -698,6 +698,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
assert(false);
break;
}
#endif
const uint sqOrder = order*order;
@ -732,7 +733,11 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
PROFILE_RANGE(render_gpu, "ProcessFace");
#ifndef USE_GLES
auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() );
#else
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
#endif
if (data == nullptr) {
continue;
}
@ -816,8 +821,15 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
glm::vec3 color{ 0.0f, 0.0f, 0.0f };
for (int i = 0; i < stride; ++i) {
for (int j = 0; j < stride; ++j) {
#ifndef USE_GLES
int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
color += unpackFunc(data[k]);
#else
const int NUM_COMPONENTS_PER_PIXEL = 4;
int k = NUM_COMPONENTS_PER_PIXEL * (int)(x + i - halfStride + (y + j - halfStride) * width);
// BGRA -> RGBA
color += glm::pow(glm::vec3(data[k + 2], data[k + 1], data[k]) / 255.0f, glm::vec3(2.2f));
#endif
}
}

View file

@ -352,7 +352,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) {
return nullptr;
}
// Set Dimensions
uint32_t numFaces = 1;
switch (texture.getType()) {

View file

@ -2,3 +2,4 @@ set(TARGET_NAME image)
setup_hifi_library()
link_hifi_libraries(shared gpu)
target_nvtt()
target_etc2comp()

View file

@ -31,8 +31,17 @@ using namespace gpu;
#define CPU_MIPMAPS 1
#include <nvtt/nvtt.h>
#ifdef USE_GLES
#include <Etc.h>
#include <EtcFilter.h>
#endif
static const glm::uvec2 SPARSE_PAGE_SIZE(128);
#ifdef Q_OS_ANDROID
static const glm::uvec2 MAX_TEXTURE_SIZE(2048);
#else
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
#endif
bool DEV_DECIMATE_TEXTURES = false;
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
@ -75,7 +84,13 @@ const QStringList getSupportedFormats() {
return stringFormats;
}
// On GLES, we don't use HDR skyboxes
#ifndef USE_GLES
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB30;
#else
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB32;
#endif
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
switch (type) {
@ -548,13 +563,15 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
QImage localCopy = std::move(image);
if (localCopy.format() != QImage::Format_ARGB32) {
if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != QIMAGE_HDR_FORMAT) {
localCopy = localCopy.convertToFormat(QImage::Format_ARGB32);
}
const int width = localCopy.width(), height = localCopy.height();
const void* data = static_cast<const void*>(localCopy.constBits());
auto mipFormat = texture->getStoredMipFormat();
#ifndef USE_GLES
const void* data = static_cast<const void*>(localCopy.constBits());
nvtt::TextureType textureType = nvtt::TextureType_2D;
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
@ -584,8 +601,6 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production);
// TODO: gles: generate ETC mips instead?
auto mipFormat = texture->getStoredMipFormat();
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) {
compressionOptions.setFormat(nvtt::Format_BC1);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) {
@ -669,21 +684,94 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
nvtt::Compressor compressor;
compressor.setTaskDispatcher(&dispatcher);
compressor.process(inputOptions, compressionOptions, outputOptions);
#else
int numMips = 1 + (int)log2(std::max(width, height));
Etc::RawImage *mipMaps = new Etc::RawImage[numMips];
Etc::Image::Format etcFormat = Etc::Image::Format::DEFAULT;
if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB) {
etcFormat = Etc::Image::Format::RGB8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
etcFormat = Etc::Image::Format::SRGB8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
etcFormat = Etc::Image::Format::RGB8A1;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA) {
etcFormat = Etc::Image::Format::SRGB8A1;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGBA) {
etcFormat = Etc::Image::Format::RGBA8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA) {
etcFormat = Etc::Image::Format::SRGBA8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED) {
etcFormat = Etc::Image::Format::R11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED_SIGNED) {
etcFormat = Etc::Image::Format::SIGNED_R11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY) {
etcFormat = Etc::Image::Format::RG11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY_SIGNED) {
etcFormat = Etc::Image::Format::SIGNED_RG11;
} else {
qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE();
return;
}
const Etc::ErrorMetric errorMetric = Etc::ErrorMetric::RGBA;
const float effort = 1.0f;
const int numEncodeThreads = 4;
int encodingTime;
const float MAX_COLOR = 255.0f;
std::vector<vec4> floatData;
floatData.resize(width * height);
for (int y = 0; y < height; y++) {
QRgb *line = (QRgb *) localCopy.scanLine(y);
for (int x = 0; x < width; x++) {
QRgb &pixel = line[x];
floatData[x + y * width] = vec4(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)) / MAX_COLOR;
}
}
// free up the memory afterward to avoid bloating the heap
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
Etc::EncodeMipmaps(
(float *)floatData.data(), width, height,
etcFormat, errorMetric, effort,
numEncodeThreads, numEncodeThreads,
numMips, Etc::FILTER_WRAP_NONE,
mipMaps, &encodingTime
);
for (int i = 0; i < numMips; i++) {
if (mipMaps[i].paucEncodingBits.get()) {
if (face >= 0) {
texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
} else {
texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
}
}
}
delete[] mipMaps;
#endif
}
#endif
void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
#if CPU_MIPMAPS
PROFILE_RANGE(resource_parse, "generateMips");
#ifndef USE_GLES
if (image.format() == QIMAGE_HDR_FORMAT) {
generateHDRMips(texture, std::move(image), abortProcessing, face);
} else {
generateLDRMips(texture, std::move(image), abortProcessing, face);
}
#else
generateLDRMips(texture, std::move(image), abortProcessing, face);
#endif
#else
texture->setAutoGenerateMips(true);
#endif
@ -738,7 +826,6 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
gpu::Element formatMip;
gpu::Element formatGPU;
if (isColorTexturesCompressionEnabled()) {
#ifndef USE_GLES
if (validAlpha) {
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures
// for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
@ -746,22 +833,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB;
}
#else
if (validAlpha) {
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
}
#endif
formatMip = formatGPU;
} else {
#ifdef USE_GLES
// GLES does not support GL_BGRA
formatMip = gpu::Element::COLOR_SRGBA_32;
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
formatMip = formatGPU;
#else
formatGPU = gpu::Element::COLOR_SRGBA_32;
formatMip = gpu::Element::COLOR_SBGRA_32;
#endif
formatGPU = gpu::Element::COLOR_SRGBA_32;
}
if (isStrict) {
@ -876,16 +956,18 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatMip = gpu::Element::VEC2NU8_XY;
gpu::Element formatGPU = gpu::Element::VEC2NU8_XY;
gpu::Element formatMip;
gpu::Element formatGPU;
if (isNormalTexturesCompressionEnabled()) {
#ifndef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
#else
} else {
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
#else
formatGPU = gpu::Element::VEC2NU8_XY;
#endif
formatMip = formatGPU;
}
formatMip = formatGPU;
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName);
@ -917,16 +999,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
gpu::Element formatMip;
gpu::Element formatGPU;
if (isGrayscaleTexturesCompressionEnabled()) {
#ifndef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
#else
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#endif
formatMip = formatGPU;
} else {
formatMip = gpu::Element::COLOR_R_8;
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#else
formatGPU = gpu::Element::COLOR_R_8;
#endif
}
formatMip = formatGPU;
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName);
@ -1283,19 +1364,25 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
QImage image = processSourceImage(std::move(localCopy), true);
if (image.format() != QIMAGE_HDR_FORMAT) {
#ifndef USE_GLES
image = convertToHDRFormat(std::move(image), HDR_FORMAT);
#else
image = image.convertToFormat(QImage::Format_RGB32);
#endif
}
gpu::Element formatMip;
gpu::Element formatGPU;
if (isCubeTexturesCompressionEnabled()) {
// TODO: gles: pick HDR ETC format
formatMip = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
} else {
formatMip = HDR_FORMAT;
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
#else
formatGPU = HDR_FORMAT;
#endif
}
formatMip = formatGPU;
// Find the layout of the cubemap in the 2D image
// Use the original image size since processSourceImage may have altered the size / aspect ratio
@ -1342,9 +1429,16 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
// Generate irradiance while we are at it
if (generateIrradiance) {
PROFILE_RANGE(resource_parse, "generateIrradiance");
auto irradianceTexture = gpu::Texture::createCube(HDR_FORMAT, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
gpu::Element irradianceFormat;
// TODO: we could locally compress the irradiance texture on Android, but we don't need to
#ifndef USE_GLES
irradianceFormat = HDR_FORMAT;
#else
irradianceFormat = gpu::Element::COLOR_SRGBA_32;
#endif
auto irradianceTexture = gpu::Texture::createCube(irradianceFormat, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
irradianceTexture->setSource(srcImageName);
irradianceTexture->setStoredMipFormat(HDR_FORMAT);
irradianceTexture->setStoredMipFormat(irradianceFormat);
for (uint8 face = 0; face < faces.size(); ++face) {
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
}

View file

@ -350,6 +350,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) {
if (idxMoveStartingPointCandidate != -1) {
_moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id();
_unusedTouches.erase(_moveCurrentTouchId);
thisPoint.x = tPoints[idxMoveStartingPointCandidate].pos().x();
thisPoint.y = tPoints[idxMoveStartingPointCandidate].pos().y();
moveTouchBegin(thisPoint);
} else {
moveTouchEnd();
@ -359,6 +361,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) {
if (idxViewStartingPointCandidate != -1) {
_viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id();
_unusedTouches.erase(_viewCurrentTouchId);
thisPoint.x = tPoints[idxViewStartingPointCandidate].pos().x();
thisPoint.y = tPoints[idxViewStartingPointCandidate].pos().y();
viewTouchBegin(thisPoint);
} else {
viewTouchEnd();
@ -368,6 +372,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) {
if (idxJumpStartingPointCandidate != -1) {
_jumpCurrentTouchId = tPoints[idxJumpStartingPointCandidate].id();
_unusedTouches.erase(_jumpCurrentTouchId);
thisPoint.x = tPoints[idxJumpStartingPointCandidate].pos().x();
thisPoint.y = tPoints[idxJumpStartingPointCandidate].pos().y();
jumpTouchBegin(thisPoint);
} else {
if (_jumpHasValidTouch) {
@ -424,6 +430,7 @@ void TouchscreenVirtualPadDevice::moveTouchBegin(glm::vec2 touchPoint) {
} else {
_moveRefTouchPoint = touchPoint;
}
_moveCurrentTouchPoint = touchPoint;
_moveHasValidTouch = true;
}
}

View file

@ -636,7 +636,10 @@ void Resource::attemptRequest() {
<< "- retrying asset load - attempt" << _attempts << " of " << MAX_ATTEMPTS;
}
ResourceCache::attemptRequest(_self);
auto self = _self.lock();
if (self) {
ResourceCache::attemptRequest(self);
}
}
void Resource::finishedLoading(bool success) {

View file

@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXReaderNodeReparenting);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FixMannequinDefaultAvatarFeet);
case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
// ICE packets

View file

@ -282,7 +282,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
AvatarIdentityLookAtSnapping,
UpdatedMannequinDefaultAvatar,
AvatarJointDefaultPoseFlags,
FBXReaderNodeReparenting
FBXReaderNodeReparenting,
FixMannequinDefaultAvatarFeet
};
enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -40,9 +40,10 @@ bool readOctreeFile(QString path, QJsonDocument* doc) {
}
bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) {
if (root.contains("Id") && root.contains("DataVersion")) {
if (root.contains("Id") && root.contains("DataVersion") && root.contains("Version")) {
id = root["Id"].toVariant().toUuid();
version = root["DataVersion"].toInt();
dataVersion = root["DataVersion"].toInt();
version = root["Version"].toInt();
}
readSubclassData(root);
return true;
@ -76,11 +77,10 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) {
}
QByteArray OctreeUtils::RawOctreeData::toByteArray() {
const auto protocolVersion = (int)versionForPacketType((PacketTypeEnum::Value)dataPacketType());
QJsonObject obj {
{ "DataVersion", QJsonValue((qint64)version) },
{ "DataVersion", QJsonValue((qint64)dataVersion) },
{ "Id", QJsonValue(id.toString()) },
{ "Version", protocolVersion },
{ "Version", QJsonValue((qint64)version) },
};
writeSubclassData(obj);
@ -111,8 +111,8 @@ PacketType OctreeUtils::RawOctreeData::dataPacketType() const {
void OctreeUtils::RawOctreeData::resetIdAndVersion() {
id = QUuid::createUuid();
version = OctreeUtils::INITIAL_VERSION;
qDebug() << "Reset octree data to: " << id << version;
dataVersion = OctreeUtils::INITIAL_VERSION;
qDebug() << "Reset octree data to: " << id << dataVersion;
}
void OctreeUtils::RawEntityData::readSubclassData(const QJsonObject& root) {

View file

@ -28,6 +28,7 @@ constexpr Version INITIAL_VERSION = 0;
class RawOctreeData {
public:
QUuid id { QUuid() };
Version dataVersion { -1 };
Version version { -1 };
virtual PacketType dataPacketType() const;

View file

@ -179,8 +179,8 @@ bool OctreePersistThread::process() {
OctreeUtils::RawOctreeData data;
if (data.readOctreeDataInfoFromFile(_filename)) {
qDebug() << "Setting entity version info to: " << data.id << data.version;
_tree->setOctreeVersionInfo(data.id, data.version);
qDebug() << "Setting entity version info to: " << data.id << data.dataVersion;
_tree->setOctreeVersionInfo(data.id, data.dataVersion);
}
bool persistentFileRead;

View file

@ -446,17 +446,13 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
// shouldSendUpdate() sould NOT be triggering updates to maintain the queryAACube of dynamic entities.
// The server is supposed to predict the transform of such moving things. The client performs a "double prediction"
// where it predicts what the the server is doing, and only sends updates whent the entity's true transform
// differs significantly. That is what the remoteSimulationOutOfSync() logic is all about.
if (_entity->dynamicDataNeedsTransmit() ||
(!_entity->getDynamic() && _entity->queryAACubeNeedsUpdate())) {
if (_entity->dynamicDataNeedsTransmit()) {
return true;
}
if (_entity->shouldSuppressLocationEdits()) {
return false;
// "shouldSuppressLocationEdits" really means: "the entity has a 'Hold' action therefore
// we don't need send an update unless the entity is not contained by its queryAACube"
return _entity->queryAACubeNeedsUpdate();
}
return remoteSimulationOutOfSync(simulationStep);

View file

@ -87,7 +87,8 @@ void CauterizedModel::createVisibleRenderItemSet() {
for (int partIndex = 0; partIndex < numParts; partIndex++) {
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
_modelMeshMaterialNames.push_back(getGeometry()->getShapeMaterial(shapeID)->getName());
auto material = getGeometry()->getShapeMaterial(shapeID);
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++;
}

View file

@ -19,7 +19,9 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render:
// Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling
// is performed, then casters not in the view frustum will be removed, which is not what we wish.
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, tagBits, tagMask);
if (isDeferred) {
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, tagBits, tagMask);
}
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask);
assert(items.canCast<RenderFetchCullSortTask::Output>());

View file

@ -149,6 +149,7 @@ public:
Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); }
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
Builder& withoutViewSpace() { _flags.reset(VIEW_SPACE); return (*this); }
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }

View file

@ -419,7 +419,7 @@ void ScriptEngine::waitTillDoneRunning() {
// Wait for the scripting thread to stop running, as
// flooding it with aborts/exceptions will persist it longer
static const auto MAX_SCRIPT_QUITTING_TIME = 0.5 * MSECS_PER_SECOND;
if (workerThread->wait(MAX_SCRIPT_QUITTING_TIME)) {
if (!workerThread->wait(MAX_SCRIPT_QUITTING_TIME)) {
workerThread->terminate();
}
}

View file

@ -20,6 +20,16 @@ const float DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD = 0.11f; // meters
const float DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD = 0.185f; // meters
const float DEFAULT_AVATAR_NECK_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD;
const float DEFAULT_AVATAR_EYE_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
const float DEFAULT_AVATAR_SUPPORT_BASE_LEFT = -0.25f;
const float DEFAULT_AVATAR_SUPPORT_BASE_RIGHT = 0.25f;
const float DEFAULT_AVATAR_SUPPORT_BASE_FRONT = -0.20f;
const float DEFAULT_AVATAR_SUPPORT_BASE_BACK = 0.10f;
const float DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR = 0.5f;
const float DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR = 2.0f;
const float DEFAULT_AVATAR_HIPS_MASS = 40.0f;
const float DEFAULT_AVATAR_HEAD_MASS = 20.0f;
const float DEFAULT_AVATAR_LEFTHAND_MASS = 2.0f;
const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
// Used when avatar is missing joints... (avatar space)
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };

View file

@ -574,8 +574,9 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda
vAxisOut = glm::cross(wAxisOut, uAxisOut);
}
// assumes z-forward and y-up
glm::vec2 getFacingDir2D(const glm::quat& rot) {
glm::vec3 facing3D = rot * Vectors::UNIT_NEG_Z;
glm::vec3 facing3D = rot * Vectors::UNIT_Z;
glm::vec2 facing2D(facing3D.x, facing3D.z);
const float ALMOST_ZERO = 0.0001f;
if (glm::length(facing2D) < ALMOST_ZERO) {
@ -585,8 +586,9 @@ glm::vec2 getFacingDir2D(const glm::quat& rot) {
}
}
// assumes z-forward and y-up
glm::vec2 getFacingDir2D(const glm::mat4& m) {
glm::vec3 facing3D = transformVectorFast(m, Vectors::UNIT_NEG_Z);
glm::vec3 facing3D = transformVectorFast(m, Vectors::UNIT_Z);
glm::vec2 facing2D(facing3D.x, facing3D.z);
const float ALMOST_ZERO = 0.0001f;
if (glm::length(facing2D) < ALMOST_ZERO) {

View file

@ -250,7 +250,10 @@ glm::vec3 transformVectorFull(const glm::mat4& m, const glm::vec3& v);
void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& secondaryAxis,
glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut);
// assumes z-forward and y-up
glm::vec2 getFacingDir2D(const glm::quat& rot);
// assumes z-forward and y-up
glm::vec2 getFacingDir2D(const glm::mat4& m);
inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }

View file

@ -32,15 +32,13 @@
#include "shared/GlobalAppProperties.h"
#include "SharedUtil.h"
// Format: AppName-PID-Timestamp
// Example: ...
QString TEMP_DIR_FORMAT { "%1-%2-%3" };
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
static bool USE_SOURCE_TREE_RESOURCES() {
#if defined(Q_OS_OSX)
return true;
#else
static bool result = false;
static std::once_flag once;
std::call_once(once, [&] {
@ -48,10 +46,28 @@ static bool USE_SOURCE_TREE_RESOURCES() {
result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG);
});
return result;
#endif
}
#endif
const QString& PathUtils::getRccPath() {
static QString rccLocation;
static std::once_flag once;
std::call_once(once, [&] {
static const QString rccName{ "/resources.rcc" };
#if defined(Q_OS_OSX)
char buffer[8192];
uint32_t bufferSize = sizeof(buffer);
_NSGetExecutablePath(buffer, &bufferSize);
rccLocation = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + rccName;
#elif defined(Q_OS_ANDROID)
rccLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + rccName;
#else
rccLocation = QCoreApplication::applicationDirPath() + rccName;
#endif
});
return rccLocation;
}
#ifdef DEV_BUILD
const QString& PathUtils::projectRootPath() {
static QString sourceFolder;
@ -65,23 +81,9 @@ const QString& PathUtils::projectRootPath() {
#endif
const QString& PathUtils::resourcesPath() {
static QString staticResourcePath;
static QString staticResourcePath{ ":/" };
static std::once_flag once;
std::call_once(once, [&]{
#if defined(Q_OS_OSX)
// FIXME fix the OSX installer to install the resources.rcc instead of the
// individual resource files
// FIXME the first call to fetch the resources location seems to return
// nothing for QCoreApplication::applicationDirPath()
char buffer[8192];
uint32_t bufferSize = sizeof(buffer);
_NSGetExecutablePath(buffer, &bufferSize);
staticResourcePath = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + "/";
#else
staticResourcePath = ":/";
#endif
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree
@ -90,21 +92,13 @@ const QString& PathUtils::resourcesPath() {
#endif
qDebug() << "Resource path resolved to " << staticResourcePath;
});
return staticResourcePath;
}
const QString& PathUtils::resourcesUrl() {
static QString staticResourcePath;
static QString staticResourcePath{ "qrc:///" };
static std::once_flag once;
std::call_once(once, [&]{
#if defined(Q_OS_OSX)
staticResourcePath = QUrl::fromLocalFile(resourcesPath()).toString();
#else
staticResourcePath = "qrc:///";
#endif
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree

View file

@ -37,6 +37,7 @@ class PathUtils : public QObject, public Dependency {
Q_PROPERTY(QString resources READ resourcesPath CONSTANT)
Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT)
public:
static const QString& getRccPath();
static const QString& resourcesUrl();
static QUrl resourcesUrl(const QString& relative);
static const QString& resourcesPath();

View file

@ -127,9 +127,29 @@ public:
double getCPURunTime() const { return _msCPURunTime; }
// Describe the node graph data connections of the associated Job/Task
/**jsdoc
* @function Render.isTask
* @returns {boolean}
*/
Q_INVOKABLE virtual bool isTask() const { return false; }
/**jsdoc
* @function Render.getSubConfigs
* @returns {object[]}
*/
Q_INVOKABLE virtual QObjectList getSubConfigs() const { return QObjectList(); }
/**jsdoc
* @function Render.getNumSubs
* @returns {number}
*/
Q_INVOKABLE virtual int getNumSubs() const { return 0; }
/**jsdoc
* @function Render.getSubConfig
* @param {number} index
* @returns {object}
*/
Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; }
public slots:

Some files were not shown because too many files have changed in this diff Show more