mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 23:12:16 +02:00
merging from upstream
This commit is contained in:
commit
7037e14d09
290 changed files with 4369 additions and 2980 deletions
|
@ -81,7 +81,11 @@ if (ANDROID)
|
|||
set(GLES_OPTION ON)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
|
||||
if (${HIFI_ANDROID_APP} STREQUAL "questInterface")
|
||||
if (
|
||||
(${HIFI_ANDROID_APP} STREQUAL "questInterface") OR
|
||||
(${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR
|
||||
(${HIFI_ANDROID_APP} STREQUAL "framePlayer")
|
||||
)
|
||||
# We know the quest hardware has this extension, so we can force the use of instanced stereo
|
||||
add_definitions(-DHAVE_EXT_clip_cull_distance)
|
||||
# We can also use multiview stereo techniques
|
||||
|
@ -89,9 +93,15 @@ if (ANDROID)
|
|||
add_definitions(-DHAVE_OVR_multiview)
|
||||
# We can also use our own foveated textures
|
||||
add_definitions(-DHAVE_QCOM_texture_foveated)
|
||||
|
||||
# if set, the application itself or some library it depends on MUST implement
|
||||
# `DisplayPluginList getDisplayPlugins()` and `InputPluginList getInputPlugins()`
|
||||
add_definitions(-DCUSTOM_INPUT_PLUGINS)
|
||||
add_definitions(-DCUSTOM_DISPLAY_PLUGINS)
|
||||
set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin)
|
||||
endif()
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
|
||||
endif ()
|
||||
|
||||
if (USE_GLES AND (NOT ANDROID))
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -54,6 +55,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
public static final String DOMAIN_URL = "url";
|
||||
public static final String EXTRA_GOTO_USERNAME = "gotousername";
|
||||
private static final String TAG = "Interface";
|
||||
public static final String EXTRA_ARGS = "args";
|
||||
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
|
||||
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
|
||||
private static final int NORMAL_DPI = 160;
|
||||
|
@ -78,6 +80,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
|
||||
private boolean nativeEnterBackgroundCallEnqueued = false;
|
||||
private SlidingDrawer mWebSlidingDrawer;
|
||||
private boolean mStartInDomain;
|
||||
// private GvrApi gvrApi;
|
||||
// Opaque native pointer to the Application C++ object.
|
||||
// This object is owned by the InterfaceActivity instance and passed to the native methods.
|
||||
|
@ -93,8 +96,14 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.isLoading = true;
|
||||
Intent intent = getIntent();
|
||||
if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
|
||||
if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) {
|
||||
intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
|
||||
} else if (intent.hasExtra(EXTRA_ARGS)) {
|
||||
String args = intent.getStringExtra(EXTRA_ARGS);
|
||||
if (!TextUtils.isEmpty(args)) {
|
||||
mStartInDomain = true;
|
||||
intent.putExtra("applicationArguments", args);
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
@ -125,7 +134,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
getActionBar().hide();
|
||||
}
|
||||
});
|
||||
startActivity(new Intent(this, SplashActivity.class));
|
||||
Intent splashIntent = new Intent(this, SplashActivity.class);
|
||||
splashIntent.putExtra(SplashActivity.EXTRA_START_IN_DOMAIN, mStartInDomain);
|
||||
startActivity(splashIntent);
|
||||
|
||||
mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
|
||||
headsetStateReceiver = new HeadsetStateReceiver();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package io.highfidelity.hifiinterface;
|
||||
|
||||
import android.app.Activity;
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -27,9 +28,14 @@ public class PermissionChecker extends Activity {
|
|||
private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
|
||||
private static final String TAG = "Interface";
|
||||
|
||||
private static final String EXTRA_ARGS = "args";
|
||||
private String mArgs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mArgs =(getIntent().getStringExtra(EXTRA_ARGS));
|
||||
|
||||
Intent myIntent = new Intent(this, BreakpadUploaderService.class);
|
||||
startService(myIntent);
|
||||
if (CHOOSE_AVATAR_ON_STARTUP) {
|
||||
|
@ -76,6 +82,11 @@ public class PermissionChecker extends Activity {
|
|||
|
||||
private void launchActivityWithPermissions(){
|
||||
Intent i = new Intent(this, InterfaceActivity.class);
|
||||
|
||||
if (!TextUtils.isEmpty(mArgs)) {
|
||||
i.putExtra(EXTRA_ARGS, mArgs);
|
||||
}
|
||||
|
||||
startActivity(i);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ import android.view.View;
|
|||
|
||||
public class SplashActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_START_IN_DOMAIN = "start-in-domain";
|
||||
private boolean mStartInDomain;
|
||||
|
||||
private native void registerLoadCompleteListener();
|
||||
|
||||
@Override
|
||||
|
@ -36,13 +39,27 @@ public class SplashActivity extends Activity {
|
|||
}
|
||||
|
||||
public void onAppLoadedComplete() {
|
||||
if (HifiUtils.getInstance().isUserLoggedIn()) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
} else {
|
||||
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
|
||||
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
|
||||
startActivity(menuIntent);
|
||||
if (!mStartInDomain) {
|
||||
if (HifiUtils.getInstance().isUserLoggedIn()) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
} else {
|
||||
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
|
||||
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
|
||||
startActivity(menuIntent);
|
||||
}
|
||||
}
|
||||
SplashActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(EXTRA_START_IN_DOMAIN, mStartInDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mStartInDomain = savedInstanceState.getBoolean(EXTRA_START_IN_DOMAIN, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ android {
|
|||
'-DHIFI_ANDROID_APP=questFramePlayer',
|
||||
'-DANDROID_TOOLCHAIN=clang',
|
||||
'-DANDROID_STL=c++_shared',
|
||||
|
||||
'-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||
targets = ['questFramePlayer']
|
||||
}
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
import de.undercouch.gradle.tasks.download.Download
|
||||
import de.undercouch.gradle.tasks.download.Verify
|
||||
import groovy.io.FileType
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.xml.XmlUtil
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.0'
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'de.undercouch.download' version '3.3.0'
|
||||
id "cz.malohlava" version "1.0.3"
|
||||
id "io.github.http-builder-ng.http-plugin" version "0.1.1"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
|
@ -18,6 +32,10 @@ allprojects {
|
|||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
ext {
|
||||
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
|
||||
VERSION_CODE = project.hasProperty('VERSION_CODE') ? project.getProperty('VERSION_CODE') : '0'
|
||||
|
@ -126,6 +144,108 @@ task setupDependencies() {
|
|||
task cleanDependencies(type: Delete) {
|
||||
}
|
||||
|
||||
def runBreakpadDumpSyms = { buildType ->
|
||||
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
|
||||
|
||||
def objDir = new File("${appDir}/build/intermediates/cmake/${buildType}/obj/arm64-v8a")
|
||||
def stripDebugSymbol = "${appDir}/build/intermediates/transforms/stripDebugSymbol/${buildType}/0/lib/arm64-v8a/"
|
||||
def outputDir = new File(breakpadDumpSymsDir, buildType)
|
||||
if (!outputDir.exists()) {
|
||||
outputDir.mkdirs()
|
||||
}
|
||||
|
||||
objDir.eachFileRecurse (FileType.FILES) { file ->
|
||||
if (file.name.endsWith('.so')) {
|
||||
def output = file.name + ".sym"
|
||||
def cmdArgs = [
|
||||
file.toString(),
|
||||
stripDebugSymbol
|
||||
]
|
||||
def result = exec {
|
||||
workingDir HIFI_ANDROID_PRECOMPILED + '/breakpad/bin'
|
||||
commandLine './dump_syms'
|
||||
args cmdArgs
|
||||
ignoreExitValue true
|
||||
standardOutput = new BufferedOutputStream(new FileOutputStream(new File(outputDir, output)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task runBreakpadDumpSymsDebug() {
|
||||
doLast {
|
||||
runBreakpadDumpSyms("debug");
|
||||
}
|
||||
}
|
||||
|
||||
task runBreakpadDumpSymsRelease() {
|
||||
doLast {
|
||||
runBreakpadDumpSyms("release");
|
||||
}
|
||||
}
|
||||
|
||||
task zipDumpSymsDebug(type: Zip, dependsOn: runBreakpadDumpSymsDebug) {
|
||||
from (new File(breakpadDumpSymsDir, "debug").absolutePath)
|
||||
archiveName "symbols-${RELEASE_NUMBER}-debug.zip"
|
||||
destinationDir(new File("${appDir}/build/tmp/"))
|
||||
}
|
||||
|
||||
task zipDumpSymsRelease(type: Zip, dependsOn: runBreakpadDumpSymsRelease) {
|
||||
from (new File(breakpadDumpSymsDir, "release").absolutePath)
|
||||
archiveName "symbols-${RELEASE_NUMBER}-release.zip"
|
||||
destinationDir(new File("${appDir}/build/tmp/"))
|
||||
}
|
||||
|
||||
task uploadBreakpadDumpSymsDebug(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsDebug) {
|
||||
onlyIf {
|
||||
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
|
||||
}
|
||||
config {
|
||||
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
|
||||
}
|
||||
post {
|
||||
request.uri.path = '/post'
|
||||
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
|
||||
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-debug.zip").bytes
|
||||
request.contentType = 'application/octet-stream'
|
||||
response.success {
|
||||
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-debug.zip uploaded")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task uploadBreakpadDumpSymsRelease(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsRelease) {
|
||||
onlyIf {
|
||||
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
|
||||
}
|
||||
config {
|
||||
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
|
||||
}
|
||||
post {
|
||||
request.uri.path = '/post'
|
||||
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
|
||||
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-release.zip").bytes
|
||||
request.contentType = 'application/octet-stream'
|
||||
response.success {
|
||||
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-release.zip uploaded")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task renameHifiACTaskDebug() {
|
||||
doLast {
|
||||
def sourceFile = new File("${appDir}/build/intermediates/cmake/debug/obj/arm64-v8a/","libhifiCodec.so")
|
||||
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
|
||||
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
|
||||
}
|
||||
}
|
||||
task renameHifiACTaskRelease(type: Copy) {
|
||||
doLast {
|
||||
def sourceFile = new File("${appDir}/build/intermediates/cmake/release/obj/arm64-v8a/","libhifiCodec.so")
|
||||
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
|
||||
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
|
||||
// See the comment on the qtBundle task above
|
||||
|
|
|
@ -12,8 +12,8 @@ project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
|
|||
// Applications
|
||||
//
|
||||
|
||||
//include ':interface'
|
||||
//project(':interface').projectDir = new File(settingsDir, 'apps/interface')
|
||||
include ':interface'
|
||||
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
|
||||
|
||||
include ':questInterface'
|
||||
project(':questInterface').projectDir = new File(settingsDir, 'apps/questInterface')
|
||||
|
@ -22,8 +22,8 @@ project(':questInterface').projectDir = new File(settingsDir, 'apps/questInterfa
|
|||
// Test projects
|
||||
//
|
||||
|
||||
//include ':framePlayer'
|
||||
//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
|
||||
include ':framePlayer'
|
||||
project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
|
||||
|
||||
//include ':questFramePlayer'
|
||||
//project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
|
||||
include ':questFramePlayer'
|
||||
project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
|
||||
|
|
|
@ -38,6 +38,19 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
|||
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
|
||||
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
||||
|
||||
const QRegularExpression AvatarMixer::suffixedNamePattern { R"(^\s*(.+)\s*_(\d)+\s*$)" };
|
||||
|
||||
// Lexicographic comparison:
|
||||
bool AvatarMixer::SessionDisplayName::operator<(const SessionDisplayName& rhs) const {
|
||||
if (_baseName < rhs._baseName) {
|
||||
return true;
|
||||
} else if (rhs._baseName < _baseName) {
|
||||
return false;
|
||||
} else {
|
||||
return _suffix < rhs._suffix;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_slavePool(&_slaveSharedData)
|
||||
|
@ -313,27 +326,40 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
|||
bool sendIdentity = false;
|
||||
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
|
||||
if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) {
|
||||
_sessionDisplayNames.remove(existingBaseDisplayName);
|
||||
const QString& existingBaseDisplayName = nodeData->getAvatar().getSessionDisplayName();
|
||||
if (!existingBaseDisplayName.isEmpty()) {
|
||||
SessionDisplayName existingDisplayName { existingBaseDisplayName };
|
||||
|
||||
auto suffixMatch = suffixedNamePattern.match(existingBaseDisplayName);
|
||||
if (suffixMatch.hasMatch()) {
|
||||
existingDisplayName._baseName = suffixMatch.captured(1);
|
||||
existingDisplayName._suffix = suffixMatch.captured(2).toInt();
|
||||
}
|
||||
_sessionDisplayNames.erase(existingDisplayName);
|
||||
}
|
||||
|
||||
QString baseName = avatar.getDisplayName().trimmed();
|
||||
const QRegularExpression curses { "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
|
||||
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
|
||||
const QRegularExpression trailingDigits { "\\s*(_\\d+\\s*)?(\\s*\\n[^$]*)?$" }; // trailing whitespace "_123" and any subsequent lines
|
||||
static const QRegularExpression trailingDigits { R"(\s*(_\d+\s*)?(\s*\n[^$]*)?$)" }; // trailing whitespace "_123" and any subsequent lines
|
||||
baseName = baseName.remove(trailingDigits);
|
||||
if (baseName.isEmpty()) {
|
||||
baseName = "anonymous";
|
||||
}
|
||||
|
||||
QPair<int, int>& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want.
|
||||
int& highWater = soFar.first;
|
||||
nodeData->setBaseDisplayName(baseName);
|
||||
QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName;
|
||||
SessionDisplayName newDisplayName { baseName };
|
||||
auto nameIter = _sessionDisplayNames.lower_bound(newDisplayName);
|
||||
if (nameIter != _sessionDisplayNames.end() && nameIter->_baseName == baseName) {
|
||||
// Existing instance(s) of name; find first free suffix
|
||||
while (*nameIter == newDisplayName && ++newDisplayName._suffix && ++nameIter != _sessionDisplayNames.end())
|
||||
;
|
||||
}
|
||||
|
||||
_sessionDisplayNames.insert(newDisplayName);
|
||||
QString sessionDisplayName = (newDisplayName._suffix > 0) ? baseName + "_" + QString::number(newDisplayName._suffix) : baseName;
|
||||
avatar.setSessionDisplayName(sessionDisplayName);
|
||||
highWater++;
|
||||
soFar.second++; // refcount
|
||||
nodeData->setBaseDisplayName(baseName);
|
||||
|
||||
nodeData->flagIdentityChange();
|
||||
nodeData->setAvatarSessionDisplayNameMustChange(false);
|
||||
sendIdentity = true;
|
||||
|
@ -409,10 +435,19 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
|
|||
{ // decrement sessionDisplayNames table and possibly remove
|
||||
QMutexLocker nodeDataLocker(&avatarNode->getLinkedData()->getMutex());
|
||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||
const QString& baseDisplayName = nodeData->getBaseDisplayName();
|
||||
// No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case.
|
||||
if (--_sessionDisplayNames[baseDisplayName].second <= 0) {
|
||||
_sessionDisplayNames.remove(baseDisplayName);
|
||||
const QString& displayName = nodeData->getAvatar().getSessionDisplayName();
|
||||
SessionDisplayName exitingDisplayName { displayName };
|
||||
|
||||
auto suffixMatch = suffixedNamePattern.match(displayName);
|
||||
if (suffixMatch.hasMatch()) {
|
||||
exitingDisplayName._baseName = suffixMatch.captured(1);
|
||||
exitingDisplayName._suffix = suffixMatch.captured(2).toInt();
|
||||
}
|
||||
auto displayNameIter = _sessionDisplayNames.find(exitingDisplayName);
|
||||
if (displayNameIter == _sessionDisplayNames.end()) {
|
||||
qCDebug(avatars) << "Exiting avatar displayname" << displayName << "not found";
|
||||
} else {
|
||||
_sessionDisplayNames.erase(displayNameIter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef hifi_AvatarMixer_h
|
||||
#define hifi_AvatarMixer_h
|
||||
|
||||
#include <set>
|
||||
#include <shared/RateCounter.h>
|
||||
#include <PortableHighResolutionClock.h>
|
||||
|
||||
|
@ -88,7 +89,24 @@ private:
|
|||
|
||||
RateCounter<> _broadcastRate;
|
||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||
QHash<QString, QPair<int, int>> _sessionDisplayNames;
|
||||
|
||||
// Pair of basename + uniquifying integer suffix.
|
||||
struct SessionDisplayName {
|
||||
explicit SessionDisplayName(QString baseName = QString(), int suffix = 0) :
|
||||
_baseName(baseName),
|
||||
_suffix(suffix) { }
|
||||
// Does lexicographic ordering:
|
||||
bool operator<(const SessionDisplayName& rhs) const;
|
||||
bool operator==(const SessionDisplayName& rhs) const {
|
||||
return _baseName == rhs._baseName && _suffix == rhs._suffix;
|
||||
}
|
||||
|
||||
QString _baseName;
|
||||
int _suffix;
|
||||
};
|
||||
static const QRegularExpression suffixedNamePattern;
|
||||
|
||||
std::set<SessionDisplayName> _sessionDisplayNames;
|
||||
|
||||
quint64 _displayNameManagementElapsedTime { 0 }; // total time spent in broadcastAvatarData/display name management... since last stats window
|
||||
quint64 _ignoreCalculationElapsedTime { 0 };
|
||||
|
|
|
@ -111,7 +111,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
||||
|
||||
startNewTraversal(newView, root);
|
||||
startNewTraversal(newView, root, isFullScene);
|
||||
|
||||
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
|
||||
// and also use the opportunity to cull anything no longer in view
|
||||
|
@ -220,9 +220,10 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
|
|||
return hasNewChild || hasNewDescendants;
|
||||
}
|
||||
|
||||
void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
|
||||
void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root,
|
||||
bool forceFirstPass) {
|
||||
|
||||
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
|
||||
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, forceFirstPass);
|
||||
// there are three types of traversal:
|
||||
//
|
||||
// (1) FirstTime = at login --> find everything in view
|
||||
|
|
|
@ -42,7 +42,7 @@ private:
|
|||
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
|
||||
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
|
||||
|
||||
void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
|
||||
void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root, bool forceFirstPass = false);
|
||||
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
|
||||
|
||||
void preDistributionProcessing() override;
|
||||
|
|
36
cmake/macros/FixupNitpick.cmake
Normal file
36
cmake/macros/FixupNitpick.cmake
Normal file
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# FixupNitpick.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2019 High Fidelity, Inc.
|
||||
# Created by Nissim Hadar on January 14th, 2016
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(fixup_nitpick)
|
||||
if (APPLE)
|
||||
string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${NITPICK_BUNDLE_NAME})
|
||||
string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${NITPICK_INSTALL_DIR})
|
||||
set(_NITPICK_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
|
||||
|
||||
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
|
||||
|
||||
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
|
||||
message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
|
||||
It is required to produce a relocatable nitpick application.\
|
||||
Check that the environment variable QT_DIR points to your Qt installation.\
|
||||
")
|
||||
endif ()
|
||||
|
||||
install(CODE "
|
||||
execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
|
||||
\${CMAKE_INSTALL_PREFIX}/${_NITPICK_INSTALL_PATH}/\
|
||||
-verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
|
||||
)"
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
)
|
||||
|
||||
endif ()
|
||||
endmacro()
|
|
@ -77,6 +77,9 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
add_definitions(-DDEV_BUILD)
|
||||
endif ()
|
||||
|
||||
set(NITPICK_BUNDLE_NAME "nitpick")
|
||||
set(NITPICK_ICON_PREFIX "nitpick")
|
||||
|
||||
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
|
||||
|
||||
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
|
||||
|
@ -140,8 +143,9 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
|
||||
|
||||
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
|
||||
if (CLIENT_ONLY)
|
||||
set(CONSOLE_EXEC_NAME "Console.app")
|
||||
|
@ -159,11 +163,14 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
|
||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
|
||||
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.icns")
|
||||
else ()
|
||||
if (WIN32)
|
||||
set(CONSOLE_INSTALL_DIR "server-console")
|
||||
set(NITPICK_INSTALL_DIR "nitpick")
|
||||
else ()
|
||||
set(CONSOLE_INSTALL_DIR ".")
|
||||
set(NITPICK_INSTALL_DIR ".")
|
||||
endif ()
|
||||
|
||||
set(COMPONENT_INSTALL_DIR ".")
|
||||
|
@ -173,6 +180,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
if (WIN32)
|
||||
set(INTERFACE_EXEC_PREFIX "interface")
|
||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.ico")
|
||||
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico")
|
||||
|
||||
set(CONSOLE_EXEC_NAME "server-console.exe")
|
||||
|
||||
|
|
|
@ -202,10 +202,6 @@ if (WIN32)
|
|||
set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF")
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
set(PLATFORM_DISPLAY_PLUGINS oculusMobile oculusMobilePlugin)
|
||||
endif()
|
||||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(
|
||||
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
|
||||
|
@ -217,8 +213,8 @@ link_hifi_libraries(
|
|||
ui-plugins display-plugins input-plugins
|
||||
# Platform specific GL libraries
|
||||
${PLATFORM_GL_BACKEND}
|
||||
# Plaform specific display plugins libraries
|
||||
${PLATFORM_DISPLAY_PLUGINS}
|
||||
# Plaform specific input & display plugin libraries
|
||||
${PLATFORM_PLUGIN_LIBRARIES}
|
||||
shaders
|
||||
)
|
||||
|
||||
|
@ -283,7 +279,7 @@ target_link_libraries(
|
|||
${TARGET_NAME}
|
||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets
|
||||
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
||||
Qt5::WebChannel
|
||||
Qt5::WebChannel
|
||||
${PLATFORM_QT_LIBRARIES}
|
||||
)
|
||||
|
||||
|
@ -337,7 +333,11 @@ if (APPLE)
|
|||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources/fonts"
|
||||
"${RESOURCES_DEV_DIR}/fonts"
|
||||
# add redirect json to macOS builds.
|
||||
#copy serverless for android
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources/serverless"
|
||||
"${RESOURCES_DEV_DIR}/serverless"
|
||||
# add redirect json to macOS builds.
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
|
||||
"${RESOURCES_DEV_DIR}/serverless/redirect.json"
|
||||
|
|
BIN
interface/resources/images/unsupportedImage.png
Normal file
BIN
interface/resources/images/unsupportedImage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -10,10 +10,6 @@ joint = jointRoot = Hips
|
|||
joint = jointLeftHand = LeftHand
|
||||
joint = jointRightHand = RightHand
|
||||
joint = jointHead = Head
|
||||
freeJoint = LeftArm
|
||||
freeJoint = LeftForeArm
|
||||
freeJoint = RightArm
|
||||
freeJoint = RightForeArm
|
||||
bs = JawOpen = mouth_Open = 1
|
||||
bs = LipsFunnel = Oo = 1
|
||||
bs = BrowsU_L = brow_Up = 1
|
||||
|
|
|
@ -1,275 +0,0 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.5
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import "qrc:////qml//windows"
|
||||
|
||||
ScrollingWindow {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
//HifiStyles.HifiConstants { id: hifistyles }
|
||||
title: "Browser"
|
||||
resizable: true
|
||||
destroyOnHidden: true
|
||||
width: 800
|
||||
height: 600
|
||||
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
|
||||
property alias url: webview.url
|
||||
property alias webView: webview
|
||||
|
||||
signal loadingChanged(int status)
|
||||
|
||||
x: 100
|
||||
y: 100
|
||||
|
||||
Component.onCompleted: {
|
||||
focus = true
|
||||
shown = true
|
||||
addressBar.text = webview.url
|
||||
}
|
||||
|
||||
function setProfile(profile) {
|
||||
webview.profile = profile;
|
||||
}
|
||||
|
||||
function showPermissionsBar(){
|
||||
permissionsContainer.visible=true;
|
||||
}
|
||||
|
||||
function hidePermissionsBar(){
|
||||
permissionsContainer.visible=false;
|
||||
}
|
||||
|
||||
function allowPermissions(){
|
||||
webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
|
||||
hidePermissionsBar();
|
||||
}
|
||||
|
||||
function setAutoAdd(auto) {
|
||||
desktop.setAutoAdd(auto);
|
||||
}
|
||||
|
||||
Item {
|
||||
id:item
|
||||
width: pane.contentWidth
|
||||
implicitHeight: pane.scrollHeight
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
spacing: 4
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
HiFiGlyphs {
|
||||
id: back;
|
||||
enabled: webview.canGoBack;
|
||||
text: hifi.glyphs.backward
|
||||
color: enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
size: 48
|
||||
MouseArea { anchors.fill: parent; onClicked: webview.goBack() }
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: forward;
|
||||
enabled: webview.canGoForward;
|
||||
text: hifi.glyphs.forward
|
||||
color: enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
size: 48
|
||||
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: reload;
|
||||
enabled: webview.canGoForward;
|
||||
text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
|
||||
color: enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
size: 48
|
||||
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
id: border
|
||||
height: 48
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.left: buttons.right
|
||||
anchors.leftMargin: 8
|
||||
|
||||
Item {
|
||||
id: barIcon
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
Image {
|
||||
source: webview.icon;
|
||||
x: (parent.height - height) / 2
|
||||
y: (parent.width - width) / 2
|
||||
sourceSize: Qt.size(width, height);
|
||||
verticalAlignment: Image.AlignVCenter;
|
||||
horizontalAlignment: Image.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: addressBar
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.left: barIcon.right
|
||||
anchors.leftMargin: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
focus: true
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
placeholderText: "Enter URL"
|
||||
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
|
||||
Keys.onPressed: {
|
||||
switch(event.key) {
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
if (text.indexOf("http") != 0) {
|
||||
text = "http://" + text;
|
||||
}
|
||||
root.hidePermissionsBar();
|
||||
root.keyboardRaised = false;
|
||||
webview.url = text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id:permissionsContainer
|
||||
visible:false
|
||||
color: "#000000"
|
||||
width: parent.width
|
||||
anchors.top: buttons.bottom
|
||||
height:40
|
||||
z:100
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "black" }
|
||||
GradientStop { position: 1.0; color: "grey" }
|
||||
}
|
||||
|
||||
RalewayLight {
|
||||
id: permissionsInfo
|
||||
anchors.right:permissionsRow.left
|
||||
anchors.rightMargin: 32
|
||||
anchors.topMargin:8
|
||||
anchors.top:parent.top
|
||||
text: "This site wants to use your microphone/camera"
|
||||
size: 18
|
||||
color: hifi.colors.white
|
||||
}
|
||||
|
||||
Row {
|
||||
id: permissionsRow
|
||||
spacing: 4
|
||||
anchors.top:parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.right: parent.right
|
||||
visible: true
|
||||
z:101
|
||||
|
||||
Button {
|
||||
id:allow
|
||||
text: "Allow"
|
||||
color: hifi.buttons.blue
|
||||
colorScheme: root.colorScheme
|
||||
width: 120
|
||||
enabled: true
|
||||
onClicked: root.allowPermissions();
|
||||
z:101
|
||||
}
|
||||
|
||||
Button {
|
||||
id:block
|
||||
text: "Block"
|
||||
color: hifi.buttons.red
|
||||
colorScheme: root.colorScheme
|
||||
width: 120
|
||||
enabled: true
|
||||
onClicked: root.hidePermissionsBar();
|
||||
z:101
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebView {
|
||||
id: webview
|
||||
url: "https://highfidelity.com/"
|
||||
profile: FileTypeProfile;
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
anchors.top: buttons.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
if (feature == 2) { // QWebEnginePage::MediaAudioCapture
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
} else {
|
||||
permissionsBar.securityOrigin = securityOrigin;
|
||||
permissionsBar.feature = feature;
|
||||
root.showPermissionsBar();
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
||||
addressBar.text = loadRequest.url
|
||||
}
|
||||
root.loadingChanged(loadRequest.status);
|
||||
}
|
||||
|
||||
onWindowCloseRequested: {
|
||||
root.destroy();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
desktop.initWebviewProfileHandlers(webview.profile);
|
||||
}
|
||||
}
|
||||
|
||||
} // item
|
||||
|
||||
|
||||
Keys.onPressed: {
|
||||
switch(event.key) {
|
||||
case Qt.Key_L:
|
||||
if (event.modifiers == Qt.ControlModifier) {
|
||||
event.accepted = true
|
||||
addressBar.selectAll()
|
||||
addressBar.forceActiveFocus()
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // dialog
|
58
interface/resources/qml/+webengine/BrowserWebView.qml
Normal file
58
interface/resources/qml/+webengine/BrowserWebView.qml
Normal file
|
@ -0,0 +1,58 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.5
|
||||
|
||||
import controlsUit 1.0
|
||||
|
||||
WebView {
|
||||
id: webview
|
||||
url: "https://highfidelity.com/"
|
||||
profile: FileTypeProfile;
|
||||
|
||||
property var parentRoot: null
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
if (feature == 2) { // QWebEnginePage::MediaAudioCapture
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
} else {
|
||||
permissionsBar.securityOrigin = securityOrigin;
|
||||
permissionsBar.feature = feature;
|
||||
parentRoot.showPermissionsBar();
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
||||
addressBar.text = loadRequest.url
|
||||
}
|
||||
parentRoot.loadingChanged(loadRequest.status);
|
||||
}
|
||||
|
||||
onWindowCloseRequested: {
|
||||
parentRoot.destroy();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
desktop.initWebviewProfileHandlers(webview.profile);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
//
|
||||
// InfoView.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 27 Apr 2015
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import Hifi 1.0 as Hifi
|
||||
|
||||
import controlsUit 1.0
|
||||
import "qrc:////qml//windows" as Windows
|
||||
|
||||
Windows.ScrollingWindow {
|
||||
id: root
|
||||
width: 800
|
||||
height: 800
|
||||
resizable: true
|
||||
|
||||
Hifi.InfoView {
|
||||
id: infoView
|
||||
width: pane.contentWidth
|
||||
implicitHeight: pane.scrollHeight
|
||||
|
||||
WebView {
|
||||
id: webview
|
||||
objectName: "WebView"
|
||||
anchors.fill: parent
|
||||
url: infoView.url
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
centerWindow(root);
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
centerWindow(root);
|
||||
}
|
||||
}
|
||||
|
||||
function centerWindow() {
|
||||
desktop.centerOnVisible(root);
|
||||
}
|
||||
|
||||
}
|
53
interface/resources/qml/+webengine/QmlWebWindowView.qml
Normal file
53
interface/resources/qml/+webengine/QmlWebWindowView.qml
Normal file
|
@ -0,0 +1,53 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import controlsUit 1.0 as Controls
|
||||
import stylesUit 1.0
|
||||
Controls.WebView {
|
||||
id: webview
|
||||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: webview.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
function onWebEventReceived(event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
eventBridge.webEventReceived.connect(onWebEventReceived);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
import QtQuick 2.5
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
|
||||
import "windows"
|
||||
import "."
|
||||
|
||||
ScrollingWindow {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
//HifiStyles.HifiConstants { id: hifistyles }
|
||||
|
||||
title: "Browser"
|
||||
resizable: true
|
||||
destroyOnHidden: true
|
||||
|
@ -30,6 +29,7 @@ ScrollingWindow {
|
|||
}
|
||||
|
||||
function setProfile(profile) {
|
||||
webview.profile = profile;
|
||||
}
|
||||
|
||||
function showPermissionsBar(){
|
||||
|
@ -41,6 +41,7 @@ ScrollingWindow {
|
|||
}
|
||||
|
||||
function allowPermissions(){
|
||||
webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
|
||||
hidePermissionsBar();
|
||||
}
|
||||
|
||||
|
@ -198,10 +199,15 @@ ScrollingWindow {
|
|||
}
|
||||
}
|
||||
|
||||
ProxyWebView {
|
||||
BrowserWebView {
|
||||
id: webview
|
||||
anchors.centerIn: parent
|
||||
url: "https://highfidelity.com/"
|
||||
parentRoot: root
|
||||
|
||||
anchors.top: buttons.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
} // item
|
||||
|
|
8
interface/resources/qml/BrowserWebView.qml
Normal file
8
interface/resources/qml/BrowserWebView.qml
Normal file
|
@ -0,0 +1,8 @@
|
|||
import QtQuick 2.5
|
||||
import controlsUit 1.0
|
||||
|
||||
ProxyWebView {
|
||||
property var parentRoot: null
|
||||
|
||||
function grantFeaturePermission(origin, feature) {}
|
||||
}
|
|
@ -24,7 +24,7 @@ Windows.ScrollingWindow {
|
|||
width: pane.contentWidth
|
||||
implicitHeight: pane.scrollHeight
|
||||
|
||||
ProxyWebView {
|
||||
BaseWebView {
|
||||
id: webview
|
||||
objectName: "WebView"
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -9,10 +9,9 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtWebView 1.1
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import "windows" as Windows
|
||||
import "."
|
||||
import controlsUit 1.0 as Controls
|
||||
import stylesUit 1.0
|
||||
|
||||
|
@ -57,25 +56,8 @@ Windows.ScrollingWindow {
|
|||
width: pane.contentWidth
|
||||
implicitHeight: pane.scrollHeight
|
||||
|
||||
Controls.WebView {
|
||||
QmlWebWindowView {
|
||||
id: webview
|
||||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
function onWebEventReceived(event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
eventBridge.webEventReceived.connect(onWebEventReceived);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
5
interface/resources/qml/QmlWebWindowView.qml
Normal file
5
interface/resources/qml/QmlWebWindowView.qml
Normal file
|
@ -0,0 +1,5 @@
|
|||
import QtQuick 2.5
|
||||
import controlsUit 1.0
|
||||
|
||||
BaseWebView {
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Created by David Rowe on 16 Dec 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
@ -9,68 +9,23 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
import "controls" as Controls
|
||||
|
||||
property string url
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#262626" }
|
||||
GradientStop { position: 1.0; color: "#000000" }
|
||||
Controls.WebView {
|
||||
|
||||
// This is for JS/QML communication, which is unused in a Web3DOverlay,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
|
||||
function onWebEventReceived(event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
}
|
||||
}
|
||||
|
||||
function shortUrl(url) {
|
||||
var hostBegin = url.indexOf("://");
|
||||
if (hostBegin > -1) {
|
||||
url = url.substring(hostBegin + 3);
|
||||
}
|
||||
|
||||
var portBegin = url.indexOf(":");
|
||||
if (portBegin > -1) {
|
||||
url = url.substring(0, portBegin);
|
||||
}
|
||||
|
||||
var pathBegin = url.indexOf("/");
|
||||
if (pathBegin > -1) {
|
||||
url = url.substring(0, pathBegin);
|
||||
}
|
||||
|
||||
if (url.length > 45) {
|
||||
url = url.substring(0, 45);
|
||||
}
|
||||
|
||||
return url;
|
||||
Component.onCompleted: {
|
||||
eventBridge.webEventReceived.connect(onWebEventReceived);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: urlText
|
||||
text: shortUrl(url)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: 10
|
||||
font.family: "Cairo"
|
||||
font.weight: Font.DemiBold
|
||||
font.pointSize: 48
|
||||
fontSizeMode: Text.Fit
|
||||
color: "#FFFFFF"
|
||||
minimumPixelSize: 5
|
||||
}
|
||||
|
||||
Image {
|
||||
id: hand
|
||||
source: "../../icons/hand.svg"
|
||||
width: 300
|
||||
height: 300
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 100
|
||||
anchors.rightMargin: 100
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// WebSpinner.qml
|
||||
//
|
||||
// Created by David Rowe on 23 May 2017
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtWebEngine 1.5
|
||||
|
||||
AnimatedImage {
|
||||
property WebEngineView webview: parent
|
||||
source: "qrc:////icons//loader-snake-64-w.gif"
|
||||
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
|
||||
playing: visible
|
||||
z: 10000
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ Rectangle {
|
|||
|
||||
property bool safeLoading: false
|
||||
property bool loadingLatched: false
|
||||
property bool loading: false
|
||||
property var loadingRequest: null
|
||||
|
||||
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtWebView 1.1
|
||||
|
||||
AnimatedImage {
|
||||
property WebView webview: parent
|
||||
source: "../../icons/loader-snake-64-w.gif"
|
||||
Image {
|
||||
Item {
|
||||
id: webView
|
||||
property bool loading: false
|
||||
property string url: ""
|
||||
}
|
||||
|
||||
source: "qrc:////images//unsupportedImage.png"
|
||||
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
|
||||
playing: visible
|
||||
z: 10000
|
||||
|
|
44
interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml
Normal file
44
interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml
Normal file
|
@ -0,0 +1,44 @@
|
|||
import QtQuick 2.7
|
||||
import QtWebEngine 1.5
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool webViewProfileSetup: false
|
||||
property string currentUrl: ""
|
||||
property string downloadUrl: ""
|
||||
property string adaptedPath: ""
|
||||
property string tempDir: ""
|
||||
function setupWebEngineSettings() {
|
||||
WebEngine.settings.javascriptCanOpenWindows = true;
|
||||
WebEngine.settings.javascriptCanAccessClipboard = false;
|
||||
WebEngine.settings.spatialNavigationEnabled = false;
|
||||
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
||||
}
|
||||
|
||||
|
||||
function initWebviewProfileHandlers(profile) {
|
||||
downloadUrl = currentUrl;
|
||||
if (webViewProfileSetup) return;
|
||||
webViewProfileSetup = true;
|
||||
|
||||
profile.downloadRequested.connect(function(download){
|
||||
adaptedPath = File.convertUrlToPath(downloadUrl);
|
||||
tempDir = File.getTempDir();
|
||||
download.path = tempDir + "/" + adaptedPath;
|
||||
download.accept();
|
||||
if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
|
||||
console.log("download failed to complete");
|
||||
}
|
||||
})
|
||||
|
||||
profile.downloadFinished.connect(function(download){
|
||||
if (download.state === WebEngineDownloadItem.DownloadCompleted) {
|
||||
File.runUnzip(download.path, downloadUrl, autoAdd);
|
||||
} else {
|
||||
console.log("The download was corrupted, state: " + download.state);
|
||||
}
|
||||
autoAdd = false;
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import QtQuick 2.7
|
||||
import QtWebView 1.1
|
||||
import Qt.labs.settings 1.0 as QtSettings
|
||||
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -87,15 +86,21 @@ OriginalDesktop.Desktop {
|
|||
return map;
|
||||
})({});
|
||||
|
||||
Component.onCompleted: {
|
||||
webEngineConfig.setupWebEngineSettings();
|
||||
}
|
||||
|
||||
// Accept a download through the webview
|
||||
property bool webViewProfileSetup: false
|
||||
property string currentUrl: ""
|
||||
property string downloadUrl: ""
|
||||
property string adaptedPath: ""
|
||||
property string tempDir: ""
|
||||
property alias webViewProfileSetup: webEngineConfig.webViewProfileSetup
|
||||
property alias currentUrl: webEngineConfig.currentUrl
|
||||
property alias downloadUrl: webEngineConfig.downloadUrl
|
||||
property alias adaptedPath: webEngineConfig.adaptedPath
|
||||
property alias tempDir: webEngineConfig.tempDir
|
||||
property var initWebviewProfileHandlers: webEngineConfig.initWebviewProfileHandlers
|
||||
property bool autoAdd: false
|
||||
|
||||
function initWebviewProfileHandlers(profile) {
|
||||
DesktopWebEngine {
|
||||
id: webEngineConfig
|
||||
}
|
||||
|
||||
function setAutoAdd(auto) {
|
||||
|
|
17
interface/resources/qml/hifi/DesktopWebEngine.qml
Normal file
17
interface/resources/qml/hifi/DesktopWebEngine.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick 2.7
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool webViewProfileSetup: false
|
||||
property string currentUrl: ""
|
||||
property string downloadUrl: ""
|
||||
property string adaptedPath: ""
|
||||
property string tempDir: ""
|
||||
function setupWebEngineSettings() {
|
||||
}
|
||||
|
||||
|
||||
function initWebviewProfileHandlers(profile) {
|
||||
}
|
||||
}
|
|
@ -1,358 +0,0 @@
|
|||
|
||||
//
|
||||
// WebBrowser.qml
|
||||
//
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 06/22/2017
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2 as QQControls
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import QtWebView 1.1
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
import "../windows"
|
||||
import "../controls"
|
||||
|
||||
import HifiWeb 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
property string title: "";
|
||||
signal sendToScript(var message);
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property var suggestionsList: []
|
||||
readonly property string searchUrlTemplate: "https://www.google.com/search?client=hifibrowser&q=";
|
||||
|
||||
|
||||
WebBrowserSuggestionsEngine {
|
||||
id: searchEngine
|
||||
|
||||
onSuggestions: {
|
||||
if (suggestions.length > 0) {
|
||||
suggestionsList = []
|
||||
suggestionsList.push(addressBarInput.text); //do not overwrite edit text
|
||||
for(var i = 0; i < suggestions.length; i++) {
|
||||
suggestionsList.push(suggestions[i]);
|
||||
}
|
||||
addressBar.model = suggestionsList
|
||||
if (!addressBar.popup.visible) {
|
||||
addressBar.popup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: suggestionRequestTimer
|
||||
interval: 200
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (addressBar.editText !== "") {
|
||||
searchEngine.querySuggestions(addressBarInput.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
function goTo(url) {
|
||||
//must be valid attempt to open an site with dot
|
||||
var urlNew = url
|
||||
if (url.indexOf(".") > 0) {
|
||||
if (url.indexOf("http") < 0) {
|
||||
urlNew = "http://" + url;
|
||||
}
|
||||
} else {
|
||||
urlNew = searchUrlTemplate + url
|
||||
}
|
||||
|
||||
addressBar.model = []
|
||||
//need to rebind if binfing was broken by selecting from suggestions
|
||||
addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
|
||||
webStack.currentItem.webEngineView.url = urlNew
|
||||
suggestionRequestTimer.stop();
|
||||
addressBar.popup.close();
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: parent.width;
|
||||
|
||||
RowLayout {
|
||||
id: addressBarRow
|
||||
width: parent.width;
|
||||
height: 48
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
enabled: webStack.currentItem.webEngineView.canGoBack || webStack.depth > 1
|
||||
glyph: hifi.glyphs.backward;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 38;
|
||||
onClicked: {
|
||||
if (webStack.currentItem.webEngineView.canGoBack) {
|
||||
webStack.currentItem.webEngineView.goBack();
|
||||
} else if (webStack.depth > 1) {
|
||||
webStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
enabled: webStack.currentItem.webEngineView.canGoForward
|
||||
glyph: hifi.glyphs.forward;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 38;
|
||||
onClicked: {
|
||||
webStack.currentItem.webEngineView.goForward();
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.ComboBox {
|
||||
id: addressBar
|
||||
|
||||
//selectByMouse: true
|
||||
focus: true
|
||||
|
||||
editable: true
|
||||
//flat: true
|
||||
indicator: Item {}
|
||||
background: Item {}
|
||||
onActivated: {
|
||||
goTo(textAt(index));
|
||||
}
|
||||
|
||||
onHighlightedIndexChanged: {
|
||||
if (highlightedIndex >= 0) {
|
||||
addressBar.editText = textAt(highlightedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
popup.height: webStack.height
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
addressBarInput.selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: QQControls.TextField {
|
||||
id: addressBarInput
|
||||
leftPadding: 26
|
||||
rightPadding: hifi.dimensions.controlLineHeight + 5
|
||||
text: addressBar.editText
|
||||
placeholderText: qsTr("Enter URL")
|
||||
font: addressBar.font
|
||||
selectByMouse: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onDeletePressed: {
|
||||
addressBarInput.text = ""
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Return) {
|
||||
goTo(addressBarInput.text);
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
x: 5
|
||||
z: 2
|
||||
id: faviconImage
|
||||
width: 16; height: 16
|
||||
sourceSize: Qt.size(width, height)
|
||||
source: webStack.currentItem.webEngineView.icon
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
glyph: webStack.currentItem.WebView.Loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
z: 2
|
||||
x: addressBarInput.width - implicitWidth
|
||||
onClicked: {
|
||||
if (webStack.currentItem.WebView.Loading) {
|
||||
webStack.currentItem.webEngineView.stop();
|
||||
} else {
|
||||
webStack.currentItem.reloadTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i");
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Return) {
|
||||
goTo(addressBarInput.text);
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
onEditTextChanged: {
|
||||
if (addressBar.editText !== "" && addressBar.editText !== webStack.currentItem.webEngineView.url.toString()) {
|
||||
suggestionRequestTimer.restart();
|
||||
} else {
|
||||
addressBar.model = []
|
||||
addressBar.popup.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
editText: webStack.currentItem.webEngineView.url
|
||||
onAccepted: goTo(addressBarInput.text);
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
checkable: true
|
||||
checked: webStack.currentItem.webEngineView.audioMuted
|
||||
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
onClicked: {
|
||||
webStack.currentItem.webEngineView.audioMuted = !webStack.currentItem.webEngineView.audioMuted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.ProgressBar {
|
||||
id: loadProgressBar
|
||||
background: Rectangle {
|
||||
implicitHeight: 2
|
||||
color: "#6A6A6A"
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitHeight: 2
|
||||
|
||||
Rectangle {
|
||||
width: loadProgressBar.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: "#00B4EF"
|
||||
}
|
||||
}
|
||||
|
||||
width: parent.width;
|
||||
from: 0
|
||||
to: 100
|
||||
value: webStack.currentItem.WebView.LoadProgress
|
||||
height: 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: webViewComponent
|
||||
Rectangle {
|
||||
property alias webEngineView: webEngineView
|
||||
property alias reloadTimer: reloadTimer
|
||||
|
||||
property var request: null
|
||||
|
||||
property bool isDialog: QQControls.StackView.index > 0
|
||||
property real margins: isDialog ? 10 : 0
|
||||
|
||||
color: "#d1d1d1"
|
||||
|
||||
QQControls.StackView.onActivated: {
|
||||
addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
|
||||
}
|
||||
|
||||
onRequestChanged: {
|
||||
if (isDialog && request !== null && request !== undefined) {//is Dialog ?
|
||||
request.openIn(webEngineView);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.BaseWebView {
|
||||
id: webEngineView
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
|
||||
layer.enabled: parent.isDialog
|
||||
layer.effect: DropShadow {
|
||||
verticalOffset: 8
|
||||
horizontalOffset: 8
|
||||
color: "#330066ff"
|
||||
samples: 10
|
||||
spread: 0.5
|
||||
}
|
||||
|
||||
focus: true
|
||||
objectName: "tabletWebEngineView"
|
||||
|
||||
profile.httpUserAgent: "Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0"
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
onLoadingChanged: {
|
||||
if (!loading) {
|
||||
addressBarInput.cursorPosition = 0 //set input field cursot to beginning
|
||||
suggestionRequestTimer.stop();
|
||||
addressBar.popup.close();
|
||||
}
|
||||
}
|
||||
|
||||
onLinkHovered: {
|
||||
//TODO: change cursor shape?
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: reloadTimer
|
||||
interval: 0
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: webEngineView.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.StackView {
|
||||
id: webStack
|
||||
width: parent.width;
|
||||
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
|
||||
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
||||
|
||||
Component.onCompleted: webStack.push(webViewComponent, {"webEngineView.url": "https://www.highfidelity.com"});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,13 @@ import TabletScriptingInterface 1.0
|
|||
|
||||
Rectangle {
|
||||
readonly property var level: AudioScriptingInterface.inputLevel;
|
||||
|
||||
|
||||
property bool gated: false;
|
||||
Component.onCompleted: {
|
||||
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
|
||||
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
|
||||
}
|
||||
|
||||
property bool standalone: false;
|
||||
property var dragTarget: null;
|
||||
|
||||
|
@ -77,6 +83,7 @@ Rectangle {
|
|||
readonly property string gutter: "#575757";
|
||||
readonly property string greenStart: "#39A38F";
|
||||
readonly property string greenEnd: "#1FC6A6";
|
||||
readonly property string yellow: "#C0C000";
|
||||
readonly property string red: colors.muted;
|
||||
readonly property string fill: "#55000000";
|
||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||
|
@ -189,7 +196,7 @@ Rectangle {
|
|||
|
||||
Rectangle { // mask
|
||||
id: mask;
|
||||
width: parent.width * level;
|
||||
width: gated ? 0 : parent.width * level;
|
||||
radius: 5;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
|
@ -212,18 +219,42 @@ Rectangle {
|
|||
color: colors.greenStart;
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.8;
|
||||
position: 0.5;
|
||||
color: colors.greenEnd;
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.81;
|
||||
color: colors.red;
|
||||
}
|
||||
GradientStop {
|
||||
position: 1;
|
||||
color: colors.red;
|
||||
color: colors.yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: gatedIndicator;
|
||||
visible: gated && !AudioScriptingInterface.clipping
|
||||
|
||||
radius: 4;
|
||||
width: 2 * radius;
|
||||
height: 2 * radius;
|
||||
color: "#0080FF";
|
||||
anchors {
|
||||
right: parent.left;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: clippingIndicator;
|
||||
visible: AudioScriptingInterface.clipping
|
||||
|
||||
radius: 4;
|
||||
width: 2 * radius;
|
||||
height: 2 * radius;
|
||||
color: colors.red;
|
||||
anchors {
|
||||
left: parent.right;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ Rectangle {
|
|||
HifiControlsUit.Keyboard {
|
||||
id: keyboard
|
||||
z: 1000
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised && HMD.active
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
|
|
|
@ -38,8 +38,8 @@ Rectangle {
|
|||
property bool keyboardEnabled: HMD.active
|
||||
property bool keyboardRaised: false
|
||||
property string searchScopeString: "Featured"
|
||||
property bool isLoggedIn: false;
|
||||
property bool supports3DHTML: true;
|
||||
property bool isLoggedIn: false
|
||||
property bool supports3DHTML: true
|
||||
|
||||
anchors.fill: (typeof parent === undefined) ? undefined : parent
|
||||
|
||||
|
@ -49,7 +49,7 @@ Rectangle {
|
|||
licenseInfo.visible = false;
|
||||
marketBrowseModel.getFirstPage();
|
||||
{
|
||||
if(root.searchString !== undefined && root.searchString !== "") {
|
||||
if(root.searchString !== undefined && root.searchString !== "") {
|
||||
root.searchScopeString = "Search Results: \"" + root.searchString + "\"";
|
||||
} else if (root.categoryString !== "") {
|
||||
root.searchScopeString = root.categoryString;
|
||||
|
@ -498,7 +498,7 @@ Rectangle {
|
|||
"",
|
||||
"",
|
||||
root.sortString,
|
||||
false,
|
||||
WalletScriptingInterface.limitedCommerce,
|
||||
marketBrowseModel.currentPageToRetrieve,
|
||||
marketBrowseModel.itemsPerPage
|
||||
);
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtWebEngine 1.5
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
@ -424,13 +422,13 @@ Rectangle {
|
|||
elide: Text.ElideRight
|
||||
size: 14
|
||||
|
||||
color: model.link ? hifi.colors.blueHighlight : hifi.colors.baseGray
|
||||
color: (model.link && root.supports3DHTML)? hifi.colors.blueHighlight : hifi.colors.baseGray
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
if (model.link) {
|
||||
if (model.link && root.supports3DHTML) {
|
||||
sendToScript({method: 'marketplace_open_link', link: model.link});
|
||||
}
|
||||
}
|
||||
|
@ -571,12 +569,14 @@ Rectangle {
|
|||
text: root.description
|
||||
size: 14
|
||||
color: hifi.colors.lightGray
|
||||
linkColor: hifi.colors.blueHighlight
|
||||
linkColor: root.supports3DHTML ? hifi.colors.blueHighlight : hifi.colors.lightGray
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.Wrap
|
||||
onLinkActivated: {
|
||||
sendToScript({method: 'marketplace_open_link', link: link});
|
||||
if (root.supports3DHTML) {
|
||||
sendToScript({method: 'marketplace_open_link', link: link});
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: { footer.evalHeight(); }
|
||||
|
|
|
@ -607,7 +607,7 @@ Rectangle {
|
|||
} else if (msg.method === "showTrashLightbox") {
|
||||
lightboxPopup.titleText = "Send \"" + msg.itemName + "\" to Trash";
|
||||
lightboxPopup.bodyText = "Sending this item to the Trash means you will no longer own this item " +
|
||||
"and it will be inaccessible to you from Purchases.\n\nThis action cannot be undone.";
|
||||
"and it will be inaccessible to you from Inventory.\n\nThis action cannot be undone.";
|
||||
lightboxPopup.button1text = "CANCEL";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import QtQuick 2.0
|
||||
import QtWebEngine 1.5
|
||||
import "../../controls" as Controls
|
||||
|
||||
Controls.TabletWebView {
|
||||
profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
import QtQuick 2.0
|
||||
import QtWebView 1.1
|
||||
|
||||
import "../../controls" as Controls
|
||||
|
||||
Controls.TabletWebView {
|
||||
|
|
|
@ -2,7 +2,7 @@ import QtQuick 2.5
|
|||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQml 2.2
|
||||
import QtWebChannel 1.0
|
||||
|
||||
|
||||
import "."
|
||||
import stylesUit 1.0
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
import "../../controls" as Controls
|
||||
|
||||
Controls.TabletWebScreen {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
import "../../controls" as Controls
|
||||
|
||||
Controls.WebView {
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {string} buildDate
|
||||
* @property {string} buildVersion
|
||||
* @property {string} qtVersion
|
||||
|
|
|
@ -2428,7 +2428,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
|
||||
AndroidHelper::instance().notifyLoadComplete();
|
||||
#endif
|
||||
AndroidHelper::instance().notifyLoadComplete();
|
||||
pauseUntilLoginDetermined();
|
||||
}
|
||||
|
||||
|
@ -3641,14 +3640,14 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
}
|
||||
|
||||
// Get controller availability
|
||||
#ifdef Q_OS_ANDROID
|
||||
#ifdef Q_OS_ANDROID
|
||||
bool hasHandControllers = true;
|
||||
#else
|
||||
#else
|
||||
bool hasHandControllers = false;
|
||||
if (PluginUtils::isViveControllerAvailable() || PluginUtils::isOculusTouchControllerAvailable()) {
|
||||
hasHandControllers = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Check HMD use (may be technically available without being in use)
|
||||
bool hasHMD = PluginUtils::isHMDAvailable();
|
||||
|
@ -4981,10 +4980,10 @@ void Application::idle() {
|
|||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
||||
// details normally.
|
||||
#ifndef Q_OS_ANDROID
|
||||
bool showWarnings = getLogger()->extraDebugging();
|
||||
#else
|
||||
#ifdef Q_OS_ANDROID
|
||||
bool showWarnings = false;
|
||||
#else
|
||||
bool showWarnings = getLogger()->extraDebugging();
|
||||
#endif
|
||||
PerformanceWarning warn(showWarnings, "idle()");
|
||||
|
||||
|
@ -8329,7 +8328,7 @@ void Application::toggleLogDialog() {
|
|||
bool keepOnTop =_keepLogWindowOnTop.get();
|
||||
#ifdef Q_OS_WIN
|
||||
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
|
||||
#else
|
||||
#elif !defined(Q_OS_ANDROID)
|
||||
_logDialog = new LogDialog(nullptr, getLogger());
|
||||
|
||||
if (keepOnTop) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class AABox;
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} presentTime <em>Read-only.</em>
|
||||
* @property {number} engineRunTime <em>Read-only.</em>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* @hifi-client-entity
|
||||
* @hifi-interface
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class LocationBookmarks : public Bookmarks, public Dependency {
|
||||
|
|
|
@ -294,13 +294,6 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
|
|||
}
|
||||
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
|
||||
if (!mapping.contains(FREE_JOINT_FIELD)) {
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm");
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm");
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightArm");
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
||||
}
|
||||
|
||||
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
|
||||
// then we can add the default mixamo to "faceshift" mappings
|
||||
|
|
|
@ -58,11 +58,6 @@ _hfmModel(hfmModel)
|
|||
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
||||
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
||||
|
||||
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
|
||||
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
|
||||
_freeJoints->addWidget(newFreeJoint);
|
||||
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
|
@ -102,11 +97,6 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
|||
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
||||
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
||||
|
||||
mapping.remove(FREE_JOINT_FIELD);
|
||||
for (int i = 0; i < _freeJoints->count() - 1; i++) {
|
||||
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
|
||||
}
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
|
||||
return mapping;
|
||||
|
@ -133,16 +123,6 @@ void ModelPropertiesDialog::reset() {
|
|||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
||||
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
||||
|
||||
while (_freeJoints->count() > 1) {
|
||||
delete _freeJoints->itemAt(0)->widget();
|
||||
}
|
||||
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
|
||||
QString jointName = joint.toString();
|
||||
if (_hfmModel.jointIndices.contains(jointName)) {
|
||||
createNewFreeJoint(jointName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::chooseTextureDirectory() {
|
||||
|
@ -176,20 +156,6 @@ void ModelPropertiesDialog::updatePivotJoint() {
|
|||
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
|
||||
QWidget* freeJoint = new QWidget();
|
||||
QHBoxLayout* freeJointLayout = new QHBoxLayout();
|
||||
freeJointLayout->setContentsMargins(QMargins());
|
||||
freeJoint->setLayout(freeJointLayout);
|
||||
QComboBox* jointBox = createJointBox(false);
|
||||
jointBox->setCurrentText(joint);
|
||||
freeJointLayout->addWidget(jointBox, 1);
|
||||
QPushButton* deleteJoint = new QPushButton("Delete");
|
||||
freeJointLayout->addWidget(deleteJoint);
|
||||
freeJoint->connect(deleteJoint, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
||||
_freeJoints->insertWidget(_freeJoints->count() - 1, freeJoint);
|
||||
}
|
||||
|
||||
QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const {
|
||||
QComboBox* box = new QComboBox();
|
||||
if (withNone) {
|
||||
|
|
|
@ -39,7 +39,6 @@ private slots:
|
|||
void chooseTextureDirectory();
|
||||
void chooseScriptDirectory();
|
||||
void updatePivotJoint();
|
||||
void createNewFreeJoint(const QString& joint = QString());
|
||||
|
||||
private:
|
||||
QComboBox* createJointBox(bool withNone = true) const;
|
||||
|
@ -66,7 +65,6 @@ private:
|
|||
QComboBox* _headJoint = nullptr;
|
||||
QComboBox* _leftHandJoint = nullptr;
|
||||
QComboBox* _rightHandJoint = nullptr;
|
||||
QVBoxLayout* _freeJoints = nullptr;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelPropertiesDialog_h
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
class SpeechRecognizer : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -31,6 +31,7 @@ class AudioScope : public QObject, public Dependency {
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} scopeInput <em>Read-only.</em>
|
||||
* @property {number} scopeOutputLeft <em>Read-only.</em>
|
||||
|
|
|
@ -569,7 +569,7 @@ void AvatarActionHold::lateAvatarUpdate(const AnimPose& prePhysicsRoomPose, cons
|
|||
}
|
||||
|
||||
btTransform worldTrans = rigidBody->getWorldTransform();
|
||||
AnimPose worldBodyPose(glm::vec3(1), bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
|
||||
AnimPose worldBodyPose(1.0f, bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
|
||||
|
||||
// transform the body transform into sensor space with the prePhysics sensor-to-world matrix.
|
||||
// then transform it back into world uisng the postAvatarUpdate sensor-to-world matrix.
|
||||
|
|
|
@ -652,28 +652,25 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
|
||||
PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
||||
|
||||
float distance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
|
||||
float bulletDistance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
|
||||
glm::vec3 rayDirection = glm::normalize(ray.direction);
|
||||
std::vector<MyCharacterController::RayAvatarResult> physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), distance, QVector<uint>());
|
||||
std::vector<MyCharacterController::RayAvatarResult> physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), bulletDistance, QVector<uint>());
|
||||
if (physicsResults.size() > 0) {
|
||||
glm::vec3 rayDirectionInv = { rayDirection.x != 0.0f ? 1.0f / rayDirection.x : INFINITY,
|
||||
rayDirection.y != 0.0f ? 1.0f / rayDirection.y : INFINITY,
|
||||
rayDirection.z != 0.0f ? 1.0f / rayDirection.z : INFINITY };
|
||||
|
||||
MyCharacterController::RayAvatarResult rayAvatarResult;
|
||||
AvatarPointer avatar = nullptr;
|
||||
|
||||
BoxFace face = BoxFace::UNKNOWN_FACE;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
|
||||
for (auto &hit : physicsResults) {
|
||||
auto avatarID = hit._intersectWithAvatar;
|
||||
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatarID)) ||
|
||||
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatarID))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
MyCharacterController::RayAvatarResult rayAvatarResult;
|
||||
BoxFace face = BoxFace::UNKNOWN_FACE;
|
||||
QVariantMap extraInfo;
|
||||
AvatarPointer avatar = nullptr;
|
||||
if (_myAvatar->getSessionUUID() != avatarID) {
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.find(avatarID);
|
||||
|
@ -683,46 +680,45 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
} else {
|
||||
avatar = _myAvatar;
|
||||
}
|
||||
|
||||
if (!hit._isBound) {
|
||||
rayAvatarResult = hit;
|
||||
} else if (avatar) {
|
||||
auto &multiSpheres = avatar->getMultiSphereShapes();
|
||||
if (multiSpheres.size() > 0) {
|
||||
std::vector<MyCharacterController::RayAvatarResult> boxHits;
|
||||
MyCharacterController::RayAvatarResult boxHit;
|
||||
boxHit._distance = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
|
||||
assert(hit._boundJoints[i] < multiSpheres.size());
|
||||
auto &mSphere = multiSpheres[hit._boundJoints[i]];
|
||||
if (mSphere.isValid()) {
|
||||
float boundDistance = FLT_MAX;
|
||||
BoxFace face;
|
||||
glm::vec3 surfaceNormal;
|
||||
BoxFace boundFace = BoxFace::UNKNOWN_FACE;
|
||||
glm::vec3 boundSurfaceNormal;
|
||||
auto &bbox = mSphere.getBoundingBox();
|
||||
if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, face, surfaceNormal)) {
|
||||
MyCharacterController::RayAvatarResult boxHit;
|
||||
boxHit._distance = boundDistance;
|
||||
boxHit._intersect = true;
|
||||
boxHit._intersectionNormal = surfaceNormal;
|
||||
boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
|
||||
boxHit._intersectWithAvatar = avatarID;
|
||||
boxHit._intersectWithJoint = mSphere.getJointIndex();
|
||||
boxHits.push_back(boxHit);
|
||||
if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, boundFace, boundSurfaceNormal)) {
|
||||
if (boundDistance < boxHit._distance) {
|
||||
boxHit._intersect = true;
|
||||
boxHit._intersectWithAvatar = avatarID;
|
||||
boxHit._intersectWithJoint = mSphere.getJointIndex();
|
||||
boxHit._distance = boundDistance;
|
||||
boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
|
||||
boxHit._intersectionNormal = boundSurfaceNormal;
|
||||
face = boundFace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (boxHits.size() > 0) {
|
||||
if (boxHits.size() > 1) {
|
||||
std::sort(boxHits.begin(), boxHits.end(), [](const MyCharacterController::RayAvatarResult& hitA,
|
||||
const MyCharacterController::RayAvatarResult& hitB) {
|
||||
return hitA._distance < hitB._distance;
|
||||
});
|
||||
}
|
||||
rayAvatarResult = boxHits[0];
|
||||
if (boxHit._distance < FLT_MAX) {
|
||||
rayAvatarResult = boxHit;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pickAgainstMesh) {
|
||||
|
||||
if (rayAvatarResult._intersect && pickAgainstMesh) {
|
||||
glm::vec3 localRayOrigin = avatar->worldToJointPoint(ray.origin, rayAvatarResult._intersectWithJoint);
|
||||
glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayDirection, rayAvatarResult._intersectWithJoint);
|
||||
glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayAvatarResult._distance * rayDirection, rayAvatarResult._intersectWithJoint);
|
||||
|
||||
auto avatarOrientation = avatar->getWorldOrientation();
|
||||
auto avatarPosition = avatar->getWorldPosition();
|
||||
|
@ -732,31 +728,37 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
|
||||
auto defaultFrameRayOrigin = jointPosition + jointOrientation * localRayOrigin;
|
||||
auto defaultFrameRayPoint = jointPosition + jointOrientation * localRayPoint;
|
||||
auto defaultFrameRayDirection = defaultFrameRayPoint - defaultFrameRayOrigin;
|
||||
auto defaultFrameRayDirection = glm::normalize(defaultFrameRayPoint - defaultFrameRayOrigin);
|
||||
|
||||
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, distance, face, surfaceNormal, extraInfo, true, false)) {
|
||||
auto newDistance = glm::length(vec3FromVariant(extraInfo["worldIntersectionPoint"]) - defaultFrameRayOrigin);
|
||||
rayAvatarResult._distance = newDistance;
|
||||
rayAvatarResult._intersectionPoint = ray.origin + newDistance * rayDirection;
|
||||
rayAvatarResult._intersectionNormal = surfaceNormal;
|
||||
extraInfo["worldIntersectionPoint"] = vec3toVariant(rayAvatarResult._intersectionPoint);
|
||||
break;
|
||||
float subMeshDistance = FLT_MAX;
|
||||
BoxFace subMeshFace = BoxFace::UNKNOWN_FACE;
|
||||
glm::vec3 subMeshSurfaceNormal;
|
||||
QVariantMap subMeshExtraInfo;
|
||||
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, subMeshDistance, subMeshFace, subMeshSurfaceNormal, subMeshExtraInfo, true, false)) {
|
||||
rayAvatarResult._distance = subMeshDistance;
|
||||
rayAvatarResult._intersectionPoint = ray.origin + subMeshDistance * rayDirection;
|
||||
rayAvatarResult._intersectionNormal = subMeshSurfaceNormal;
|
||||
face = subMeshFace;
|
||||
extraInfo = subMeshExtraInfo;
|
||||
} else {
|
||||
rayAvatarResult._intersect = false;
|
||||
}
|
||||
} else if (rayAvatarResult._intersect){
|
||||
}
|
||||
|
||||
if (rayAvatarResult._intersect) {
|
||||
result.intersects = true;
|
||||
result.avatarID = rayAvatarResult._intersectWithAvatar;
|
||||
result.distance = rayAvatarResult._distance;
|
||||
result.face = face;
|
||||
result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
|
||||
result.surfaceNormal = rayAvatarResult._intersectionNormal;
|
||||
result.jointIndex = rayAvatarResult._intersectWithJoint;
|
||||
result.extraInfo = extraInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rayAvatarResult._intersect) {
|
||||
result.intersects = true;
|
||||
result.avatarID = rayAvatarResult._intersectWithAvatar;
|
||||
result.distance = rayAvatarResult._distance;
|
||||
result.surfaceNormal = rayAvatarResult._intersectionNormal;
|
||||
result.jointIndex = rayAvatarResult._intersectWithJoint;
|
||||
result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
|
||||
result.extraInfo = extraInfo;
|
||||
result.face = face;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @borrows AvatarList.getAvatarIdentifiers as getAvatarIdentifiers
|
||||
* @borrows AvatarList.getAvatarsInRange as getAvatarsInRange
|
||||
|
|
|
@ -915,14 +915,9 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
auto entityTreeRenderer = qApp->getEntities();
|
||||
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
||||
if (entityTree) {
|
||||
bool zoneAllowsFlying = true;
|
||||
bool collisionlessAllowed = true;
|
||||
std::pair<bool, bool> zoneInteractionProperties;
|
||||
entityTree->withWriteLock([&] {
|
||||
std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone();
|
||||
if (zone) {
|
||||
zoneAllowsFlying = zone->getFlyingAllowed();
|
||||
collisionlessAllowed = zone->getGhostingAllowed();
|
||||
}
|
||||
zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties();
|
||||
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
|
||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||
// we need to update attached queryAACubes in our own local tree so point-select always works
|
||||
|
@ -935,6 +930,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
});
|
||||
});
|
||||
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
|
||||
bool zoneAllowsFlying = zoneInteractionProperties.first;
|
||||
bool collisionlessAllowed = zoneInteractionProperties.second;
|
||||
_characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled);
|
||||
_characterController.setCollisionlessAllowed(collisionlessAllowed);
|
||||
}
|
||||
|
@ -2983,7 +2980,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
|||
auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
|
||||
|
||||
// the rig is in the skeletonModel frame
|
||||
AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation());
|
||||
AnimPose xform(1.0f, _skeletonModel->getRotation(), _skeletonModel->getTranslation());
|
||||
|
||||
if (_enableDebugDrawDefaultPose && animSkeleton) {
|
||||
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
|
||||
|
@ -3028,7 +3025,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
|||
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
|
||||
|
||||
if (_enableDebugDrawDetailedCollision) {
|
||||
AnimPose rigToWorldPose(glm::vec3(1.0f), getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
|
||||
AnimPose rigToWorldPose(1.0f, getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
|
||||
const int NUM_DEBUG_COLORS = 8;
|
||||
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
|
||||
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
|
@ -4824,7 +4821,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
swingTwistDecomposition(hipsinWorldSpace, avatarUpWorld, resultingSwingInWorld, resultingTwistInWorld);
|
||||
|
||||
// remove scale present from sensorToWorldMatrix
|
||||
followWorldPose.scale() = glm::vec3(1.0f);
|
||||
followWorldPose.scale() = 1.0f;
|
||||
|
||||
if (isActive(Rotation)) {
|
||||
//use the hmd reading for the hips follow
|
||||
|
|
|
@ -66,6 +66,7 @@ class MyAvatar : public Avatar {
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {Vec3} qmlPosition - A synonym for <code>position</code> for use by QML.
|
||||
* @property {boolean} shouldRenderLocally=true - If <code>true</code> then your avatar is rendered for you in Interface,
|
||||
|
|
|
@ -41,7 +41,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
|||
if (myAvatar->isJointPinned(hipsIndex)) {
|
||||
Transform avatarTransform = myAvatar->getTransform();
|
||||
AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180);
|
||||
result.scale() = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
result.scale() = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
Rig::ControllerParameters params;
|
||||
|
||||
AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f));
|
||||
AnimPose avatarToRigPose(1.0f, Quaternions::Y_180, glm::vec3(0.0f));
|
||||
|
||||
glm::mat4 rigToAvatarMatrix = Matrices::Y_180;
|
||||
glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition());
|
||||
|
@ -127,7 +127,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
// preMult 180 is necessary to convert from avatar to rig coordinates.
|
||||
// postMult 180 is necessary to convert head from -z forward to z forward.
|
||||
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
|
||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f));
|
||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(1.0f, headRot, glm::vec3(0.0f));
|
||||
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -447,6 +447,10 @@ void OtherAvatar::handleChangedAvatarEntityData() {
|
|||
EntityItemProperties properties;
|
||||
int32_t bytesLeftToRead = data.size();
|
||||
unsigned char* dataAt = (unsigned char*)(data.data());
|
||||
// FIXME: This function will cause unintented changes in SpaillyNestable
|
||||
// E.g overriding the ID index of an exisiting entity to temporary entity
|
||||
// in the following map QHash<QUuid, SpatiallyNestableWeakPointer> _children;
|
||||
// Andrew Meadows will address this issue
|
||||
if (!properties.constructFromBuffer(dataAt, bytesLeftToRead)) {
|
||||
// properties are corrupt
|
||||
continue;
|
||||
|
@ -489,6 +493,17 @@ void OtherAvatar::handleChangedAvatarEntityData() {
|
|||
bool success = true;
|
||||
if (entity) {
|
||||
QUuid oldParentID = entity->getParentID();
|
||||
|
||||
// Since has overwrtiiten the back pointer
|
||||
// from the parent children map (see comment for function call above),
|
||||
// we need to for reset the back pointer in the map correctly by setting the parentID, but
|
||||
// since the parentID of the entity has not changed we first need to set it some ither ID,
|
||||
// then set the the original ID for the changes to take effect
|
||||
// TODO: This is a horrible hack and once properties.constructFromBuffer no longer causes
|
||||
// side effects...remove the following three lines
|
||||
const QUuid NULL_ID = QUuid("{00000000-0000-0000-0000-000000000005}");
|
||||
entity->setParentID(NULL_ID);
|
||||
entity->setParentID(oldParentID);
|
||||
if (entityTree->updateEntity(entityID, properties)) {
|
||||
entity->updateLastEditedFromRemote();
|
||||
} else {
|
||||
|
|
|
@ -72,20 +72,17 @@ void QmlMarketplace::getMarketplaceItems(
|
|||
|
||||
void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) {
|
||||
QString endpoint = QString("items/") + marketplaceItemId;
|
||||
QUrlQuery request;
|
||||
send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
|
||||
send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional);
|
||||
}
|
||||
|
||||
void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) {
|
||||
QString endpoint = QString("items/") + marketplaceItemId + "/like";
|
||||
QUrlQuery request;
|
||||
send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request);
|
||||
send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required);
|
||||
}
|
||||
|
||||
void QmlMarketplace::getMarketplaceCategories() {
|
||||
QString endpoint = "categories";
|
||||
QUrlQuery request;
|
||||
send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None, request);
|
||||
send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None);
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,14 +91,13 @@ void QmlMarketplace::send(const QString& endpoint, const QString& success, const
|
|||
const QString URL = "/api/v1/marketplace/";
|
||||
JSONCallbackParameters callbackParams(this, success, fail);
|
||||
|
||||
accountManager->sendRequest(URL + endpoint,
|
||||
accountManager->sendRequest(URL + endpoint + "?" + request.toString(),
|
||||
authType,
|
||||
method,
|
||||
callbackParams,
|
||||
QByteArray(),
|
||||
NULL,
|
||||
QVariantMap(),
|
||||
request);
|
||||
QVariantMap());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,12 @@ signals:
|
|||
void marketplaceItemLikeResult(QJsonObject result);
|
||||
|
||||
private:
|
||||
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request);
|
||||
void send(const QString& endpoint,
|
||||
const QString& success,
|
||||
const QString& fail,
|
||||
QNetworkAccessManager::Operation method,
|
||||
AccountManagerAuth::Type authType,
|
||||
const QUrlQuery& request = QUrlQuery());
|
||||
QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
|
||||
QJsonObject failResponse(const QString& label, QNetworkReply* reply);
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class DdeFaceTracker : public FaceTracker, public Dependency {
|
||||
|
|
|
@ -40,11 +40,12 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
|
||||
#ifndef Q_OS_ANDROID
|
||||
const int WAY_BEHIND = 300;
|
||||
|
||||
if (packetsToProcessCount() > WAY_BEHIND && qApp->getLogger()->extraDebugging()) {
|
||||
qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool wasStatsPacket = false;
|
||||
|
||||
PacketType octreePacketType = message->getType();
|
||||
|
|
|
@ -27,6 +27,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency {
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
public:
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} PICK_ENTITIES A filter flag. Include domain and avatar entities when intersecting. <em>Read-only.</em>. Deprecated.
|
||||
* @property {number} PICK_OVERLAYS A filter flag. Include local entities when intersecting. <em>Read-only.</em>. Deprecated.
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class PointerScriptingInterface : public QObject, public Dependency {
|
||||
|
|
|
@ -56,7 +56,8 @@ PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
|
|||
}
|
||||
|
||||
PickResultPointer RayPick::getAvatarIntersection(const PickRay& pick) {
|
||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), true);
|
||||
bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
|
||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), precisionPicking);
|
||||
if (avatarRes.intersects) {
|
||||
return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
|
||||
} else {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} PICK_ENTITIES <em>Read-only.</em>
|
||||
* @property {number} PICK_OVERLAYS <em>Read-only.</em>
|
||||
|
|
|
@ -42,6 +42,7 @@ class AccountServicesScriptingInterface : public QObject {
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @namespace AccountServices
|
||||
* @property {string} username <em>Read-only.</em>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "AudioClient.h"
|
||||
#include "AudioHelpers.h"
|
||||
#include "ui/AvatarInputs.h"
|
||||
|
||||
using namespace scripting;
|
||||
|
@ -26,26 +27,9 @@ QString Audio::HMD { "VR" };
|
|||
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
|
||||
|
||||
float Audio::loudnessToLevel(float loudness) {
|
||||
const float LOG2 = log(2.0f);
|
||||
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
|
||||
const float LOG2_LOUDNESS_FLOOR = 11.0f;
|
||||
|
||||
float level = 0.0f;
|
||||
|
||||
loudness += 1.0f;
|
||||
float log2loudness = logf(loudness) / LOG2;
|
||||
|
||||
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
|
||||
level = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE;
|
||||
} else {
|
||||
level = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE;
|
||||
}
|
||||
|
||||
if (level > 1.0f) {
|
||||
level = 1.0;
|
||||
}
|
||||
|
||||
return level;
|
||||
float level = 6.02059991f * fastLog2f(loudness); // level in dBFS
|
||||
level = (level + 48.0f) * (1/39.0f); // map [-48, -9] dBFS to [0, 1]
|
||||
return glm::clamp(level, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
Audio::Audio() : _devices(_contextIsHMD) {
|
||||
|
@ -150,18 +134,33 @@ float Audio::getInputLevel() const {
|
|||
});
|
||||
}
|
||||
|
||||
void Audio::onInputLoudnessChanged(float loudness) {
|
||||
bool Audio::isClipping() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _isClipping;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::onInputLoudnessChanged(float loudness, bool isClipping) {
|
||||
float level = loudnessToLevel(loudness);
|
||||
bool changed = false;
|
||||
bool levelChanged = false;
|
||||
bool isClippingChanged = false;
|
||||
|
||||
withWriteLock([&] {
|
||||
if (_inputLevel != level) {
|
||||
_inputLevel = level;
|
||||
changed = true;
|
||||
levelChanged = true;
|
||||
}
|
||||
if (_isClipping != isClipping) {
|
||||
_isClipping = isClipping;
|
||||
isClippingChanged = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
if (levelChanged) {
|
||||
emit inputLevelChanged(level);
|
||||
}
|
||||
if (isClippingChanged) {
|
||||
emit clippingChanged(isClipping);
|
||||
}
|
||||
}
|
||||
|
||||
QString Audio::getContext() const {
|
||||
|
|
|
@ -32,6 +32,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
|
@ -41,6 +42,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* above the noise floor.
|
||||
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) –
|
||||
* <code>1.0</code> (the onset of clipping). <em>Read-only.</em>
|
||||
* @property {boolean} clipping - <code>true</code> if the audio input is clipping, otherwise <code>false</code>.
|
||||
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> – <code>1.0</code>.
|
||||
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
|
||||
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||
|
@ -58,6 +60,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
||||
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
|
||||
Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged)
|
||||
Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged)
|
||||
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
|
||||
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
|
||||
|
||||
|
@ -74,6 +77,7 @@ public:
|
|||
bool noiseReductionEnabled() const;
|
||||
float getInputVolume() const;
|
||||
float getInputLevel() const;
|
||||
bool isClipping() const;
|
||||
QString getContext() const;
|
||||
|
||||
void showMicMeter(bool show);
|
||||
|
@ -217,6 +221,14 @@ signals:
|
|||
*/
|
||||
void inputLevelChanged(float level);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the clipping state of the input audio changes.
|
||||
* @function Audio.clippingChanged
|
||||
* @param {boolean} isClipping - <code>true</code> if the audio input is clipping, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void clippingChanged(bool isClipping);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the current context of the audio changes.
|
||||
* @function Audio.contextChanged
|
||||
|
@ -237,7 +249,7 @@ private slots:
|
|||
void setMuted(bool muted);
|
||||
void enableNoiseReduction(bool enable);
|
||||
void setInputVolume(float volume);
|
||||
void onInputLoudnessChanged(float loudness);
|
||||
void onInputLoudnessChanged(float loudness, bool isClipping);
|
||||
|
||||
protected:
|
||||
// Audio must live on a separate thread from AudioClient to avoid deadlocks
|
||||
|
@ -247,6 +259,7 @@ private:
|
|||
|
||||
float _inputVolume { 1.0f };
|
||||
float _inputLevel { 0.0f };
|
||||
bool _isClipping { false };
|
||||
bool _isMuted { false };
|
||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||
bool _contextIsHMD { false };
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
class ClipboardScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -199,6 +199,7 @@ class ScriptEngine;
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
|
||||
* points in a {@link RouteObject} mapping. A synonym for <code>Controller.Hardware.Actions</code>.
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {number} ALWAYS_ON_TOP - InteractiveWindow flag for always showing a window on top
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class GooglePolyScriptingInterface : public QObject, public Dependency {
|
||||
|
|
|
@ -31,6 +31,7 @@ class QScriptEngine;
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {Vec3} position - The position of the HMD if currently in VR display mode, otherwise
|
||||
* {@link Vec3(0)|Vec3.ZERO}. <em>Read-only.</em>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {bool} raised - <code>true</code> If the keyboard is visible <code>false</code> otherwise
|
||||
* @property {bool} password - <code>true</code> Will show * instead of characters in the text display <code>false</code> otherwise
|
||||
|
|
|
@ -35,6 +35,7 @@ class MenuItemProperties;
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -88,6 +88,7 @@ protected:
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @example <caption>Outline an entity when it is grabbed by a controller.</caption>
|
||||
* // Create a box and copy the following text into the entity's "Script URL" field.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class SettingsScriptingInterface : public QObject {
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} walletStatus
|
||||
* @property {bool} limitedCommerce
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
|
||||
* chrome), in pixels. <em>Read-only.</em>
|
||||
|
|
|
@ -17,7 +17,7 @@ HIFI_QML_DEF(AnimStats)
|
|||
static AnimStats* INSTANCE{ nullptr };
|
||||
|
||||
AnimStats* AnimStats::getInstance() {
|
||||
//Q_ASSERT(INSTANCE);
|
||||
Q_ASSERT(INSTANCE);
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ class AvatarInputs : public QObject {
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {boolean} cameraEnabled <em>Read-only.</em>
|
||||
* @property {boolean} cameraMuted <em>Read-only.</em>
|
||||
|
|
|
@ -734,7 +734,6 @@ void Keyboard::setLayerIndex(int layerIndex) {
|
|||
}
|
||||
|
||||
void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
|
||||
return;
|
||||
if (keyboardFile.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -782,7 +781,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
|
|||
{ "isSolid", true },
|
||||
{ "visible", false },
|
||||
{ "grabbable", true },
|
||||
{ "ignoreRayIntersection", false },
|
||||
{ "ignorePickIntersection", false },
|
||||
{ "dimensions", anchorObject["dimensions"].toVariant() },
|
||||
{ "position", anchorObject["position"].toVariant() },
|
||||
{ "orientation", anchorObject["rotation"].toVariant() }
|
||||
|
|
|
@ -42,6 +42,7 @@ private:
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class Snapshot : public QObject, public Dependency {
|
||||
|
|
|
@ -46,7 +46,7 @@ static Stats* INSTANCE{ nullptr };
|
|||
QString getTextureMemoryPressureModeString();
|
||||
#endif
|
||||
Stats* Stats::getInstance() {
|
||||
//Q_ASSERT(INSTANCE);
|
||||
Q_ASSERT(INSTANCE);
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ private: \
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus.
|
||||
* If no overlay has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to
|
||||
|
|
|
@ -4,5 +4,6 @@ link_hifi_libraries(shared graphics fbx)
|
|||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(hfm)
|
||||
include_hifi_library_headers(image)
|
||||
|
||||
target_nsight()
|
||||
|
|
|
@ -140,10 +140,10 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
postRot = animSkeleton.getPostRotationPose(animJoint);
|
||||
|
||||
// cancel out scale
|
||||
preRot.scale() = glm::vec3(1.0f);
|
||||
postRot.scale() = glm::vec3(1.0f);
|
||||
preRot.scale() = 1.0f;
|
||||
postRot.scale() = 1.0f;
|
||||
|
||||
AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3());
|
||||
AnimPose rot(1.0f, hfmAnimRot, glm::vec3());
|
||||
|
||||
// adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// will be adjusted when played on a skeleton with short limbs.
|
||||
|
@ -155,7 +155,7 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans);
|
||||
}
|
||||
|
||||
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans));
|
||||
AnimPose trans = AnimPose(1.0f, glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans));
|
||||
|
||||
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
||||
}
|
||||
|
|
|
@ -552,7 +552,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
AnimPose accum = absolutePoses[_hipsIndex];
|
||||
AnimPose baseParentPose = absolutePoses[_hipsIndex];
|
||||
for (int i = (int)chainDepth - 1; i >= 0; i--) {
|
||||
accum = accum * AnimPose(glm::vec3(1.0f), jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans);
|
||||
accum = accum * AnimPose(1.0f, jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans);
|
||||
postAbsPoses[i] = accum;
|
||||
if (jointChainInfoOut.jointInfoVec[i].jointIndex == topJointIndex) {
|
||||
topChainIndex = i;
|
||||
|
@ -734,7 +734,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani
|
|||
glm::mat3 m(u, v, glm::cross(u, v));
|
||||
glm::quat rot = glm::normalize(glm::quat_cast(m));
|
||||
|
||||
AnimPose pose(glm::vec3(1.0f), rot, spline(t));
|
||||
AnimPose pose(1.0f, rot, spline(t));
|
||||
AnimPose offsetPose = pose.inverse() * defaultPose;
|
||||
|
||||
SplineJointInfo splineJointInfo = { index, ratio, offsetPose };
|
||||
|
@ -767,7 +767,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
|||
const int baseIndex = _hipsIndex;
|
||||
|
||||
// build spline from tip to base
|
||||
AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation());
|
||||
AnimPose tipPose = AnimPose(1.0f, target.getRotation(), target.getTranslation());
|
||||
AnimPose basePose = absolutePoses[baseIndex];
|
||||
CubicHermiteSplineFunctorWithArcLength spline;
|
||||
if (target.getIndex() == _headIndex) {
|
||||
|
@ -815,7 +815,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
|||
glm::mat3 m(u, v, glm::cross(u, v));
|
||||
glm::quat rot = glm::normalize(glm::quat_cast(m));
|
||||
|
||||
AnimPose desiredAbsPose = AnimPose(glm::vec3(1.0f), rot, trans) * splineJointInfo.offsetPose;
|
||||
AnimPose desiredAbsPose = AnimPose(1.0f, rot, trans) * splineJointInfo.offsetPose;
|
||||
|
||||
// apply flex coefficent
|
||||
AnimPose flexedAbsPose;
|
||||
|
@ -960,7 +960,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
}
|
||||
|
||||
_relativePoses[_hipsIndex] = parentAbsPose.inverse() * absPose;
|
||||
_relativePoses[_hipsIndex].scale() = glm::vec3(1.0f);
|
||||
_relativePoses[_hipsIndex].scale() = 1.0f;
|
||||
}
|
||||
|
||||
// if there is an active jointChainInfo for the hips store the post shifted hips into it.
|
||||
|
@ -1748,7 +1748,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) {
|
|||
AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix()));
|
||||
for (auto& iter : _secondaryTargetsInRigFrame) {
|
||||
AnimPose absPose = rigToGeometryPose * iter.second;
|
||||
absPose.scale() = glm::vec3(1.0f);
|
||||
absPose.scale() = 1.0f;
|
||||
|
||||
AnimPose parentAbsPose;
|
||||
int parentIndex = _skeleton->getParentIndex(iter.first);
|
||||
|
@ -1820,7 +1820,7 @@ void AnimInverseKinematics::debugDrawSpineSplines(const AnimContext& context, co
|
|||
const int baseIndex = _hipsIndex;
|
||||
|
||||
// build spline
|
||||
AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation());
|
||||
AnimPose tipPose = AnimPose(1.0f, target.getRotation(), target.getTranslation());
|
||||
AnimPose basePose = _skeleton->getAbsolutePose(baseIndex, _relativePoses);
|
||||
|
||||
CubicHermiteSplineFunctorWithArcLength spline;
|
||||
|
|
|
@ -172,5 +172,5 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap&
|
|||
break;
|
||||
}
|
||||
|
||||
return AnimPose(glm::vec3(1), relRot, relTrans);
|
||||
return AnimPose(1.0f, relRot, relTrans);
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& anim
|
|||
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
// Look up refVector from animVars, make sure to convert into geom space.
|
||||
glm::vec3 refVector = midPose.xformVectorFast(_referenceVector);
|
||||
glm::vec3 refVector = midPose.xformVector(_referenceVector);
|
||||
float refVectorLength = glm::length(refVector);
|
||||
|
||||
glm::vec3 axis = basePose.trans() - tipPose.trans();
|
||||
|
|
|
@ -14,15 +14,14 @@
|
|||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "AnimUtil.h"
|
||||
|
||||
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
||||
glm::quat(),
|
||||
glm::vec3(0.0f));
|
||||
const AnimPose AnimPose::identity = AnimPose(1.0f, glm::quat(), glm::vec3(0.0f));
|
||||
|
||||
AnimPose::AnimPose(const glm::mat4& mat) {
|
||||
static const float EPSILON = 0.0001f;
|
||||
_scale = extractScale(mat);
|
||||
glm::vec3 scale = extractScale(mat);
|
||||
// quat_cast doesn't work so well with scaled matrices, so cancel it out.
|
||||
glm::mat4 tmp = glm::scale(mat, 1.0f / _scale);
|
||||
glm::mat4 tmp = glm::scale(mat, 1.0f / scale);
|
||||
_scale = extractUniformScale(scale);
|
||||
_rot = glm::quat_cast(tmp);
|
||||
float lengthSquared = glm::length2(_rot);
|
||||
if (glm::abs(lengthSquared - 1.0f) > EPSILON) {
|
||||
|
@ -40,29 +39,22 @@ glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const {
|
|||
return *this * rhs;
|
||||
}
|
||||
|
||||
// really slow, but accurate for transforms with non-uniform scale
|
||||
glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const {
|
||||
glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f);
|
||||
glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f);
|
||||
glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale.z);
|
||||
glm::mat3 mat(xAxis, yAxis, zAxis);
|
||||
glm::mat3 transInvMat = glm::inverse(glm::transpose(mat));
|
||||
return transInvMat * rhs;
|
||||
}
|
||||
|
||||
// faster, but does not handle non-uniform scale correctly.
|
||||
glm::vec3 AnimPose::xformVectorFast(const glm::vec3& rhs) const {
|
||||
return _rot * (_scale * rhs);
|
||||
}
|
||||
|
||||
AnimPose AnimPose::operator*(const AnimPose& rhs) const {
|
||||
glm::mat4 result;
|
||||
glm_mat4u_mul(*this, rhs, result);
|
||||
return AnimPose(result);
|
||||
float scale = _scale * rhs._scale;
|
||||
glm::quat rot = _rot * rhs._rot;
|
||||
glm::vec3 trans = _trans + (_rot * (_scale * rhs._trans));
|
||||
return AnimPose(scale, rot, trans);
|
||||
}
|
||||
|
||||
AnimPose AnimPose::inverse() const {
|
||||
return AnimPose(glm::inverse(static_cast<glm::mat4>(*this)));
|
||||
float invScale = 1.0f / _scale;
|
||||
glm::quat invRot = glm::inverse(_rot);
|
||||
glm::vec3 invTrans = invScale * (invRot * -_trans);
|
||||
return AnimPose(invScale, invRot, invTrans);
|
||||
}
|
||||
|
||||
// mirror about x-axis without applying negative scale.
|
||||
|
@ -71,11 +63,10 @@ AnimPose AnimPose::mirror() const {
|
|||
}
|
||||
|
||||
AnimPose::operator glm::mat4() const {
|
||||
glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f);
|
||||
glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f);
|
||||
glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale.z);
|
||||
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
|
||||
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||
glm::vec3 xAxis = _rot * glm::vec3(_scale, 0.0f, 0.0f);
|
||||
glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale, 0.0f);
|
||||
glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale);
|
||||
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||
}
|
||||
|
||||
void AnimPose::blend(const AnimPose& srcPose, float alpha) {
|
||||
|
|
|
@ -21,14 +21,13 @@ class AnimPose {
|
|||
public:
|
||||
AnimPose() {}
|
||||
explicit AnimPose(const glm::mat4& mat);
|
||||
explicit AnimPose(const glm::quat& rotIn) : _scale(1.0f), _rot(rotIn), _trans(0.0f) {}
|
||||
AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _scale(1.0f), _rot(rotIn), _trans(transIn) {}
|
||||
AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _scale(scaleIn), _rot(rotIn), _trans(transIn) {}
|
||||
explicit AnimPose(const glm::quat& rotIn) : _rot(rotIn), _trans(0.0f), _scale(1.0f) {}
|
||||
AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _rot(rotIn), _trans(transIn), _scale(1.0f) {}
|
||||
AnimPose(float scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _rot(rotIn), _trans(transIn), _scale(scaleIn) {}
|
||||
static const AnimPose identity;
|
||||
|
||||
glm::vec3 xformPoint(const glm::vec3& rhs) const;
|
||||
glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow, but accurate for transforms with non-uniform scale
|
||||
glm::vec3 xformVectorFast(const glm::vec3& rhs) const; // faster, but does not handle non-uniform scale correctly.
|
||||
|
||||
glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint
|
||||
AnimPose operator*(const AnimPose& rhs) const;
|
||||
|
@ -37,8 +36,8 @@ public:
|
|||
AnimPose mirror() const;
|
||||
operator glm::mat4() const;
|
||||
|
||||
const glm::vec3& scale() const { return _scale; }
|
||||
glm::vec3& scale() { return _scale; }
|
||||
float scale() const { return _scale; }
|
||||
float& scale() { return _scale; }
|
||||
|
||||
const glm::quat& rot() const { return _rot; }
|
||||
glm::quat& rot() { return _rot; }
|
||||
|
@ -50,13 +49,13 @@ public:
|
|||
|
||||
private:
|
||||
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
||||
glm::vec3 _scale { 1.0f };
|
||||
glm::quat _rot;
|
||||
glm::vec3 _trans;
|
||||
float _scale { 1.0f }; // uniform scale only.
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const AnimPose& pose) {
|
||||
debug << "AnimPose, trans = (" << pose.trans().x << pose.trans().y << pose.trans().z << "), rot = (" << pose.rot().x << pose.rot().y << pose.rot().z << pose.rot().w << "), scale = (" << pose.scale().x << pose.scale().y << pose.scale().z << ")";
|
||||
debug << "AnimPose, trans = (" << pose.trans().x << pose.trans().y << pose.trans().z << "), rot = (" << pose.rot().x << pose.rot().y << pose.rot().z << pose.rot().w << "), scale =" << pose.scale();
|
||||
return debug;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const {
|
|||
int index = jointIndex;
|
||||
do {
|
||||
chainDepth++;
|
||||
index = _joints[index].parentIndex;
|
||||
index = _parentIndices[index];
|
||||
} while (index != -1);
|
||||
return chainDepth;
|
||||
} else {
|
||||
|
@ -102,17 +102,12 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
|
|||
return _relativePostRotationPoses[jointIndex];
|
||||
}
|
||||
|
||||
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
||||
return _joints[jointIndex].parentIndex;
|
||||
}
|
||||
|
||||
std::vector<int> AnimSkeleton::getChildrenOfJoint(int jointIndex) const {
|
||||
// Children and grandchildren, etc.
|
||||
std::vector<int> result;
|
||||
if (jointIndex != -1) {
|
||||
for (int i = jointIndex + 1; i < (int)_joints.size(); i++) {
|
||||
if (_joints[i].parentIndex == jointIndex
|
||||
|| (std::find(result.begin(), result.end(), _joints[i].parentIndex) != result.end())) {
|
||||
for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) {
|
||||
if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) {
|
||||
result.push_back(i);
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +123,7 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& relati
|
|||
if (jointIndex < 0 || jointIndex >= (int)relativePoses.size() || jointIndex >= _jointsSize) {
|
||||
return AnimPose::identity;
|
||||
} else {
|
||||
return getAbsolutePose(_joints[jointIndex].parentIndex, relativePoses) * relativePoses[jointIndex];
|
||||
return getAbsolutePose(_parentIndices[jointIndex], relativePoses) * relativePoses[jointIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,7 +131,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const {
|
|||
// poses start off relative and leave in absolute frame
|
||||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||
for (int i = 0; i < lastIndex; ++i) {
|
||||
int parentIndex = _joints[i].parentIndex;
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
poses[i] = poses[parentIndex] * poses[i];
|
||||
}
|
||||
|
@ -147,7 +142,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const {
|
|||
// poses start off absolute and leave in relative frame
|
||||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||
int parentIndex = _joints[i].parentIndex;
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
poses[i] = poses[parentIndex].inverse() * poses[i];
|
||||
}
|
||||
|
@ -158,7 +153,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector<glm::quat>& ro
|
|||
// poses start off absolute and leave in relative frame
|
||||
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||
int parentIndex = _joints[i].parentIndex;
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i];
|
||||
}
|
||||
|
@ -197,6 +192,14 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const {
|
|||
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) {
|
||||
|
||||
_joints = joints;
|
||||
|
||||
// build a seperate vector of parentIndices for cache coherency
|
||||
// AnimSkeleton::getParentIndex is called very frequently in tight loops.
|
||||
_parentIndices.reserve(_joints.size());
|
||||
for (auto& joint : _joints) {
|
||||
_parentIndices.push_back(joint.parentIndex);
|
||||
}
|
||||
|
||||
_jointsSize = (int)joints.size();
|
||||
// build a cache of bind poses
|
||||
|
||||
|
@ -289,8 +292,6 @@ void AnimSkeleton::dump(bool verbose) const {
|
|||
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
|
||||
if (verbose) {
|
||||
qCDebug(animation) << " hfmJoint =";
|
||||
qCDebug(animation) << " isFree =" << _joints[i].isFree;
|
||||
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
|
||||
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
||||
qCDebug(animation) << " translation =" << _joints[i].translation;
|
||||
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
||||
|
|
|
@ -43,7 +43,10 @@ public:
|
|||
// get post transform which might include FBX offset transformations
|
||||
const AnimPose& getPostRotationPose(int jointIndex) const;
|
||||
|
||||
int getParentIndex(int jointIndex) const;
|
||||
int getParentIndex(int jointIndex) const {
|
||||
return _parentIndices[jointIndex];
|
||||
}
|
||||
|
||||
std::vector<int> getChildrenOfJoint(int jointIndex) const;
|
||||
|
||||
AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const;
|
||||
|
@ -69,6 +72,7 @@ protected:
|
|||
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets);
|
||||
|
||||
std::vector<HFMJoint> _joints;
|
||||
std::vector<int> _parentIndices;
|
||||
int _jointsSize { 0 };
|
||||
AnimPoseVec _relativeDefaultPoses;
|
||||
AnimPoseVec _absoluteDefaultPoses;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue