mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
Merge remote-tracking branch 'upstream/master' into integratedStepping
This commit is contained in:
commit
55ae08c071
101 changed files with 2358 additions and 436 deletions
|
@ -39,7 +39,7 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables
|
|||
|
||||
### Step 6. Installing OpenSSL via vcpkg
|
||||
|
||||
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `vcpkg install openssl:x64-windows`
|
||||
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `.\vcpkg install openssl:x64-windows`
|
||||
* Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl`
|
||||
|
||||
### Step 7. Running CMake to Generate Build Files
|
||||
|
|
34
README.md
34
README.md
|
@ -1,8 +1,8 @@
|
|||
High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR.
|
||||
|
||||
In this repository you'll find the source to many of the components in our
|
||||
alpha-stage virtual world. The project embraces distributed development
|
||||
and if you'd like to help, we'll pay you -- find out more at [Worklist.net](https://worklist.net).
|
||||
This repository contains the source to many of the components in our
|
||||
alpha-stage virtual world. The project embraces distributed development.
|
||||
If you'd like to help, we'll pay you -- find out more at [Worklist.net](https://worklist.net).
|
||||
If you find a small bug and have a fix, pull requests are welcome. If you'd
|
||||
like to get paid for your work, make sure you report the bug via a job on
|
||||
[Worklist.net](https://worklist.net).
|
||||
|
@ -32,9 +32,10 @@ Running Interface
|
|||
When you launch interface, you will automatically connect to our default domain: "root.highfidelity.io".
|
||||
|
||||
If you don't see anything, make sure your preferences are pointing to
|
||||
root.highfidelity.io (set your domain via Cmnd+D/Cntrl+D), if you still have no luck it's possible our servers are
|
||||
simply down; if you're experiencing a major bug, let us know by adding an issue to this repository.
|
||||
Make sure to include details about your computer and how to reproduce the bug.
|
||||
root.highfidelity.io (set your domain via Cmnd+D/Cntrl+D). If you still have no luck,
|
||||
it's possible our servers are down. If you're experiencing a major bug, let us know by
|
||||
adding an issue to this repository. Include details about your computer and how to
|
||||
reproduce the bug in your issue.
|
||||
|
||||
To move around in-world, use the arrow keys (and Shift + up/down to fly up or
|
||||
down) or W A S D, and E or C to fly up/down. All of the other possible options
|
||||
|
@ -48,7 +49,8 @@ you to run the full stack of the virtual world.
|
|||
In order to set up your own virtual world, you need to set up and run your own
|
||||
local "domain".
|
||||
|
||||
The domain-server gives a number different types of assignments to the assignment-client for different features: audio, avatars, voxels, particles, meta-voxels and models.
|
||||
The domain-server gives a number different types of assignments to the assignment-client
|
||||
for different features: audio, avatars, voxels, particles, meta-voxels and models.
|
||||
|
||||
Follow the instructions in the [build guide](BUILD.md) to build the various components.
|
||||
|
||||
|
@ -56,7 +58,8 @@ From the domain-server build directory, launch a domain-server.
|
|||
|
||||
./domain-server
|
||||
|
||||
Then, run an assignment-client. The assignment-client uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
|
||||
Then, run an assignment-client. The assignment-client uses localhost as its assignment-server
|
||||
and talks to it on port 40102 (the default domain-server port).
|
||||
|
||||
In a new Terminal window, run:
|
||||
|
||||
|
@ -64,13 +67,20 @@ In a new Terminal window, run:
|
|||
|
||||
Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal window.
|
||||
|
||||
This assignment-client will grab one assignment from the domain-server. You can tell the assignment-client what type you want it to be with the `-t` option. You can also run an assignment-client that forks off *n* assignment-clients with the `-n` option. The `-min` and `-max` options allow you to set a range of required assignment-clients, this allows you to have flexibility in the number of assignment-clients that are running. See `--help` for more options.
|
||||
This assignment-client will grab one assignment from the domain-server. You can tell the
|
||||
assignment-client what type you want it to be with the `-t` option. You can also run an
|
||||
assignment-client that forks off *n* assignment-clients with the `-n` option. The `-min`
|
||||
and `-max` options allow you to set a range of required assignment-clients. This allows
|
||||
you to have flexibility in the number of assignment-clients that are running.
|
||||
See `--help` for more options.
|
||||
|
||||
./assignment-client --min 6 --max 20
|
||||
|
||||
To test things out you'll want to run the Interface client.
|
||||
To test things out, you'll need to run the Interface client.
|
||||
|
||||
To access your local domain in Interface, open your Preferences -- on OS X this is available in the Interface menu, on Linux you'll find it in the File menu. Enter "localhost" in the "Domain server" field.
|
||||
To access your local domain in Interface, open your Preferences. On OS X, this is available
|
||||
in the Interface menu. On Linux, you'll find it in the File menu. Enter "localhost" in the
|
||||
"Domain server" field.
|
||||
|
||||
If everything worked you should see that you are connected to at least one server.
|
||||
If everything worked, you should see that you are connected to at least one server.
|
||||
Nice work!
|
||||
|
|
|
@ -4,7 +4,7 @@ android {
|
|||
compileSdkVersion 26
|
||||
//buildToolsVersion '27.0.3'
|
||||
|
||||
def appVersionCode = Integer.valueOf(RELEASE_NUMBER ?: 1)
|
||||
def appVersionCode = Integer.valueOf(VERSION_CODE ?: 1)
|
||||
def appVersionName = RELEASE_NUMBER ?: "1.0"
|
||||
|
||||
defaultConfig {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="io.highfidelity.hifiinterface.WebViewActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@android:style/Theme.Material.Light.NoActionBar"/>
|
||||
<!-- We don't want to show this on Daydream yet (we need to fix the turn-around problem on this screen)
|
||||
<activity android:name="io.highfidelity.hifiinterface.GvrLoaderActivity">
|
||||
|
|
|
@ -153,10 +153,29 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCrea
|
|||
unpackAndroidAssets();
|
||||
qInstallMessageHandler(oldMessageHandler);
|
||||
|
||||
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [](const QString& a, const bool backToScene) {
|
||||
JavaVM* jvm;
|
||||
env->GetJavaVM(&jvm);
|
||||
|
||||
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [jvm](const QString& a, const bool backToScene, QList<QString> args) {
|
||||
JNIEnv* myNewEnv;
|
||||
JavaVMAttachArgs jvmArgs;
|
||||
jvmArgs.version = JNI_VERSION_1_6; // choose your JNI version
|
||||
jvmArgs.name = NULL; // you might want to give the java thread a name
|
||||
jvmArgs.group = NULL; // you might want to assign the java thread to a ThreadGroup
|
||||
jvm->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&myNewEnv), &jvmArgs);
|
||||
|
||||
QAndroidJniObject string = QAndroidJniObject::fromString(a);
|
||||
jboolean jBackToScene = (jboolean) backToScene;
|
||||
__interfaceActivity.callMethod<void>("openAndroidActivity", "(Ljava/lang/String;Z)V", string.object<jstring>(), jBackToScene);
|
||||
jclass hashMapClass = myNewEnv->FindClass("java/util/HashMap");
|
||||
jmethodID mapClassConstructor = myNewEnv->GetMethodID(hashMapClass, "<init>", "()V");
|
||||
jobject hashmap = myNewEnv->NewObject(hashMapClass, mapClassConstructor);
|
||||
jmethodID mapClassPut = myNewEnv->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
for (const QString& arg: args) {
|
||||
QAndroidJniObject jArg = QAndroidJniObject::fromString(arg);
|
||||
myNewEnv->CallObjectMethod(hashmap, mapClassPut, QAndroidJniObject::fromString("url").object<jstring>(), jArg.object<jstring>());
|
||||
}
|
||||
__interfaceActivity.callMethod<void>("openAndroidActivity", "(Ljava/lang/String;ZLjava/util/HashMap;)V", string.object<jstring>(), jBackToScene, hashmap);
|
||||
jvm->DetachCurrentThread();
|
||||
});
|
||||
|
||||
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::hapticFeedbackRequested, [](int duration) {
|
||||
|
@ -295,4 +314,10 @@ Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterForeground(JNIEn
|
|||
AndroidHelper::instance().notifyEnterForeground();
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_WebViewActivity_nativeProcessURL(JNIEnv* env, jobject obj, jstring url_str) {
|
||||
const char *nativeString = env->GetStringUTFChars(url_str, 0);
|
||||
AndroidHelper::instance().processURL(QString::fromUtf8(nativeString));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -11,38 +11,48 @@
|
|||
|
||||
package io.highfidelity.hifiinterface;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Vibrator;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.WindowManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.SlidingDrawer;
|
||||
|
||||
import org.qtproject.qt5.android.QtLayout;
|
||||
import org.qtproject.qt5.android.QtSurface;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.fragment.WebViewFragment;
|
||||
|
||||
/*import com.google.vr.cardboard.DisplaySynchronizer;
|
||||
import com.google.vr.cardboard.DisplayUtils;
|
||||
import com.google.vr.ndk.base.GvrApi;*/
|
||||
import android.graphics.Point;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class InterfaceActivity extends QtActivity {
|
||||
public class InterfaceActivity extends QtActivity implements WebViewFragment.OnWebViewInteractionListener {
|
||||
|
||||
public static final String DOMAIN_URL = "url";
|
||||
private static final String TAG = "Interface";
|
||||
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;
|
||||
|
||||
private Vibrator mVibrator;
|
||||
|
||||
//public static native void handleHifiURL(String hifiURLString);
|
||||
|
@ -58,6 +68,7 @@ public class InterfaceActivity extends QtActivity {
|
|||
private static boolean inVrMode;
|
||||
|
||||
private boolean nativeEnterBackgroundCallEnqueued = false;
|
||||
private SlidingDrawer webSlidingDrawer;
|
||||
// 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.
|
||||
|
@ -118,6 +129,25 @@ public class InterfaceActivity extends QtActivity {
|
|||
});
|
||||
startActivity(new Intent(this, SplashActivity.class));
|
||||
mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
|
||||
|
||||
FrameLayout mainLayout = findViewById(android.R.id.content);
|
||||
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
webSlidingDrawer = (SlidingDrawer) inflater.inflate(R.layout.web_drawer, mainLayout, false);
|
||||
QtLayout qtLayout = (QtLayout) mainLayout.getChildAt(0);
|
||||
QtLayout.LayoutParams layoutParams = new QtLayout.LayoutParams(webSlidingDrawer.getLayoutParams());
|
||||
webSlidingDrawer.setOnDrawerCloseListener(() -> {
|
||||
WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment");
|
||||
webViewFragment.close();
|
||||
});
|
||||
int widthPx = Math.max(size.x, size.y);
|
||||
int heightPx = Math.min(size.x, size.y);
|
||||
|
||||
layoutParams.x = (int) (widthPx - WEB_DRAWER_RIGHT_MARGIN * getResources().getDisplayMetrics().xdpi / NORMAL_DPI);
|
||||
layoutParams.y = (int) (heightPx - WEB_DRAWER_BOTTOM_MARGIN * getResources().getDisplayMetrics().ydpi / NORMAL_DPI);
|
||||
|
||||
layoutParams.resolveLayoutDirection(View.LAYOUT_DIRECTION_RTL);
|
||||
qtLayout.addView(webSlidingDrawer, layoutParams);
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,6 +179,7 @@ public class InterfaceActivity extends QtActivity {
|
|||
super.onResume();
|
||||
nativeEnterForeground();
|
||||
surfacesWorkaround();
|
||||
keepInterfaceRunning = false;
|
||||
//gvrApi.resumeTracking();
|
||||
}
|
||||
|
||||
|
@ -180,9 +211,16 @@ public class InterfaceActivity extends QtActivity {
|
|||
FrameLayout fl = findViewById(android.R.id.content);
|
||||
if (fl.getChildCount() > 0) {
|
||||
QtLayout qtLayout = (QtLayout) fl.getChildAt(0);
|
||||
if (qtLayout.getChildCount() > 1) {
|
||||
QtSurface s1 = (QtSurface) qtLayout.getChildAt(0);
|
||||
QtSurface s2 = (QtSurface) qtLayout.getChildAt(1);
|
||||
List<QtSurface> surfaces = new ArrayList<>();
|
||||
for (int i = 0; i < qtLayout.getChildCount(); i++) {
|
||||
Object ch = qtLayout.getChildAt(i);
|
||||
if (ch instanceof QtSurface) {
|
||||
surfaces.add((QtSurface) ch);
|
||||
}
|
||||
}
|
||||
if (surfaces.size() > 1) {
|
||||
QtSurface s1 = surfaces.get(0);
|
||||
QtSurface s2 = surfaces.get(1);
|
||||
Integer subLayer1 = 0;
|
||||
Integer subLayer2 = 0;
|
||||
try {
|
||||
|
@ -239,11 +277,16 @@ public class InterfaceActivity extends QtActivity {
|
|||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
if (intent.hasExtra(DOMAIN_URL)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGotoUrl(intent.getStringExtra(DOMAIN_URL));
|
||||
}
|
||||
}
|
||||
|
||||
public void openAndroidActivity(String activityName, boolean backToScene) {
|
||||
openAndroidActivity(activityName, backToScene, null);
|
||||
}
|
||||
|
||||
public void openAndroidActivity(String activityName, boolean backToScene, HashMap args) {
|
||||
switch (activityName) {
|
||||
case "Home":
|
||||
case "Privacy Policy":
|
||||
|
@ -254,6 +297,25 @@ public class InterfaceActivity extends QtActivity {
|
|||
startActivity(intent);
|
||||
break;
|
||||
}
|
||||
case "WebView":
|
||||
runOnUiThread(() -> {
|
||||
webSlidingDrawer.setVisibility(View.VISIBLE);
|
||||
if (!webSlidingDrawer.isOpened()) {
|
||||
webSlidingDrawer.animateOpen();
|
||||
}
|
||||
if (args != null && args.containsKey(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL)) {
|
||||
WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment");
|
||||
webViewFragment.loadUrl((String) args.get(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL), true);
|
||||
webViewFragment.setToolbarVisible(true);
|
||||
webViewFragment.setCloseAction(() -> {
|
||||
if (webSlidingDrawer.isOpened()) {
|
||||
webSlidingDrawer.animateClose();
|
||||
}
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
default: {
|
||||
Log.w(TAG, "Could not open activity by name " + activityName);
|
||||
break;
|
||||
|
@ -278,4 +340,18 @@ public class InterfaceActivity extends QtActivity {
|
|||
public void onBackPressed() {
|
||||
openAndroidActivity("Home", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processURL(String url) { }
|
||||
|
||||
@Override
|
||||
public void onWebLoaded(String url, WebViewFragment.SafenessLevel safenessLevel) { }
|
||||
|
||||
@Override
|
||||
public void onTitleReceived(String title) { }
|
||||
|
||||
@Override
|
||||
public void onExpand() {
|
||||
keepInterfaceRunning = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,93 +8,64 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
//package hifiinterface.highfidelity.io.mybrowserapplication;
|
||||
package io.highfidelity.hifiinterface;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.net.http.SslError;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebResourceError;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
import android.os.Looper;
|
||||
import java.lang.Thread;
|
||||
import java.lang.Runnable;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class WebViewActivity extends Activity {
|
||||
import io.highfidelity.hifiinterface.fragment.WebViewFragment;
|
||||
|
||||
public class WebViewActivity extends Activity implements WebViewFragment.OnWebViewInteractionListener {
|
||||
|
||||
public static final String WEB_VIEW_ACTIVITY_EXTRA_URL = "url";
|
||||
private static final String FRAGMENT_TAG = "WebViewActivity_WebFragment";
|
||||
|
||||
private native void nativeProcessURL(String url);
|
||||
|
||||
private WebView myWebView;
|
||||
private ProgressBar mProgressBar;
|
||||
private ActionBar mActionBar;
|
||||
private String mUrl;
|
||||
|
||||
enum SafenessLevel {
|
||||
NOT_ANALYZED_YET(""),
|
||||
NOT_SECURE(""),
|
||||
SECURE("\uD83D\uDD12 "),
|
||||
BAD_SECURE("\uD83D\uDD13 ");
|
||||
|
||||
String icon;
|
||||
SafenessLevel(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
private SafenessLevel safenessLevel = SafenessLevel.NOT_ANALYZED_YET;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_web_view);
|
||||
|
||||
setActionBar((Toolbar) findViewById(R.id.toolbar_actionbar));
|
||||
setActionBar(findViewById(R.id.toolbar_actionbar));
|
||||
mActionBar = getActionBar();
|
||||
mActionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mProgressBar = (ProgressBar) findViewById(R.id.toolbarProgressBar);
|
||||
loadWebViewFragment(getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL));
|
||||
}
|
||||
|
||||
mUrl = getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL);
|
||||
myWebView = (WebView) findViewById(R.id.web_view);
|
||||
myWebView.setWebViewClient(new HiFiWebViewClient());
|
||||
myWebView.setWebChromeClient(new HiFiWebChromeClient());
|
||||
WebSettings webSettings = myWebView.getSettings();
|
||||
webSettings.setJavaScriptEnabled(true);
|
||||
webSettings.setBuiltInZoomControls(true);
|
||||
webSettings.setDisplayZoomControls(false);
|
||||
myWebView.loadUrl(mUrl);
|
||||
private void loadWebViewFragment(String url) {
|
||||
WebViewFragment fragment = WebViewFragment.newInstance();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(WebViewFragment.URL, url);
|
||||
bundle.putBoolean(WebViewFragment.TOOLBAR_VISIBLE, false);
|
||||
fragment.setArguments(bundle);
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.replace(R.id.content_frame, fragment, FRAGMENT_TAG);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
// Check if the key event was the Back button and if there's history
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
|
||||
myWebView.goBack();
|
||||
WebViewFragment fragment = (WebViewFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
||||
if (fragment != null && fragment.onKeyDown(keyCode)) {
|
||||
return true;
|
||||
}
|
||||
// If it wasn't the Back key or there's no web page history, bubble up to the default
|
||||
|
@ -102,15 +73,6 @@ public class WebViewActivity extends Activity {
|
|||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private void showSubtitleWithUrl(String url) {
|
||||
try {
|
||||
mActionBar.setSubtitle(safenessLevel.icon + new URL(url.toString()).getHost());
|
||||
} catch (MalformedURLException e) {
|
||||
Toast.makeText(WebViewActivity.this, "Error loading page: " + "bad url", Toast.LENGTH_LONG).show();
|
||||
Log.e("openUrl", "bad url");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
|
@ -119,7 +81,7 @@ public class WebViewActivity extends Activity {
|
|||
}
|
||||
|
||||
private String intentUrlOrWebUrl() {
|
||||
return myWebView==null || myWebView.getUrl()==null?mUrl:myWebView.getUrl();
|
||||
return ((WebViewFragment) getFragmentManager().findFragmentById(R.id.content_frame)).intentUrlOrWebUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,98 +107,28 @@ public class WebViewActivity extends Activity {
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
class HiFiWebViewClient extends WebViewClient {
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
mProgressBar.setVisibility(View.GONE);
|
||||
if (safenessLevel!=SafenessLevel.BAD_SECURE) {
|
||||
if (url.startsWith("https:")) {
|
||||
safenessLevel=SafenessLevel.SECURE;
|
||||
} else {
|
||||
safenessLevel=SafenessLevel.NOT_SECURE;
|
||||
}
|
||||
}
|
||||
showSubtitleWithUrl(url);
|
||||
}
|
||||
@Override
|
||||
public void processURL(String url) {
|
||||
nativeProcessURL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
super.onPageStarted(view, url, favicon);
|
||||
safenessLevel = SafenessLevel.NOT_ANALYZED_YET;
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setProgress(0);
|
||||
showSubtitleWithUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
Toast.makeText(WebViewActivity.this, "Error loading page: " + error.getDescription(), Toast.LENGTH_LONG).show();
|
||||
if (ERROR_FAILED_SSL_HANDSHAKE == error.getErrorCode()) {
|
||||
safenessLevel = SafenessLevel.BAD_SECURE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
|
||||
Toast.makeText(WebViewActivity.this, "Network Error loading page: " + errorResponse.getReasonPhrase(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
super.onReceivedSslError(view, handler, error);
|
||||
Toast.makeText(WebViewActivity.this, "SSL error loading page: " + error.toString(), Toast.LENGTH_LONG).show();
|
||||
safenessLevel = SafenessLevel.BAD_SECURE;
|
||||
}
|
||||
|
||||
private boolean isFst(WebResourceRequest request) {
|
||||
return isFst(request.getUrl().toString());
|
||||
}
|
||||
|
||||
private boolean isFst(String url) {
|
||||
return url.endsWith(".fst");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
// managing avatar selections
|
||||
if (isFst(request)) {
|
||||
final String url = request.getUrl().toString();
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
nativeProcessURL(url);
|
||||
}
|
||||
}).start(); // Avoid deadlock in Qt dialog
|
||||
WebViewActivity.this.finish();
|
||||
return true;
|
||||
}
|
||||
return super.shouldOverrideUrlLoading(view, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadResource(WebView view, String url) {
|
||||
if (isFst(url)) {
|
||||
// processed separately
|
||||
} else {
|
||||
super.onLoadResource(view, url);
|
||||
}
|
||||
@Override
|
||||
public void onWebLoaded(String url, WebViewFragment.SafenessLevel safenessLevel) {
|
||||
try {
|
||||
mActionBar.setSubtitle(safenessLevel.icon + new URL(url.toString()).getHost());
|
||||
} catch (MalformedURLException e) {
|
||||
Toast.makeText(WebViewActivity.this, "Error loading page: " + "bad url", Toast.LENGTH_LONG).show();
|
||||
Log.e("openUrl", "bad url");
|
||||
}
|
||||
}
|
||||
|
||||
class HiFiWebChromeClient extends WebChromeClient {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
super.onProgressChanged(view, newProgress);
|
||||
mProgressBar.setProgress(newProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedTitle(WebView view, String title) {
|
||||
super.onReceivedTitle(view, title);
|
||||
mActionBar.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleReceived(String title) {
|
||||
mActionBar.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExpand() { }
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,343 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.http.SslError;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebResourceError;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.WebViewActivity;
|
||||
|
||||
public class WebViewFragment extends Fragment implements GestureDetector.OnGestureListener {
|
||||
|
||||
public static final String URL = "url";
|
||||
public static final String TOOLBAR_VISIBLE = "toolbar_visible";
|
||||
private static final long DELAY_HIDE_TOOLBAR_MILLIS = 3000;
|
||||
private static final long FADE_OUT_DURATION = 2000;
|
||||
|
||||
private WebView myWebView;
|
||||
private GestureDetector gestureDetector;
|
||||
private View mToolbar;
|
||||
private ProgressBar mProgressBar;
|
||||
private String mUrl;
|
||||
private boolean mToolbarVisible;
|
||||
|
||||
private OnWebViewInteractionListener mListener;
|
||||
private Runnable mCloseAction;
|
||||
private Handler mHandler;
|
||||
|
||||
private Runnable mHideToolbar = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mToolbar != null) {
|
||||
AlphaAnimation anim = new AlphaAnimation(1.0f, 0.0f);
|
||||
anim.setDuration(FADE_OUT_DURATION);
|
||||
anim.setFillAfter(true);
|
||||
mToolbar.startAnimation(anim);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public boolean onKeyDown(int keyCode) {
|
||||
// Check if the key event was the Back button and if there's history
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
|
||||
myWebView.goBack();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String intentUrlOrWebUrl() {
|
||||
return myWebView == null || myWebView.getUrl() == null ? mUrl : myWebView.getUrl();
|
||||
}
|
||||
|
||||
public void loadUrl(String url, boolean showToolbar) {
|
||||
mUrl = url;
|
||||
mToolbarVisible = showToolbar;
|
||||
loadUrl(myWebView, url);
|
||||
}
|
||||
|
||||
private void loadUrl(WebView webView, String url) {
|
||||
webView.setVisibility(View.GONE);
|
||||
webView.getSettings().setLoadWithOverviewMode(true);
|
||||
webView.getSettings().setUseWideViewPort(true);
|
||||
webView.loadUrl(url);
|
||||
mToolbar.setVisibility(mToolbarVisible ? View.VISIBLE : View.GONE);
|
||||
mToolbar.clearAnimation();
|
||||
if (mToolbarVisible) {
|
||||
mHandler.postDelayed(mHideToolbar, DELAY_HIDE_TOOLBAR_MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
public void setToolbarVisible(boolean visible) {
|
||||
mToolbar.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void setCloseAction(Runnable closeAction) {
|
||||
this.mCloseAction = closeAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent motionEvent) {
|
||||
mHandler.removeCallbacks(mHideToolbar);
|
||||
if (mToolbarVisible) {
|
||||
mToolbar.setVisibility(mToolbarVisible ? View.VISIBLE : View.GONE);
|
||||
mToolbar.clearAnimation();
|
||||
mHandler.postDelayed(mHideToolbar, DELAY_HIDE_TOOLBAR_MILLIS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent motionEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent motionEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent motionEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
myWebView.loadUrl("about:blank");
|
||||
if (mCloseAction != null) {
|
||||
mCloseAction.run();
|
||||
}
|
||||
}
|
||||
|
||||
public enum SafenessLevel {
|
||||
NOT_ANALYZED_YET(""),
|
||||
NOT_SECURE(""),
|
||||
SECURE("\uD83D\uDD12 "),
|
||||
BAD_SECURE("\uD83D\uDD13 ");
|
||||
|
||||
public String icon;
|
||||
SafenessLevel(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
private SafenessLevel safenessLevel = SafenessLevel.NOT_ANALYZED_YET;
|
||||
|
||||
|
||||
public WebViewFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static WebViewFragment newInstance() {
|
||||
WebViewFragment fragment = new WebViewFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) {
|
||||
mUrl = getArguments().getString(URL);
|
||||
mToolbarVisible = getArguments().getBoolean(TOOLBAR_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_web_view, container, false);
|
||||
mProgressBar = rootView.findViewById(R.id.toolbarProgressBar);
|
||||
myWebView = rootView.findViewById(R.id.web_view);
|
||||
mHandler = new Handler();
|
||||
gestureDetector = new GestureDetector(this);
|
||||
gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent motionEvent) {
|
||||
expand();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTapEvent(MotionEvent motionEvent) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
myWebView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
|
||||
|
||||
myWebView.setWebViewClient(new HiFiWebViewClient());
|
||||
myWebView.setWebChromeClient(new HiFiWebChromeClient());
|
||||
WebSettings webSettings = myWebView.getSettings();
|
||||
webSettings.setJavaScriptEnabled(true);
|
||||
webSettings.setBuiltInZoomControls(true);
|
||||
webSettings.setDisplayZoomControls(false);
|
||||
|
||||
mToolbar = rootView.findViewById(R.id.toolbar);
|
||||
mToolbar.findViewById(R.id.viewFullScreen).setOnClickListener(view -> {
|
||||
expand();
|
||||
});
|
||||
mToolbar.findViewById(R.id.close).setOnClickListener(view -> {
|
||||
close();
|
||||
});
|
||||
if (mUrl != null) {
|
||||
loadUrl(myWebView, mUrl);
|
||||
}
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void expand() {
|
||||
if (mListener != null) {
|
||||
mListener.onExpand();
|
||||
}
|
||||
Intent intent = new Intent(getActivity(), WebViewActivity.class);
|
||||
intent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL, intentUrlOrWebUrl());
|
||||
getActivity().startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnWebViewInteractionListener) {
|
||||
mListener = (OnWebViewInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnWebViewInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public interface OnWebViewInteractionListener {
|
||||
void processURL(String url);
|
||||
void onWebLoaded(String url, SafenessLevel safenessLevel);
|
||||
void onTitleReceived(String title);
|
||||
void onExpand();
|
||||
}
|
||||
|
||||
|
||||
class HiFiWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
mProgressBar.setVisibility(View.GONE);
|
||||
if (safenessLevel!= SafenessLevel.BAD_SECURE) {
|
||||
if (url.startsWith("https:")) {
|
||||
safenessLevel = SafenessLevel.SECURE;
|
||||
} else {
|
||||
safenessLevel = SafenessLevel.NOT_SECURE;
|
||||
}
|
||||
}
|
||||
if (mListener != null) {
|
||||
myWebView.setVisibility(View.VISIBLE);
|
||||
mListener.onWebLoaded(url, safenessLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
super.onPageStarted(view, url, favicon);
|
||||
safenessLevel = SafenessLevel.NOT_ANALYZED_YET;
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setProgress(0);
|
||||
if (mListener != null) {
|
||||
myWebView.setVisibility(View.VISIBLE);
|
||||
mListener.onWebLoaded(url, safenessLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
Toast.makeText(getActivity(), "Error loading page: " + error.getDescription(), Toast.LENGTH_LONG).show();
|
||||
if (ERROR_FAILED_SSL_HANDSHAKE == error.getErrorCode()) {
|
||||
safenessLevel = SafenessLevel.BAD_SECURE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
|
||||
Toast.makeText(getActivity(), "Network Error loading page: " + errorResponse.getReasonPhrase(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
super.onReceivedSslError(view, handler, error);
|
||||
Toast.makeText(getActivity(), "SSL error loading page: " + error.toString(), Toast.LENGTH_LONG).show();
|
||||
safenessLevel = SafenessLevel.BAD_SECURE;
|
||||
}
|
||||
|
||||
private boolean isFst(WebResourceRequest request) {
|
||||
return isFst(request.getUrl().toString());
|
||||
}
|
||||
|
||||
private boolean isFst(String url) {
|
||||
return url.contains(".fst");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadResource(WebView view, String url) {
|
||||
if (isFst(url)) {
|
||||
// processed separately
|
||||
} else {
|
||||
super.onLoadResource(view, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HiFiWebChromeClient extends WebChromeClient {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
super.onProgressChanged(view, newProgress);
|
||||
mProgressBar.setProgress(newProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedTitle(WebView view, String title) {
|
||||
super.onReceivedTitle(view, title);
|
||||
if (mListener != null) {
|
||||
mListener.onTitleReceived(title);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -69,6 +69,7 @@ public class QtActivity extends Activity {
|
|||
private QtActivityLoader m_loader = new QtActivityLoader(this);
|
||||
|
||||
public boolean isLoading;
|
||||
public boolean keepInterfaceRunning;
|
||||
|
||||
public QtActivity() {
|
||||
}
|
||||
|
@ -503,7 +504,7 @@ public class QtActivity extends Activity {
|
|||
super.onPause();
|
||||
// GC: this trick allow us to show a splash activity until Qt app finishes
|
||||
// loading
|
||||
if (!isLoading) {
|
||||
if (!isLoading && !keepInterfaceRunning) {
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
}
|
||||
|
@ -644,7 +645,9 @@ public class QtActivity extends Activity {
|
|||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
QtApplication.invokeDelegate();
|
||||
if (!keepInterfaceRunning) {
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
23
android/app/src/main/res/drawable/ic_close.xml
Normal file
23
android/app/src/main/res/drawable/ic_close.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="vector"
|
||||
android:width="173dp"
|
||||
android:height="173dp"
|
||||
android:viewportWidth="173"
|
||||
android:viewportHeight="173">
|
||||
<path
|
||||
android:name="path"
|
||||
android:pathData="M 86.5 173 C 134.273 173 173 134.273 173 86.5 C 173 38.727 134.273 0 86.5 0 C 38.727 0 0 38.727 0 86.5 C 0 134.273 38.727 173 86.5 173 Z"
|
||||
android:fillColor="#181818"
|
||||
android:fillAlpha="0.6"
|
||||
android:strokeWidth="1"/>
|
||||
<path
|
||||
android:name="path_1"
|
||||
android:pathData="M 53.3 53.3 L 119.7 119.7 M 53.3 119.7 L 119.7 53.3"
|
||||
android:fillColor="#000"
|
||||
android:fillAlpha="0.6"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeWidth="13.5424"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
20
android/app/src/main/res/drawable/ic_expand.xml
Normal file
20
android/app/src/main/res/drawable/ic_expand.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="vector"
|
||||
android:width="173dp"
|
||||
android:height="173dp"
|
||||
android:viewportWidth="173"
|
||||
android:viewportHeight="173">
|
||||
<path
|
||||
android:name="path"
|
||||
android:pathData="M 86.5 173 C 134.273 173 173 134.273 173 86.5 C 173 38.727 134.273 0 86.5 0 C 38.727 0 0 38.727 0 86.5 C 0 134.273 38.727 173 86.5 173 Z"
|
||||
android:fillColor="#181818"
|
||||
android:fillAlpha="0.6"
|
||||
android:strokeWidth="1"/>
|
||||
<path
|
||||
android:name="path_2"
|
||||
android:pathData="M 52.4 52.4 C 52.4 58.4 52.3 63.7 52.4 69 C 52.5 71.8 51.5 72.7 48.7 72.7 C 37.6 72.4 39.7 74.1 39.6 63.6 C 39.5 56.9 39.7 50.1 39.5 43.4 C 39.4 40.4 40.5 39.5 43.4 39.6 C 51.8 39.7 60.2 39.7 68.7 39.6 C 71.6 39.6 72.7 40.4 72.6 43.4 C 72.3 54.2 73.9 52.2 63.8 52.4 L 52.4 52.4 Z M 120.6 52.4 C 114.8 52.4 109.5 52.3 104.2 52.5 C 101.3 52.6 100.2 51.6 100.3 48.7 C 100.6 38 99 39.9 109.1 39.7 C 116 39.6 122.9 39.8 129.7 39.6 C 132.5 39.5 133.4 40.5 133.4 43.3 C 133.3 52 133.3 60.7 133.4 69.4 C 133.4 71.7 132.7 72.8 130.3 72.7 C 118.4 72.5 120.9 74.1 120.6 63.7 L 120.6 52.4 Z M 52.4 120.6 C 58.4 120.6 63.7 120.7 69 120.6 C 71.7 120.5 72.8 121.4 72.7 124.2 C 72.4 135.4 74 133.2 63.7 133.4 C 56.8 133.5 49.9 133.3 43.1 133.5 C 40.6 133.5 39.6 132.7 39.6 130.1 C 39.7 121.4 39.7 112.7 39.6 104 C 39.6 101.6 40.2 100.4 42.9 100.4 C 54.5 100.6 52.2 99 52.4 109.5 C 52.5 113 52.4 116.4 52.4 120.6 Z M 120.6 120.6 C 120.6 114.6 120.7 109.4 120.6 104.2 C 120.5 101.3 121.4 100.2 124.4 100.2 C 135.2 100.5 133.3 98.9 133.4 109 C 133.5 115.9 133.3 122.8 133.5 129.6 C 133.6 132.3 132.7 133.3 129.9 133.3 C 121.2 133.2 112.5 133.2 103.8 133.3 C 101.2 133.3 100.4 132.3 100.4 129.8 C 100.6 118.5 99.1 120.7 109.3 120.5 C 112.9 120.6 116.5 120.6 120.6 120.6 Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeWidth="1"/>
|
||||
</vector>
|
|
@ -1,8 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Toolbar
|
||||
android:id="@+id/toolbar_actionbar"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -16,19 +17,9 @@
|
|||
android:contentInsetStartWithNavigation="0dp"
|
||||
android:title="">
|
||||
</Toolbar>
|
||||
|
||||
<WebView
|
||||
android:id="@+id/web_view"
|
||||
<FrameLayout
|
||||
android:id="@+id/content_frame"
|
||||
android:layout_below="@id/toolbar_actionbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/toolbarProgressBar"
|
||||
android:layout_below="@id/toolbar_actionbar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:indeterminate="false"
|
||||
android:padding="0dp" />
|
||||
android:layout_height="match_parent" />
|
||||
</RelativeLayout>
|
42
android/app/src/main/res/layout/fragment_web_view.xml
Normal file
42
android/app/src/main/res/layout/fragment_web_view.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<WebView
|
||||
android:id="@+id/web_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:visibility="gone">
|
||||
<ImageView
|
||||
android:id="@+id/viewFullScreen"
|
||||
android:layout_width="31dp"
|
||||
android:layout_height="31dp"
|
||||
android:src="@drawable/ic_expand" />
|
||||
<ImageView
|
||||
android:id="@+id/close"
|
||||
android:layout_width="31dp"
|
||||
android:layout_height="31dp"
|
||||
app:layout_constraintLeft_toRightOf="@id/viewFullScreen"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:src="@drawable/ic_close" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/toolbarProgressBar"
|
||||
android:layout_below="@id/toolbar_actionbar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:indeterminate="false"
|
||||
android:padding="0dp" />
|
||||
</RelativeLayout>
|
24
android/app/src/main/res/layout/web_drawer.xml
Normal file
24
android/app/src/main/res/layout/web_drawer.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<SlidingDrawer android:id="@+id/drawer"
|
||||
android:layout_width="218dp"
|
||||
android:layout_height="125dp"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:layout_marginBottom="11dp"
|
||||
android:layout_marginRight="11dp"
|
||||
android:handle="@+id/handle"
|
||||
android:content="@+id/content"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<ImageView
|
||||
android:id="@id/handle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"/>
|
||||
<fragment
|
||||
android:id="@id/content"
|
||||
android:name="io.highfidelity.hifiinterface.fragment.WebViewFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:tag="webViewFragment"
|
||||
/>
|
||||
|
||||
</SlidingDrawer>
|
|
@ -37,6 +37,7 @@ task clean(type: Delete) {
|
|||
|
||||
ext {
|
||||
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
|
||||
VERSION_CODE = project.hasProperty('VERSION_CODE') ? project.getProperty('VERSION_CODE') : '0'
|
||||
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
|
||||
STABLE_BUILD = project.hasProperty('STABLE_BUILD') ? project.getProperty('STABLE_BUILD') : '0'
|
||||
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
|
||||
|
|
|
@ -53,6 +53,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket");
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedAvatarIdentity,
|
||||
|
@ -602,6 +603,31 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
|
|||
_handleAvatarIdentityPacketElapsedTime += (end - start);
|
||||
}
|
||||
|
||||
void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
if (message->getSize() < NUM_BYTES_RFC4122_UUID) {
|
||||
qCDebug(avatars) << "Malformed AvatarIdentityRequest received from" << message->getSenderSockAddr().toString();
|
||||
return;
|
||||
}
|
||||
|
||||
QUuid avatarID(QUuid::fromRfc4122(message->getMessage()) );
|
||||
if (!avatarID.isNull()) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto node = nodeList->nodeWithUUID(avatarID);
|
||||
if (node) {
|
||||
QMutexLocker lock(&node->getMutex());
|
||||
AvatarMixerClientData* avatarClientData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
if (avatarClientData) {
|
||||
const AvatarData& avatarData = avatarClientData->getAvatar();
|
||||
QByteArray serializedAvatar = avatarData.identityByteArray();
|
||||
auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
|
||||
identityPackets->write(serializedAvatar);
|
||||
nodeList->sendPacketList(std::move(identityPackets), *senderNode);
|
||||
++_sumIdentityPackets;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
||||
auto start = usecTimestampNow();
|
||||
handleAvatarKilled(node);
|
||||
|
|
|
@ -54,6 +54,7 @@ private slots:
|
|||
void handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleReplicatedPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleAvatarIdentityRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void domainSettingsRequestComplete();
|
||||
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
||||
void start();
|
||||
|
|
|
@ -17,6 +17,9 @@ EntityTreeHeadlessViewer::EntityTreeHeadlessViewer()
|
|||
}
|
||||
|
||||
EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
|
||||
if (_simulation) {
|
||||
_simulation->setEntityTree(nullptr); // Break shared_ptr cycle.
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeHeadlessViewer::init() {
|
||||
|
|
|
@ -69,9 +69,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
DependencyManager::set<ScriptCache>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
|
||||
|
||||
DependencyManager::set<EntityScriptServerServices>();
|
||||
|
||||
|
||||
// Needed to ensure the creation of the DebugDraw instance on the main thread
|
||||
|
@ -254,6 +251,9 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer<Recei
|
|||
|
||||
|
||||
void EntityScriptServer::run() {
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
|
||||
DependencyManager::set<EntityScriptServerServices>();
|
||||
|
||||
// make sure we request our script once the agent connects to the domain
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -571,6 +571,8 @@ void EntityScriptServer::aboutToFinish() {
|
|||
entityScriptingInterface->setPacketSender(nullptr);
|
||||
}
|
||||
|
||||
DependencyManager::destroy<AssignmentParentFinder>();
|
||||
|
||||
DependencyManager::get<ResourceManager>()->cleanup();
|
||||
|
||||
DependencyManager::destroy<PluginManager>();
|
||||
|
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68-v2.zip
|
||||
URL_MD5 f7d290471baf7f5694c147217b8fc548
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC69.zip
|
||||
URL_MD5 e2467b08de069da7e22ec8e032435592
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -90,7 +90,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
# for the second parent of HEAD (not HEAD) since that is the
|
||||
# SHA of the commit merged to master for the build
|
||||
if (PR_BUILD)
|
||||
set(_GIT_LOG_FORMAT "%p")
|
||||
set(_GIT_LOG_FORMAT "%p %h")
|
||||
else ()
|
||||
set(_GIT_LOG_FORMAT "%h")
|
||||
endif ()
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
{
|
||||
"name": "Keyboard/Mouse to Actions",
|
||||
"channels": [
|
||||
{ "from": "Keyboard.A", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Q", "to": "Actions.LATERAL_LEFT" },
|
||||
|
||||
{ "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "when": "!Keyboard.Control", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Q", "when": "!Keyboard.Control", "to": "Actions.LATERAL_LEFT" },
|
||||
|
||||
{ "comment" : "Mouse turn need to be small continuous increments",
|
||||
"from": { "makeAxis" : [
|
||||
|
@ -87,21 +86,41 @@
|
|||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A", "Keyboard.TouchpadLeft"],
|
||||
["Keyboard.D", "Keyboard.TouchpadRight"]
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPerson", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraThirdPerson", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraFirstPerson",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A", "Keyboard.TouchpadLeft"],
|
||||
["Keyboard.D", "Keyboard.TouchpadRight"]
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraThirdPerson",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
|
||||
"when": "Keyboard.RightMouseButton",
|
||||
"to": "Actions.Yaw",
|
||||
|
|
BIN
interface/resources/fonts/Cairo-SemiBold.ttf
Executable file
BIN
interface/resources/fonts/Cairo-SemiBold.ttf
Executable file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 229 KiB |
3
interface/resources/icons/+android/hand.svg
Normal file
3
interface/resources/icons/+android/hand.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="123" height="126" viewBox="0 0 123 126" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.85" d="M120.429 69.9586C124.864 79.9597 123.148 89.9608 118.427 99.3905C116.71 102.819 114.421 105.963 112.418 109.249C101.689 114.678 90.8162 120.107 80.0867 125.679C73.0768 126.822 66.6391 124.822 60.7737 121.107C52.7623 115.821 45.1802 110.249 37.455 104.677C31.3034 100.248 25.1518 95.6758 18.8572 91.2467C13.421 87.532 11.4181 81.5313 13.9932 76.245C16.7113 70.673 22.4337 68.5299 28.8714 70.5301C31.8756 71.5302 35.0229 72.3874 38.4564 73.3875C35.5952 67.8155 32.8771 62.2434 29.8728 56.6714C29.5867 56.0999 28.013 55.8141 27.1547 55.8141C15.4238 55.3855 5.69575 48.3847 1.54703 37.3835C-2.17252 27.2395 0.974791 15.0953 9.27224 7.52302C19.0003 -1.33511 33.3062 -2.4781 44.0357 4.66556C54.7652 11.8092 59.0569 24.6678 54.479 37.5264C54.336 37.955 54.0499 38.5265 53.6207 39.3837C58.6278 37.5264 62.9196 38.0979 66.6391 41.0982C73.3629 34.5261 77.0824 33.9546 84.2354 37.955C87.0966 36.2405 89.5286 34.2403 92.3898 33.3831C97.2538 31.8115 102.547 33.9546 104.979 38.5265C110.129 48.9562 115.709 59.2431 120.429 69.9586ZM113.133 97.2474C117.282 88.5321 118.713 79.674 114.564 70.673C109.986 60.9576 104.979 51.528 100.115 41.9555C98.5414 38.9551 95.251 37.8121 92.3898 39.3837C89.3856 40.9553 88.3841 43.9557 89.9578 47.0989C91.8176 50.8136 93.6773 54.5283 95.5371 58.243C96.5385 60.1003 96.9677 61.9577 94.6788 63.1007C92.3898 64.2437 91.2453 62.6721 90.2439 60.8147C87.5258 55.3855 84.8077 50.0992 82.0895 44.67C80.5159 41.6697 77.2255 40.5267 74.3643 41.9555C71.5031 43.3842 70.3587 46.6703 71.9323 49.6706C73.7921 53.5282 75.6518 57.2429 77.6547 60.9576C78.513 62.8149 78.7992 64.5294 76.7963 65.6724C74.6504 66.8154 73.3629 65.3867 72.3615 63.5293C69.6433 58.1001 66.9252 52.8138 64.2071 47.3846C62.4904 44.0985 59.3431 43.0984 56.3388 44.67C53.3346 46.2416 52.4762 49.242 54.0499 52.5281C55.7666 56.0999 57.6264 59.8146 59.4861 63.3864C60.7737 65.9581 60.4875 67.8155 58.7708 68.5299C56.4819 69.3871 55.3374 67.9584 54.336 66.101C52.1901 61.672 49.9011 57.3858 47.7552 53.0996C42.8912 43.5271 38.0272 33.9546 33.1632 24.3821C31.3034 20.6674 27.2977 19.8101 24.1504 22.3818C22.0045 24.0963 21.4323 26.8109 23.006 29.6684C26.8686 37.5264 30.8742 45.2415 34.8799 53.0996C38.8856 60.9576 42.7482 68.6727 46.7538 76.3879C47.6122 77.9595 48.4705 79.5311 46.8969 80.6741C45.8955 81.3884 44.0357 81.5313 42.7482 81.1027C37.455 79.5311 32.1618 77.5309 26.8686 75.8164C23.7212 74.8163 21.0031 75.8164 19.5725 78.2452C18.1419 80.6741 18.7142 83.3887 21.1462 85.5318C21.5754 85.9604 22.1476 86.2461 22.7198 86.6747C26.8686 89.6751 31.1603 92.3897 35.3091 95.5329C42.7482 101.105 50.0442 107.248 57.9125 112.535C62.2043 115.535 67.2113 117.535 72.0754 119.536C74.9365 120.821 77.9408 120.25 80.9451 118.678C88.2411 114.821 95.5371 110.82 103.119 107.534C107.983 105.391 110.988 101.819 113.133 97.2474ZM46.8969 38.9551C52.1901 31.8115 51.3317 20.6674 45.3232 13.6666C38.4564 5.66567 27.2977 3.66545 17.5697 8.66601C8.41388 13.5237 3.69292 24.0963 6.41105 33.8117C8.84306 42.9556 17.7128 50.0992 26.4394 49.6706C23.5782 43.9557 20.717 38.3836 17.8558 32.8116C14.5654 26.2394 16.5683 19.5244 22.7198 16.524C28.7283 13.5237 35.166 15.9525 38.4564 22.3818C41.1745 27.811 43.8926 33.2402 46.8969 38.9551Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
76
interface/resources/qml/+android/Web3DOverlay.qml
Normal file
76
interface/resources/qml/+android/Web3DOverlay.qml
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Copyright 2016 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 QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
|
||||
property string url
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#262626" }
|
||||
GradientStop { position: 1.0; color: "#000000" }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
287
interface/resources/qml/InteractiveWindow.qml
Normal file
287
interface/resources/qml/InteractiveWindow.qml
Normal file
|
@ -0,0 +1,287 @@
|
|||
//
|
||||
// InteractiveWindow.qml
|
||||
//
|
||||
// Created by Thijs Wenker on 2018-06-25
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.3
|
||||
|
||||
import "windows" as Windows
|
||||
import "controls"
|
||||
import "controls-uit" as Controls
|
||||
import "styles"
|
||||
import "styles-uit"
|
||||
|
||||
Windows.Window {
|
||||
id: root;
|
||||
HifiConstants { id: hifi }
|
||||
title: "InteractiveWindow";
|
||||
resizable: true;
|
||||
// Virtual window visibility
|
||||
shown: false;
|
||||
focus: true;
|
||||
property var channel;
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false;
|
||||
|
||||
signal selfDestruct();
|
||||
|
||||
property var flags: 0;
|
||||
|
||||
property var source;
|
||||
property var dynamicContent;
|
||||
property var nativeWindow;
|
||||
|
||||
// custom visibility flag for interactiveWindow to proxy virtualWindow.shown / nativeWindow.visible
|
||||
property var interactiveWindowVisible: true;
|
||||
|
||||
property point interactiveWindowPosition;
|
||||
|
||||
property size interactiveWindowSize;
|
||||
|
||||
// Keyboard control properties in case needed by QML content.
|
||||
property bool keyboardEnabled: false;
|
||||
property bool keyboardRaised: false;
|
||||
property bool punctuationMode: false;
|
||||
|
||||
property int presentationMode: 0;
|
||||
|
||||
property var initialized: false;
|
||||
onSourceChanged: {
|
||||
if (dynamicContent) {
|
||||
dynamicContent.destroy();
|
||||
dynamicContent = null;
|
||||
}
|
||||
QmlSurface.load(source, contentHolder, function(newObject) {
|
||||
dynamicContent = newObject;
|
||||
if (dynamicContent && dynamicContent.anchors) {
|
||||
dynamicContent.anchors.fill = contentHolder;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateInteractiveWindowPositionForMode() {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
x = interactiveWindowPosition.x;
|
||||
y = interactiveWindowPosition.y;
|
||||
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
|
||||
if (interactiveWindowPosition.x === 0 && interactiveWindowPosition.y === 0) {
|
||||
// default position for native window in center of main application window
|
||||
nativeWindow.x = Math.floor(Window.x + (Window.innerWidth / 2) - (interactiveWindowSize.width / 2));
|
||||
nativeWindow.y = Math.floor(Window.y + (Window.innerHeight / 2) - (interactiveWindowSize.height / 2));
|
||||
} else {
|
||||
nativeWindow.x = interactiveWindowPosition.x;
|
||||
nativeWindow.y = interactiveWindowPosition.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateInteractiveWindowSizeForMode() {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
width = interactiveWindowSize.width;
|
||||
height = interactiveWindowSize.height;
|
||||
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
|
||||
nativeWindow.width = interactiveWindowSize.width;
|
||||
nativeWindow.height = interactiveWindowSize.height;
|
||||
}
|
||||
}
|
||||
|
||||
function updateContentParent() {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
contentHolder.parent = root;
|
||||
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
|
||||
contentHolder.parent = nativeWindow.contentItem;
|
||||
}
|
||||
}
|
||||
|
||||
function setupPresentationMode() {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
if (nativeWindow) {
|
||||
nativeWindow.setVisible(false);
|
||||
}
|
||||
updateContentParent();
|
||||
updateInteractiveWindowPositionForMode();
|
||||
shown = interactiveWindowVisible;
|
||||
} else if (presentationMode === Desktop.PresentationMode.NATIVE) {
|
||||
shown = false;
|
||||
if (nativeWindow) {
|
||||
updateContentParent();
|
||||
updateInteractiveWindowPositionForMode();
|
||||
nativeWindow.setVisible(interactiveWindowVisible);
|
||||
}
|
||||
} else if (presentationMode === modeNotSet) {
|
||||
console.error("presentationMode should be set.");
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Fix for parent loss on OSX:
|
||||
parent.heightChanged.connect(function() {
|
||||
updateContentParent();
|
||||
});
|
||||
parent.widthChanged.connect(function() {
|
||||
updateContentParent();
|
||||
});
|
||||
|
||||
x = interactiveWindowPosition.x;
|
||||
y = interactiveWindowPosition.y;
|
||||
width = interactiveWindowSize.width;
|
||||
height = interactiveWindowSize.height;
|
||||
|
||||
nativeWindow = Qt.createQmlObject('
|
||||
import QtQuick 2.3;
|
||||
import QtQuick.Window 2.3;
|
||||
|
||||
Window {
|
||||
id: root;
|
||||
Rectangle {
|
||||
color: hifi.colors.baseGray
|
||||
anchors.fill: parent
|
||||
}
|
||||
}', root, 'InteractiveWindow.qml->nativeWindow');
|
||||
nativeWindow.title = root.title;
|
||||
var nativeWindowFlags = Qt.Window |
|
||||
Qt.WindowTitleHint |
|
||||
Qt.WindowSystemMenuHint |
|
||||
Qt.WindowCloseButtonHint |
|
||||
Qt.WindowMaximizeButtonHint |
|
||||
Qt.WindowMinimizeButtonHint;
|
||||
if ((flags & Desktop.ALWAYS_ON_TOP) === Desktop.ALWAYS_ON_TOP) {
|
||||
nativeWindowFlags |= Qt.WindowStaysOnTopHint;
|
||||
}
|
||||
nativeWindow.flags = nativeWindowFlags;
|
||||
|
||||
nativeWindow.x = interactiveWindowPosition.x;
|
||||
nativeWindow.y = interactiveWindowPosition.y;
|
||||
|
||||
nativeWindow.width = interactiveWindowSize.width;
|
||||
nativeWindow.height = interactiveWindowSize.height;
|
||||
|
||||
nativeWindow.xChanged.connect(function() {
|
||||
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
|
||||
interactiveWindowPosition = Qt.point(nativeWindow.x, interactiveWindowPosition.y);
|
||||
}
|
||||
});
|
||||
nativeWindow.yChanged.connect(function() {
|
||||
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
|
||||
interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, nativeWindow.y);
|
||||
}
|
||||
});
|
||||
|
||||
nativeWindow.widthChanged.connect(function() {
|
||||
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
|
||||
interactiveWindowSize = Qt.size(nativeWindow.width, interactiveWindowSize.height);
|
||||
}
|
||||
});
|
||||
nativeWindow.heightChanged.connect(function() {
|
||||
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
|
||||
interactiveWindowSize = Qt.size(interactiveWindowSize.width, nativeWindow.height);
|
||||
}
|
||||
});
|
||||
|
||||
nativeWindow.closing.connect(function(closeEvent) {
|
||||
closeEvent.accepted = false;
|
||||
windowClosed();
|
||||
});
|
||||
|
||||
// finally set the initial window mode:
|
||||
setupPresentationMode();
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// Handle message traffic from the script that launched us to the loaded QML
|
||||
function fromScript(message) {
|
||||
if (root.dynamicContent && root.dynamicContent.fromScript) {
|
||||
root.dynamicContent.fromScript(message);
|
||||
}
|
||||
}
|
||||
|
||||
function show() {
|
||||
interactiveWindowVisible = true;
|
||||
raiseWindow();
|
||||
}
|
||||
|
||||
function raiseWindow() {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
raise();
|
||||
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
|
||||
nativeWindow.raise();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle message traffic from our loaded QML to the script that launched us
|
||||
signal sendToScript(var message);
|
||||
|
||||
onDynamicContentChanged: {
|
||||
if (dynamicContent && dynamicContent.sendToScript) {
|
||||
dynamicContent.sendToScript.connect(sendToScript);
|
||||
}
|
||||
}
|
||||
|
||||
onInteractiveWindowVisibleChanged: {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
shown = interactiveWindowVisible;
|
||||
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
|
||||
if (!nativeWindow.visible && interactiveWindowVisible) {
|
||||
nativeWindow.showNormal();
|
||||
} else {
|
||||
nativeWindow.setVisible(interactiveWindowVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTitleChanged: {
|
||||
if (nativeWindow) {
|
||||
nativeWindow.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
onXChanged: {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
interactiveWindowPosition = Qt.point(x, interactiveWindowPosition.y);
|
||||
}
|
||||
}
|
||||
|
||||
onYChanged: {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, y);
|
||||
}
|
||||
}
|
||||
|
||||
onWidthChanged: {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
interactiveWindowSize = Qt.size(width, interactiveWindowSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: {
|
||||
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
|
||||
interactiveWindowSize = Qt.size(interactiveWindowSize.width, height);
|
||||
}
|
||||
}
|
||||
|
||||
onPresentationModeChanged: {
|
||||
if (initialized) {
|
||||
setupPresentationMode();
|
||||
}
|
||||
}
|
||||
|
||||
onWindowClosed: {
|
||||
// set invisible on close, to make it not re-appear unintended after switching PresentationMode
|
||||
interactiveWindowVisible = false;
|
||||
|
||||
if ((flags & Desktop.CLOSE_BUTTON_HIDES) !== Desktop.CLOSE_BUTTON_HIDES) {
|
||||
selfDestruct();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
Label {
|
||||
text: OverlayWindowTestString
|
||||
text: "OverlayWindowTestString"
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
|
76
interface/resources/qml/controls/+android/WebEntityView.qml
Normal file
76
interface/resources/qml/controls/+android/WebEntityView.qml
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Copyright 2016 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 QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
|
||||
property string url
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#262626" }
|
||||
GradientStop { position: 1.0; color: "#000000" }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -69,6 +69,7 @@ Item {
|
|||
id: stack
|
||||
initialItem: inputConfiguration
|
||||
property alias messageVisible: imageMessageBox.visible
|
||||
property string selectedPlugin: ""
|
||||
Rectangle {
|
||||
id: inputConfiguration
|
||||
anchors {
|
||||
|
@ -274,6 +275,8 @@ Item {
|
|||
} else {
|
||||
box.label = "";
|
||||
}
|
||||
|
||||
stack.selectedPlugin = selectedDevice;
|
||||
}
|
||||
|
||||
Timer {
|
||||
|
|
|
@ -28,6 +28,7 @@ Flickable {
|
|||
onPluginNameChanged: {
|
||||
if (page !== null) {
|
||||
page.pluginName = flick.pluginName;
|
||||
page.displayConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ StackView {
|
|||
|
||||
property bool keyboardEnabled: false
|
||||
property bool punctuationMode: false
|
||||
property bool keyboardRaised: false
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
@ -210,6 +211,8 @@ StackView {
|
|||
|
||||
QQC2.TextField {
|
||||
id: addressLine
|
||||
|
||||
focus: true
|
||||
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
|
||||
anchors {
|
||||
left: addressLineContainer.left;
|
||||
|
@ -236,24 +239,20 @@ StackView {
|
|||
color: hifi.colors.text
|
||||
background: Item {}
|
||||
|
||||
QQC2.Label {
|
||||
T.TextField {
|
||||
id: control
|
||||
}
|
||||
|
||||
padding: 6 // numbers taken from Qt\5.9.2\Src\qtquickcontrols2\src\imports\controls\TextField.qml
|
||||
leftPadding: padding + 4
|
||||
}
|
||||
QQC2.Label {
|
||||
font: addressLine.font
|
||||
|
||||
font: parent.font
|
||||
x: addressLine.x
|
||||
y: addressLine.y
|
||||
leftPadding: addressLine.leftPadding
|
||||
topPadding: addressLine.topPadding
|
||||
|
||||
x: control.leftPadding
|
||||
y: control.topPadding
|
||||
|
||||
text: parent.placeholderText2
|
||||
verticalAlignment: "AlignVCenter"
|
||||
color: 'gray'
|
||||
visible: parent.text === ''
|
||||
}
|
||||
text: addressLine.placeholderText2
|
||||
verticalAlignment: "AlignVCenter"
|
||||
color: 'gray'
|
||||
visible: addressLine.text === ''
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
//
|
||||
#include "AndroidHelper.h"
|
||||
#include <QDebug>
|
||||
#include "Application.h"
|
||||
|
||||
#if defined(qApp)
|
||||
#undef qApp
|
||||
#endif
|
||||
#define qApp (static_cast<Application*>(QCoreApplication::instance()))
|
||||
|
||||
AndroidHelper::AndroidHelper() {
|
||||
}
|
||||
|
@ -17,8 +23,8 @@ AndroidHelper::AndroidHelper() {
|
|||
AndroidHelper::~AndroidHelper() {
|
||||
}
|
||||
|
||||
void AndroidHelper::requestActivity(const QString &activityName, const bool backToScene) {
|
||||
emit androidActivityRequested(activityName, backToScene);
|
||||
void AndroidHelper::requestActivity(const QString &activityName, const bool backToScene, QList<QString> args) {
|
||||
emit androidActivityRequested(activityName, backToScene, args);
|
||||
}
|
||||
|
||||
void AndroidHelper::notifyLoadComplete() {
|
||||
|
@ -40,3 +46,9 @@ void AndroidHelper::performHapticFeedback(int duration) {
|
|||
void AndroidHelper::showLoginDialog() {
|
||||
emit androidActivityRequested("Login", true);
|
||||
}
|
||||
|
||||
void AndroidHelper::processURL(const QString &url) {
|
||||
if (qApp->canAcceptURL(url)) {
|
||||
qApp->acceptURL(url);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,13 @@ public:
|
|||
static AndroidHelper instance;
|
||||
return instance;
|
||||
}
|
||||
void requestActivity(const QString &activityName, const bool backToScene);
|
||||
void requestActivity(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
|
||||
void notifyLoadComplete();
|
||||
void notifyEnterForeground();
|
||||
void notifyEnterBackground();
|
||||
|
||||
void performHapticFeedback(int duration);
|
||||
void processURL(const QString &url);
|
||||
|
||||
AndroidHelper(AndroidHelper const&) = delete;
|
||||
void operator=(AndroidHelper const&) = delete;
|
||||
|
@ -35,7 +36,7 @@ public slots:
|
|||
void showLoginDialog();
|
||||
|
||||
signals:
|
||||
void androidActivityRequested(const QString &activityName, const bool backToScene);
|
||||
void androidActivityRequested(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
|
||||
void qtAppLoadComplete();
|
||||
void enterForeground();
|
||||
void enterBackground();
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <QtCore/QFileSelector>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
@ -129,6 +130,7 @@
|
|||
#include <SoundCache.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <ui/ToolbarScriptingInterface.h>
|
||||
#include <InteractiveWindow.h>
|
||||
#include <Tooltip.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
@ -1072,6 +1074,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Regular.ttf");
|
||||
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Bold.ttf");
|
||||
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-SemiBold.ttf");
|
||||
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Cairo-SemiBold.ttf");
|
||||
_window->setWindowTitle("High Fidelity Interface");
|
||||
|
||||
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
|
||||
|
@ -2120,6 +2123,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|| ((rightHandPose.valid || lastRightHandPose.valid) && (rightHandPose != lastRightHandPose));
|
||||
lastLeftHandPose = leftHandPose;
|
||||
lastRightHandPose = rightHandPose;
|
||||
properties["avatar_identity_requests_sent"] = DependencyManager::get<AvatarManager>()->getIdentityRequestsSent();
|
||||
|
||||
UserActivityLogger::getInstance().logAction("stats", properties);
|
||||
});
|
||||
|
@ -2979,6 +2983,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
|||
|
||||
surfaceContext->setContextProperty("Overlays", &_overlays);
|
||||
surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
|
@ -4019,7 +4024,18 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
// Fix for OSX right click dragging on window when coming from a native window
|
||||
bool isFocussed = hasFocus();
|
||||
if (!isFocussed && event->button() == Qt::MouseButton::RightButton) {
|
||||
setFocus();
|
||||
isFocussed = true;
|
||||
}
|
||||
|
||||
if (isFocussed) {
|
||||
#else
|
||||
if (hasFocus()) {
|
||||
#endif
|
||||
if (_keyboardMouseDevice->isActive()) {
|
||||
_keyboardMouseDevice->mousePressEvent(event);
|
||||
}
|
||||
|
@ -6636,6 +6652,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
|
||||
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
registerInteractiveWindowMetaType(scriptEngine.data());
|
||||
|
||||
DependencyManager::get<PickScriptingInterface>()->registerMetaTypes(scriptEngine.data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
|
@ -6983,7 +7001,9 @@ void Application::showAssetServerWidget(QString filePath) {
|
|||
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
||||
} else {
|
||||
static const QUrl url("hifi/dialogs/TabletAssetServer.qml");
|
||||
tablet->pushOntoStack(url);
|
||||
if (!tablet->isPathLoaded(url)) {
|
||||
tablet->pushOntoStack(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8302,6 +8322,16 @@ void Application::saveNextPhysicsStats(QString filename) {
|
|||
_physicsEngine->saveNextPhysicsStats(filename);
|
||||
}
|
||||
|
||||
void Application::copyToClipboard(const QString& text) {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
QMetaObject::invokeMethod(this, "copyToClipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
// assume that the address is being copied because the user wants a shareable address
|
||||
QApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void Application::enterBackground() {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
|
||||
|
|
|
@ -310,6 +310,8 @@ public:
|
|||
void loadAvatarScripts(const QVector<QString>& urls);
|
||||
void unloadAvatarScripts();
|
||||
|
||||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void enterBackground();
|
||||
void enterForeground();
|
||||
|
|
|
@ -256,8 +256,6 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, true, qApp, SLOT(rotationModeChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Overlays, 0, true);
|
||||
|
||||
// View > Enter First Person Mode in HMD
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPersonHMD, 0, true);
|
||||
|
||||
|
@ -818,6 +816,9 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VerboseLogging, 0, false,
|
||||
qApp, SLOT(updateVerboseLogging()));
|
||||
|
||||
// Developer > Show Overlays
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Overlays, 0, true);
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
QAction* backAction = addActionToQMenuAndActionHash(navigateMenu, MenuOption::Back, 0, addressManager.data(), SLOT(goBack()));
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include <QScriptEngine>
|
||||
|
||||
#include "AvatarLogging.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
|
@ -54,6 +56,13 @@ static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND /
|
|||
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
|
||||
const QUuid MY_AVATAR_KEY; // NULL key
|
||||
|
||||
namespace {
|
||||
// For an unknown avatar-data packet, wait this long before requesting the identity.
|
||||
constexpr std::chrono::milliseconds REQUEST_UNKNOWN_IDENTITY_DELAY { 5 * 1000 };
|
||||
constexpr int REQUEST_UNKNOWN_IDENTITY_TRANSMITS = 3;
|
||||
}
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
AvatarManager::AvatarManager(QObject* parent) :
|
||||
_avatarsToFade(),
|
||||
_myAvatar(new MyAvatar(qApp->thread()), [](MyAvatar* ptr) { ptr->deleteLater(); })
|
||||
|
@ -118,6 +127,7 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
_lastSendAvatarDataTime = now;
|
||||
_myAvatarSendRate.increment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,6 +296,28 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
||||
// Check on avatars with pending identities:
|
||||
steady_clock::time_point now = steady_clock::now();
|
||||
QWriteLocker writeLock(&_hashLock);
|
||||
for (auto pendingAvatar = _pendingAvatars.begin(); pendingAvatar != _pendingAvatars.end(); ++pendingAvatar) {
|
||||
if (now - pendingAvatar->creationTime >= REQUEST_UNKNOWN_IDENTITY_DELAY) {
|
||||
// Too long without an ID
|
||||
sendIdentityRequest(pendingAvatar->avatar->getID());
|
||||
if (++pendingAvatar->transmits >= REQUEST_UNKNOWN_IDENTITY_TRANSMITS) {
|
||||
qCDebug(avatars) << "Requesting identity for unknown avatar (final request)" <<
|
||||
pendingAvatar->avatar->getID().toString();
|
||||
|
||||
pendingAvatar = _pendingAvatars.erase(pendingAvatar);
|
||||
if (pendingAvatar == _pendingAvatars.end()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
pendingAvatar->creationTime = now;
|
||||
qCDebug(avatars) << "Requesting identity for unknown avatar" << pendingAvatar->avatar->getID().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
}
|
||||
|
||||
|
@ -298,6 +330,20 @@ void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scen
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarManager::sendIdentityRequest(const QUuid& avatarID) const {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
return node->getType() == NodeType::AvatarMixer && node->getActiveSocket();
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
auto packet = NLPacket::create(PacketType::AvatarIdentityRequest, NUM_BYTES_RFC4122_UUID, true);
|
||||
packet->write(avatarID.toRfc4122());
|
||||
nodeList->sendPacket(std::move(packet), *node);
|
||||
++_identityRequestsSent;
|
||||
});
|
||||
}
|
||||
|
||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||
if (_avatarsToFade.empty()) {
|
||||
return;
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
|
||||
void updateMyAvatar(float deltaTime);
|
||||
void updateOtherAvatars(float deltaTime);
|
||||
void sendIdentityRequest(const QUuid& avatarID) const;
|
||||
|
||||
void postUpdate(float deltaTime, const render::ScenePointer& scene);
|
||||
|
||||
|
@ -157,6 +158,7 @@ public:
|
|||
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
|
||||
|
||||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||
int getIdentityRequestsSent() const { return _identityRequestsSent; }
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -194,6 +196,7 @@ private:
|
|||
int _numAvatarsNotUpdated { 0 };
|
||||
float _avatarSimulationTime { 0.0f };
|
||||
bool _shouldRender { true };
|
||||
mutable int _identityRequestsSent { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarManager_h
|
||||
|
|
|
@ -727,7 +727,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
}
|
||||
});
|
||||
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
|
||||
_characterController.setFlyingAllowed(zoneAllowsFlying && (_enableFlying || !isPhysicsEnabled));
|
||||
_characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled);
|
||||
_characterController.setCollisionlessAllowed(collisionlessAllowed);
|
||||
}
|
||||
|
||||
|
|
|
@ -109,6 +109,11 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f));
|
||||
|
||||
glm::mat4 rigToAvatarMatrix = Matrices::Y_180;
|
||||
glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition());
|
||||
glm::mat4 sensorToWorldMatrix = myAvatar->getSensorToWorldMatrix();
|
||||
params.rigToSensorMatrix = glm::inverse(sensorToWorldMatrix) * avatarToWorldMatrix * rigToAvatarMatrix;
|
||||
|
||||
// input action is the highest priority source for head orientation.
|
||||
auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (avatarHeadPose.isValid()) {
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <QWindow>
|
||||
#include <QScreen>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
|
@ -29,6 +31,14 @@ int DesktopScriptingInterface::getHeight() {
|
|||
return size.height();
|
||||
}
|
||||
|
||||
QVariantMap DesktopScriptingInterface::getPresentationMode() {
|
||||
static QVariantMap presentationModes {
|
||||
{ "VIRTUAL", Virtual },
|
||||
{ "NATIVE", Native }
|
||||
};
|
||||
return presentationModes;
|
||||
}
|
||||
|
||||
void DesktopScriptingInterface::setHUDAlpha(float alpha) {
|
||||
qApp->getApplicationCompositor().setAlpha(alpha);
|
||||
}
|
||||
|
@ -41,3 +51,14 @@ void DesktopScriptingInterface::show(const QString& path, const QString& title)
|
|||
DependencyManager::get<OffscreenUi>()->show(path, title);
|
||||
}
|
||||
|
||||
InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString& sourceUrl, const QVariantMap& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
InteractiveWindowPointer interactiveWindow = nullptr;
|
||||
BLOCKING_INVOKE_METHOD(this, "createWindow",
|
||||
Q_RETURN_ARG(InteractiveWindowPointer, interactiveWindow),
|
||||
Q_ARG(QString, sourceUrl),
|
||||
Q_ARG(QVariantMap, properties));
|
||||
return interactiveWindow;
|
||||
}
|
||||
return new InteractiveWindow(sourceUrl, properties);;
|
||||
}
|
||||
|
|
|
@ -13,20 +13,48 @@
|
|||
#define hifi_DesktopScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "InteractiveWindow.h"
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Desktop
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {number} ALWAYS_ON_TOP - InteractiveWindow flag for always showing a window on top
|
||||
* @property {number} CLOSE_BUTTON_HIDES - InteractiveWindow flag for hiding the window instead of closing on window close by user
|
||||
*/
|
||||
class DesktopScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int width READ getWidth) // Physical width of screen(s) including task bars and system menus
|
||||
Q_PROPERTY(int height READ getHeight) // Physical height of screen(s) including task bars and system menus
|
||||
|
||||
Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL)
|
||||
Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL)
|
||||
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void setHUDAlpha(float alpha);
|
||||
Q_INVOKABLE void show(const QString& path, const QString& title);
|
||||
|
||||
Q_INVOKABLE InteractiveWindowPointer createWindow(const QString& sourceUrl, const QVariantMap& properties = QVariantMap());
|
||||
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
|
||||
|
||||
private:
|
||||
static int flagAlwaysOnTop() { return AlwaysOnTop; }
|
||||
static int flagCloseButtonHides() { return CloseButtonHides; }
|
||||
|
||||
Q_INVOKABLE static QVariantMap getPresentationMode();
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_DesktopScriptingInterface_h
|
||||
|
|
|
@ -137,8 +137,13 @@ void WindowScriptingInterface::openUrl(const QUrl& url) {
|
|||
if (url.scheme() == URL_SCHEME_HIFI) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
|
||||
} else {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
QList<QString> args = { url.toString() };
|
||||
AndroidHelper::instance().requestActivity("WebView", true, args);
|
||||
#else
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
QDesktopServices::openUrl(url);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1244,7 +1244,8 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
||||
|
||||
const bool ENABLE_POLE_VECTORS = false;
|
||||
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
@ -1271,19 +1272,20 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
|
||||
if (ENABLE_POLE_VECTORS && !leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
|
||||
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
if (!_prevLeftHandPoleVectorValid) {
|
||||
_prevLeftHandPoleVectorValid = true;
|
||||
_prevLeftHandPoleVector = poleVector;
|
||||
_prevLeftHandPoleVector = sensorPoleVector;
|
||||
}
|
||||
glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, poleVector);
|
||||
glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, sensorPoleVector);
|
||||
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
|
||||
_prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector;
|
||||
|
||||
_animVars.set("leftHandPoleVectorEnabled", true);
|
||||
_animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X);
|
||||
_animVars.set("leftHandPoleVector", _prevLeftHandPoleVector);
|
||||
_animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftHandPoleVector));
|
||||
} else {
|
||||
_prevLeftHandPoleVectorValid = false;
|
||||
_animVars.set("leftHandPoleVectorEnabled", false);
|
||||
|
@ -1318,19 +1320,20 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
|
||||
if (ENABLE_POLE_VECTORS && !rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
|
||||
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
if (!_prevRightHandPoleVectorValid) {
|
||||
_prevRightHandPoleVectorValid = true;
|
||||
_prevRightHandPoleVector = poleVector;
|
||||
_prevRightHandPoleVector = sensorPoleVector;
|
||||
}
|
||||
glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, poleVector);
|
||||
glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, sensorPoleVector);
|
||||
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
|
||||
_prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector;
|
||||
|
||||
_animVars.set("rightHandPoleVectorEnabled", true);
|
||||
_animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X);
|
||||
_animVars.set("rightHandPoleVector", _prevRightHandPoleVector);
|
||||
_animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightHandPoleVector));
|
||||
} else {
|
||||
_prevRightHandPoleVectorValid = false;
|
||||
_animVars.set("rightHandPoleVectorEnabled", false);
|
||||
|
@ -1345,7 +1348,8 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose) {
|
||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
||||
|
||||
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
||||
|
@ -1360,19 +1364,20 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
|||
int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
|
||||
int upLegJointIndex = _animSkeleton->nameToJointIndex("LeftUpLeg");
|
||||
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, kneeJointIndex, upLegJointIndex, hipsIndex, leftFootPose);
|
||||
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter, but in sensor space.
|
||||
if (!_prevLeftFootPoleVectorValid) {
|
||||
_prevLeftFootPoleVectorValid = true;
|
||||
_prevLeftFootPoleVector = poleVector;
|
||||
_prevLeftFootPoleVector = sensorPoleVector;
|
||||
}
|
||||
glm::quat deltaRot = rotationBetween(_prevLeftFootPoleVector, poleVector);
|
||||
glm::quat deltaRot = rotationBetween(_prevLeftFootPoleVector, sensorPoleVector);
|
||||
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, KNEE_POLE_VECTOR_BLEND_FACTOR);
|
||||
_prevLeftFootPoleVector = smoothDeltaRot * _prevLeftFootPoleVector;
|
||||
|
||||
_animVars.set("leftFootPoleVectorEnabled", true);
|
||||
_animVars.set("leftFootPoleReferenceVector", Vectors::UNIT_Z);
|
||||
_animVars.set("leftFootPoleVector", _prevLeftFootPoleVector);
|
||||
_animVars.set("leftFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftFootPoleVector));
|
||||
} else {
|
||||
_animVars.unset("leftFootPosition");
|
||||
_animVars.unset("leftFootRotation");
|
||||
|
@ -1390,19 +1395,20 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
|||
int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
|
||||
int upLegJointIndex = _animSkeleton->nameToJointIndex("RightUpLeg");
|
||||
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, kneeJointIndex, upLegJointIndex, hipsIndex, rightFootPose);
|
||||
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
if (!_prevRightFootPoleVectorValid) {
|
||||
_prevRightFootPoleVectorValid = true;
|
||||
_prevRightFootPoleVector = poleVector;
|
||||
_prevRightFootPoleVector = sensorPoleVector;
|
||||
}
|
||||
glm::quat deltaRot = rotationBetween(_prevRightFootPoleVector, poleVector);
|
||||
glm::quat deltaRot = rotationBetween(_prevRightFootPoleVector, sensorPoleVector);
|
||||
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, KNEE_POLE_VECTOR_BLEND_FACTOR);
|
||||
_prevRightFootPoleVector = smoothDeltaRot * _prevRightFootPoleVector;
|
||||
|
||||
_animVars.set("rightFootPoleVectorEnabled", true);
|
||||
_animVars.set("rightFootPoleReferenceVector", Vectors::UNIT_Z);
|
||||
_animVars.set("rightFootPoleVector", _prevRightFootPoleVector);
|
||||
_animVars.set("rightFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightFootPoleVector));
|
||||
} else {
|
||||
_animVars.unset("rightFootPosition");
|
||||
_animVars.unset("rightFootRotation");
|
||||
|
@ -1434,9 +1440,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
|
||||
glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);
|
||||
|
||||
// limit swing rotation of the deltaQuat by a 30 degree cone.
|
||||
// limit swing rotation of the deltaQuat by a 25 degree cone.
|
||||
// TODO: use swing twist decomposition constraint instead, for off axis rotation clamping.
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
const float MAX_ANGLE = 25.0f * RADIANS_PER_DEGREE;
|
||||
if (fabsf(glm::angle(deltaQuat)) > MAX_ANGLE) {
|
||||
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
|
||||
}
|
||||
|
@ -1546,16 +1552,18 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
bool spine2Enabled = params.primaryControllerFlags[PrimaryControllerType_Spine2] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool leftArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_LeftArm] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool rightArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_RightArm] & (uint8_t)ControllerFlags::Enabled;
|
||||
glm::mat4 sensorToRigMatrix = glm::inverse(params.rigToSensorMatrix);
|
||||
|
||||
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
|
||||
|
||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
|
||||
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo);
|
||||
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo,
|
||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||
|
||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]);
|
||||
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot],
|
||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||
|
||||
if (headEnabled) {
|
||||
// Blend IK chains toward the joint limit centers, this should stablize head and hand ik.
|
||||
|
|
|
@ -75,6 +75,7 @@ public:
|
|||
};
|
||||
|
||||
struct ControllerParameters {
|
||||
glm::mat4 rigToSensorMatrix;
|
||||
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
|
||||
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
|
||||
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
||||
|
@ -231,8 +232,10 @@ protected:
|
|||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo);
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose);
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||
|
||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const;
|
||||
|
@ -359,16 +362,16 @@ protected:
|
|||
int _nextStateHandlerId { 0 };
|
||||
QMutex _stateMutex;
|
||||
|
||||
glm::vec3 _prevRightFootPoleVector { Vectors::UNIT_Z };
|
||||
glm::vec3 _prevRightFootPoleVector { Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevRightFootPoleVectorValid { false };
|
||||
|
||||
glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z };
|
||||
glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevLeftFootPoleVectorValid { false };
|
||||
|
||||
glm::vec3 _prevRightHandPoleVector { -Vectors::UNIT_Z };
|
||||
glm::vec3 _prevRightHandPoleVector { -Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevRightHandPoleVectorValid { false };
|
||||
|
||||
glm::vec3 _prevLeftHandPoleVector { -Vectors::UNIT_Z };
|
||||
glm::vec3 _prevLeftHandPoleVector { -Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevLeftHandPoleVectorValid { false };
|
||||
|
||||
int _rigId;
|
||||
|
|
|
@ -220,30 +220,44 @@ void Head::calculateMouthShapes(float deltaTime) {
|
|||
|
||||
void Head::applyEyelidOffset(glm::quat headOrientation) {
|
||||
// Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches.
|
||||
|
||||
if (disableEyelidAdjustment) {
|
||||
bool isBlinking = (_rightEyeBlinkVelocity != 0.0f && _rightEyeBlinkVelocity != 0.0f);
|
||||
if (disableEyelidAdjustment || isBlinking) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FORWARD, getLookAtPosition() - _eyePosition);
|
||||
eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head
|
||||
float eyePitch = safeEulerAngles(eyeRotation).x;
|
||||
const float EYE_PITCH_TO_COEFFICIENT = 3.5f; // Empirically determined
|
||||
const float MAX_EYELID_OFFSET = 1.5f;
|
||||
const float BLINK_DOWN_MULTIPLIER = 0.25f;
|
||||
const float OPEN_DOWN_MULTIPLIER = 0.3f;
|
||||
const float BROW_UP_MULTIPLIER = 0.5f;
|
||||
|
||||
const float EYE_PITCH_TO_COEFFICIENT = 1.6f; // Empirically determined
|
||||
const float MAX_EYELID_OFFSET = 0.8f; // So that don't fully close eyes when looking way down
|
||||
float eyelidOffset = glm::clamp(-eyePitch * EYE_PITCH_TO_COEFFICIENT, -1.0f, MAX_EYELID_OFFSET);
|
||||
glm::vec3 lookAt = glm::normalize(getLookAtPosition() - _eyePosition);
|
||||
glm::vec3 headUp = headOrientation * Vectors::UNIT_Y;
|
||||
float eyePitch = (PI / 2.0f) - acos(glm::dot(lookAt, headUp));
|
||||
float eyelidOffset = glm::clamp(abs(eyePitch * EYE_PITCH_TO_COEFFICIENT), 0.0f, MAX_EYELID_OFFSET);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
const int LEFT_EYE = 8;
|
||||
float eyeCoefficient = _transientBlendshapeCoefficients[i] - _transientBlendshapeCoefficients[LEFT_EYE + i];
|
||||
eyeCoefficient = glm::clamp(eyelidOffset + eyeCoefficient * (1.0f - eyelidOffset), -1.0f, 1.0f);
|
||||
if (eyeCoefficient > 0.0f) {
|
||||
_transientBlendshapeCoefficients[i] = eyeCoefficient;
|
||||
_transientBlendshapeCoefficients[LEFT_EYE + i] = 0.0f;
|
||||
float blinkUpCoefficient = -eyelidOffset;
|
||||
float blinkDownCoefficient = BLINK_DOWN_MULTIPLIER * eyelidOffset;
|
||||
|
||||
float openUpCoefficient = eyelidOffset;
|
||||
float openDownCoefficient = OPEN_DOWN_MULTIPLIER * eyelidOffset;
|
||||
|
||||
float browsUpCoefficient = BROW_UP_MULTIPLIER * eyelidOffset;
|
||||
float browsDownCoefficient = 0.0f;
|
||||
|
||||
} else {
|
||||
_transientBlendshapeCoefficients[i] = 0.0f;
|
||||
_transientBlendshapeCoefficients[LEFT_EYE + i] = -eyeCoefficient;
|
||||
bool isLookingUp = (eyePitch > 0);
|
||||
|
||||
if (isLookingUp) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
_transientBlendshapeCoefficients[EYE_BLINK_INDICES[i]] = blinkUpCoefficient;
|
||||
_transientBlendshapeCoefficients[EYE_OPEN_INDICES[i]] = openUpCoefficient;
|
||||
_transientBlendshapeCoefficients[BROWS_U_INDICES[i]] = browsUpCoefficient;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
_transientBlendshapeCoefficients[EYE_BLINK_INDICES[i]] = blinkDownCoefficient;
|
||||
_transientBlendshapeCoefficients[EYE_OPEN_INDICES[i]] = openDownCoefficient;
|
||||
_transientBlendshapeCoefficients[BROWS_U_INDICES[i]] = browsDownCoefficient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -401,7 +401,6 @@ class AvatarData : public QObject, public SpatiallyNestable {
|
|||
Q_PROPERTY(float sensorToWorldScale READ getSensorToWorldScale)
|
||||
|
||||
public:
|
||||
|
||||
virtual QString getName() const override { return QString("Avatar:") + _displayName; }
|
||||
|
||||
static const QString FRAME_NAME;
|
||||
|
|
|
@ -89,11 +89,15 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
return avatar;
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer,
|
||||
bool& isNew) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
auto avatar = _avatarHash.value(sessionUUID);
|
||||
if (!avatar) {
|
||||
avatar = addAvatar(sessionUUID, mixerWeakPointer);
|
||||
isNew = true;
|
||||
} else {
|
||||
isNew = false;
|
||||
}
|
||||
return avatar;
|
||||
}
|
||||
|
@ -125,8 +129,13 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
|
|||
// make sure this isn't our own avatar data or for a previously ignored node
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
bool isNewAvatar;
|
||||
if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) {
|
||||
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode);
|
||||
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar);
|
||||
if (isNewAvatar) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
_pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar });
|
||||
}
|
||||
|
||||
// have the matching (or new) avatar parse the data from the packet
|
||||
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
||||
|
@ -157,6 +166,7 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
|
||||
{
|
||||
QReadLocker locker(&_hashLock);
|
||||
_pendingAvatars.remove(identityUUID);
|
||||
auto me = _avatarHash.find(EMPTY);
|
||||
if ((me != _avatarHash.end()) && (identityUUID == me.value()->getSessionUUID())) {
|
||||
// We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an
|
||||
|
@ -168,7 +178,8 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
|
||||
if (!nodeList->isIgnoringNode(identityUUID) || nodeList->getRequestsDomainListData()) {
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
auto avatar = newOrExistingAvatar(identityUUID, sendingNode);
|
||||
bool isNewAvatar;
|
||||
auto avatar = newOrExistingAvatar(identityUUID, sendingNode, isNewAvatar);
|
||||
bool identityChanged = false;
|
||||
bool displayNameChanged = false;
|
||||
bool skeletonModelUrlChanged = false;
|
||||
|
@ -189,6 +200,7 @@ void AvatarHashMap::processKillAvatar(QSharedPointer<ReceivedMessage> message, S
|
|||
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
_pendingAvatars.remove(sessionUUID);
|
||||
auto removedAvatar = _avatarHash.take(sessionUUID);
|
||||
|
||||
if (removedAvatar) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
@ -145,13 +146,21 @@ protected:
|
|||
virtual AvatarSharedPointer parseAvatarData(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer,
|
||||
bool& isNew);
|
||||
virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock
|
||||
virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
||||
AvatarHash _avatarHash;
|
||||
struct PendingAvatar {
|
||||
std::chrono::steady_clock::time_point creationTime;
|
||||
int transmits;
|
||||
AvatarSharedPointer avatar;
|
||||
};
|
||||
using AvatarPendingHash = QHash<QUuid, PendingAvatar>;
|
||||
AvatarPendingHash _pendingAvatars;
|
||||
mutable QReadWriteLock _hashLock;
|
||||
|
||||
private:
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <QtCore/QJsonArray>
|
||||
#include <QVector>
|
||||
|
||||
#include <FaceshiftConstants.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <shared/JSONHelpers.h>
|
||||
|
||||
|
@ -33,7 +32,7 @@ HeadData::HeadData(AvatarData* owningAvatar) :
|
|||
_summedBlendshapeCoefficients(QVector<float>(0, 0.0f)),
|
||||
_owningAvatar(owningAvatar)
|
||||
{
|
||||
|
||||
computeBlendshapesLookupMap();
|
||||
}
|
||||
|
||||
glm::quat HeadData::getRawOrientation() const {
|
||||
|
@ -71,16 +70,10 @@ void HeadData::setOrientation(const glm::quat& orientation) {
|
|||
setHeadOrientation(orientation);
|
||||
}
|
||||
|
||||
//Lazily construct a lookup map from the blendshapes
|
||||
static const QMap<QString, int>& getBlendshapesLookupMap() {
|
||||
static std::once_flag once;
|
||||
static QMap<QString, int> blendshapeLookupMap;
|
||||
std::call_once(once, [&] {
|
||||
for (int i = 0; i < NUM_FACESHIFT_BLENDSHAPES; i++) {
|
||||
blendshapeLookupMap[FACESHIFT_BLENDSHAPES[i]] = i;
|
||||
}
|
||||
});
|
||||
return blendshapeLookupMap;
|
||||
void HeadData::computeBlendshapesLookupMap(){
|
||||
for (int i = 0; i < NUM_FACESHIFT_BLENDSHAPES; i++) {
|
||||
_blendshapeLookupMap[FACESHIFT_BLENDSHAPES[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
int HeadData::getNumSummedBlendshapeCoefficients() const {
|
||||
|
@ -108,11 +101,10 @@ const QVector<float>& HeadData::getSummedBlendshapeCoefficients() {
|
|||
}
|
||||
|
||||
void HeadData::setBlendshape(QString name, float val) {
|
||||
const auto& blendshapeLookupMap = getBlendshapesLookupMap();
|
||||
|
||||
//Check to see if the named blendshape exists, and then set its value if it does
|
||||
auto it = blendshapeLookupMap.find(name);
|
||||
if (it != blendshapeLookupMap.end()) {
|
||||
auto it = _blendshapeLookupMap.find(name);
|
||||
if (it != _blendshapeLookupMap.end()) {
|
||||
if (_blendshapeCoefficients.size() <= it.value()) {
|
||||
_blendshapeCoefficients.resize(it.value() + 1);
|
||||
}
|
||||
|
@ -123,6 +115,18 @@ void HeadData::setBlendshape(QString name, float val) {
|
|||
}
|
||||
}
|
||||
|
||||
int HeadData::getBlendshapeIndex(const QString& name) {
|
||||
auto it = _blendshapeLookupMap.find(name);
|
||||
int index = it != _blendshapeLookupMap.end() ? it.value() : -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
void HeadData::getBlendshapeIndices(const std::vector<QString>& blendShapeNames, std::vector<int>& indexes) {
|
||||
for (auto& name : blendShapeNames) {
|
||||
indexes.push_back(getBlendshapeIndex(name));
|
||||
}
|
||||
}
|
||||
|
||||
static const QString JSON_AVATAR_HEAD_ROTATION = QStringLiteral("rotation");
|
||||
static const QString JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS = QStringLiteral("blendShapes");
|
||||
static const QString JSON_AVATAR_HEAD_LEAN_FORWARD = QStringLiteral("leanForward");
|
||||
|
@ -131,10 +135,9 @@ static const QString JSON_AVATAR_HEAD_LOOKAT = QStringLiteral("lookAt");
|
|||
|
||||
QJsonObject HeadData::toJson() const {
|
||||
QJsonObject headJson;
|
||||
const auto& blendshapeLookupMap = getBlendshapesLookupMap();
|
||||
QJsonObject blendshapesJson;
|
||||
for (auto name : blendshapeLookupMap.keys()) {
|
||||
auto index = blendshapeLookupMap[name];
|
||||
for (auto name : _blendshapeLookupMap.keys()) {
|
||||
auto index = _blendshapeLookupMap[name];
|
||||
float value = 0.0f;
|
||||
if (index < _blendshapeCoefficients.size()) {
|
||||
value += _blendshapeCoefficients[index];
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <FaceshiftConstants.h>
|
||||
|
||||
// degrees
|
||||
const float MIN_HEAD_YAW = -180.0f;
|
||||
|
@ -55,6 +56,8 @@ public:
|
|||
void setOrientation(const glm::quat& orientation);
|
||||
|
||||
void setBlendshape(QString name, float val);
|
||||
int getBlendshapeIndex(const QString& name);
|
||||
void getBlendshapeIndices(const std::vector<QString>& blendShapeNames, std::vector<int>& indexes);
|
||||
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||
const QVector<float>& getSummedBlendshapeCoefficients();
|
||||
int getNumSummedBlendshapeCoefficients() const;
|
||||
|
@ -114,6 +117,7 @@ protected:
|
|||
QVector<float> _blendshapeCoefficients;
|
||||
QVector<float> _transientBlendshapeCoefficients;
|
||||
QVector<float> _summedBlendshapeCoefficients;
|
||||
QMap<QString, int> _blendshapeLookupMap;
|
||||
AvatarData* _owningAvatar;
|
||||
|
||||
private:
|
||||
|
@ -122,6 +126,7 @@ private:
|
|||
HeadData& operator= (const HeadData&);
|
||||
|
||||
void setHeadOrientation(const glm::quat& orientation);
|
||||
void computeBlendshapesLookupMap();
|
||||
};
|
||||
|
||||
#endif // hifi_HeadData_h
|
||||
|
|
|
@ -100,7 +100,7 @@ void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
} else if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
if (itr == _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
|
||||
if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
if (itr == _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
|
|
|
@ -48,12 +48,12 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) {
|
|||
return;
|
||||
}
|
||||
|
||||
// check the program cache
|
||||
// pick the program version
|
||||
// check the program cache
|
||||
// pick the program version
|
||||
// check the program cache
|
||||
// pick the program version
|
||||
// check the program cache
|
||||
// pick the program version
|
||||
#ifdef GPU_STEREO_CAMERA_BUFFER
|
||||
GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version) isStereo());
|
||||
GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version)isStereo());
|
||||
#else
|
||||
GLuint glprogram = pipelineObject->_program->getProgram();
|
||||
#endif
|
||||
|
@ -85,10 +85,11 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) {
|
|||
} else {
|
||||
cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer);
|
||||
}
|
||||
// Invalidate uniform buffer cache slot
|
||||
_uniform._buffers[_pipeline._cameraCorrectionLocation].reset();
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection));
|
||||
|
||||
}
|
||||
(void) CHECK_GL_ERROR();
|
||||
(void)CHECK_GL_ERROR();
|
||||
_pipeline._invalidProgram = false;
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +98,7 @@ void GLBackend::updatePipeline() {
|
|||
if (_pipeline._invalidProgram) {
|
||||
// doing it here is aproblem for calls to glUniform.... so will do it on assing...
|
||||
glUseProgram(_pipeline._program);
|
||||
(void) CHECK_GL_ERROR();
|
||||
(void)CHECK_GL_ERROR();
|
||||
_pipeline._invalidProgram = false;
|
||||
}
|
||||
|
||||
|
@ -106,12 +107,12 @@ void GLBackend::updatePipeline() {
|
|||
// first reset to default what should be
|
||||
// the fields which were not to default and are default now
|
||||
resetPipelineState(_pipeline._state->_signature);
|
||||
|
||||
|
||||
// Update the signature cache with what's going to be touched
|
||||
_pipeline._stateSignatureCache |= _pipeline._state->_signature;
|
||||
|
||||
// And perform
|
||||
for (auto command: _pipeline._state->_commands) {
|
||||
for (auto command : _pipeline._state->_commands) {
|
||||
command->run(this);
|
||||
}
|
||||
} else {
|
||||
|
@ -142,8 +143,8 @@ void GLBackend::releaseUniformBuffer(uint32_t slot) {
|
|||
if (buf) {
|
||||
auto* object = Backend::getGPUObject<GLBuffer>(*buf);
|
||||
if (object) {
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE
|
||||
(void) CHECK_GL_ERROR();
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
buf.reset();
|
||||
}
|
||||
|
@ -157,8 +158,9 @@ void GLBackend::resetUniformStage() {
|
|||
|
||||
void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 3]._uint;
|
||||
if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers();
|
||||
if (slot > (GLuint)MAX_NUM_UNIFORM_BUFFERS) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot
|
||||
<< " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers();
|
||||
return;
|
||||
}
|
||||
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
|
||||
|
@ -169,7 +171,7 @@ void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) {
|
|||
releaseUniformBuffer(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// check cache before thinking
|
||||
if (_uniform._buffers[slot] == uniformBuffer) {
|
||||
return;
|
||||
|
@ -181,7 +183,7 @@ void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) {
|
|||
glBindBufferRange(GL_UNIFORM_BUFFER, slot, object->_buffer, rangeStart, rangeSize);
|
||||
|
||||
_uniform._buffers[slot] = uniformBuffer;
|
||||
(void) CHECK_GL_ERROR();
|
||||
(void)CHECK_GL_ERROR();
|
||||
} else {
|
||||
releaseUniformBuffer(slot);
|
||||
return;
|
||||
|
@ -195,8 +197,8 @@ void GLBackend::releaseResourceTexture(uint32_t slot) {
|
|||
if (object) {
|
||||
GLuint target = object->_target;
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(target, 0); // RELEASE
|
||||
(void) CHECK_GL_ERROR();
|
||||
glBindTexture(target, 0); // RELEASE
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
tex.reset();
|
||||
}
|
||||
|
@ -212,11 +214,11 @@ void GLBackend::resetResourceStage() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 1]._uint;
|
||||
if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers();
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot
|
||||
<< " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -237,7 +239,7 @@ void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
|
|||
// If successful bind then cache it
|
||||
if (bindResourceBuffer(slot, resourceBuffer)) {
|
||||
_resource._buffers[slot] = resourceBuffer;
|
||||
} else { // else clear slot and cache
|
||||
} else { // else clear slot and cache
|
||||
releaseResourceBuffer(slot);
|
||||
return;
|
||||
}
|
||||
|
@ -245,8 +247,9 @@ void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
|
|||
|
||||
void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 1]._uint;
|
||||
if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures();
|
||||
if (slot >= (GLuint)MAX_NUM_RESOURCE_TEXTURES) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot
|
||||
<< " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -265,11 +268,14 @@ void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourc
|
|||
void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 1]._uint;
|
||||
if (slot >= (GLuint)MAX_NUM_RESOURCE_TEXTURES) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceFramebufferSwapChainTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures();
|
||||
qCDebug(gpugllogging)
|
||||
<< "GLBackend::do_setResourceFramebufferSwapChainTexture: Trying to set a resource Texture at slot #" << slot
|
||||
<< " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures();
|
||||
return;
|
||||
}
|
||||
|
||||
auto swapChain = std::static_pointer_cast<FramebufferSwapChain>(batch._swapChains.get(batch._params[paramOffset + 0]._uint));
|
||||
auto swapChain =
|
||||
std::static_pointer_cast<FramebufferSwapChain>(batch._swapChains.get(batch._params[paramOffset + 0]._uint));
|
||||
|
||||
if (!swapChain) {
|
||||
releaseResourceTexture(slot);
|
||||
|
|
|
@ -168,7 +168,9 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta
|
|||
|
||||
void GLBackend::TransformStageState::bindCurrentCamera(int eye) const {
|
||||
if (_currentCameraOffset != INVALID_OFFSET) {
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement));
|
||||
static_assert(TRANSFORM_CAMERA_SLOT >= MAX_NUM_UNIFORM_BUFFERS, "TransformCamera may overlap pipeline uniform buffer slots. Invalidate uniform buffer slot cache for safety (call _uniform._buffers[TRANSFORM_CAMERA_SLOT].reset()).");
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize,
|
||||
sizeof(CameraBufferElement));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -852,17 +852,16 @@ void AddressManager::refreshPreviousLookup() {
|
|||
|
||||
void AddressManager::copyAddress() {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
QMetaObject::invokeMethod(this, "copyAddress");
|
||||
QMetaObject::invokeMethod(qApp, "copyToClipboard", Q_ARG(QString, currentShareableAddress().toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
// assume that the address is being copied because the user wants a shareable address
|
||||
QGuiApplication::clipboard()->setText(currentShareableAddress().toString());
|
||||
}
|
||||
|
||||
void AddressManager::copyPath() {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
QMetaObject::invokeMethod(this, "copyPath");
|
||||
QMetaObject::invokeMethod(qApp, "copyToClipboard", Q_ARG(QString, currentPath()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -475,13 +475,16 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
|
|||
}
|
||||
}
|
||||
|
||||
void DomainHandler::sentCheckInPacket() {
|
||||
bool DomainHandler::checkInPacketTimeout() {
|
||||
++_checkInPacketsSinceLastReply;
|
||||
|
||||
if (_checkInPacketsSinceLastReply >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
if (_checkInPacketsSinceLastReply > MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||
// so emit our signal that says that
|
||||
qCDebug(networking) << "Limit of silent domain checkins reached";
|
||||
emit limitOfSilentDomainCheckInsReached();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ public:
|
|||
void softReset();
|
||||
|
||||
int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
|
||||
void sentCheckInPacket();
|
||||
bool checkInPacketTimeout();
|
||||
void clearPendingCheckins() { _checkInPacketsSinceLastReply = 0; }
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -199,7 +199,9 @@ void NLPacket::readVersion() {
|
|||
}
|
||||
|
||||
void NLPacket::readSourceID() {
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(_type)) {
|
||||
if (PacketTypeEnum::getNonSourcedPackets().contains(_type)) {
|
||||
_sourceID = NULL_LOCAL_ID;
|
||||
} else {
|
||||
_sourceID = sourceIDInHeader(*this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -305,7 +305,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
} else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) {
|
||||
qCDebug(networking) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in.";
|
||||
handleICEConnectionToDomainServer();
|
||||
} else if (!_domainHandler.getIP().isNull()) {
|
||||
// let the domain handler know we are due to send a checkin packet
|
||||
} else if (!_domainHandler.getIP().isNull() && !_domainHandler.checkInPacketTimeout()) {
|
||||
|
||||
PacketType domainPacketType = !_domainHandler.isConnected()
|
||||
? PacketType::DomainConnectRequest : PacketType::DomainListRequest;
|
||||
|
@ -419,10 +420,15 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);
|
||||
|
||||
// Send duplicate check-ins in the exponentially increasing sequence 1, 1, 2, 4, ...
|
||||
int outstandingCheckins = _domainHandler.getCheckInPacketsSinceLastReply();
|
||||
int checkinCount = outstandingCheckins > 1 ? std::pow(2, outstandingCheckins - 2) : 1;
|
||||
for (int i = 1; i < checkinCount; ++i) {
|
||||
auto packetCopy = domainPacket->createCopy(*domainPacket);
|
||||
sendPacket(std::move(packetCopy), _domainHandler.getSockAddr());
|
||||
}
|
||||
sendPacket(std::move(domainPacket), _domainHandler.getSockAddr());
|
||||
|
||||
// let the domain handler know we sent another check in or connect packet
|
||||
_domainHandler.sentCheckInPacket();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
|
||||
case PacketType::AvatarQuery:
|
||||
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentityRequest:
|
||||
return 22;
|
||||
default:
|
||||
return 21;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
ICEServerQuery,
|
||||
OctreeStats,
|
||||
UNUSED_PACKET_TYPE_1,
|
||||
UNUSED_PACKET_TYPE_2,
|
||||
AvatarIdentityRequest,
|
||||
AssignmentClientStatus,
|
||||
NoisyMute,
|
||||
AvatarIdentity,
|
||||
|
|
|
@ -20,12 +20,6 @@
|
|||
|
||||
#include "OctreeLogging.h"
|
||||
|
||||
OctreeProcessor::OctreeProcessor() :
|
||||
_tree(NULL),
|
||||
_managedTree(false)
|
||||
{
|
||||
}
|
||||
|
||||
void OctreeProcessor::init() {
|
||||
if (!_tree) {
|
||||
_tree = createTree();
|
||||
|
@ -34,6 +28,9 @@ void OctreeProcessor::init() {
|
|||
}
|
||||
|
||||
OctreeProcessor::~OctreeProcessor() {
|
||||
if (_tree) {
|
||||
_tree->eraseAllOctreeElements(false);
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeProcessor::setTree(OctreePointer newTree) {
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
class OctreeProcessor : public QObject, public QEnableSharedFromThis<OctreeProcessor> {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OctreeProcessor();
|
||||
virtual ~OctreeProcessor();
|
||||
|
||||
virtual char getMyNodeType() const = 0;
|
||||
|
@ -61,7 +60,7 @@ protected:
|
|||
virtual OctreePointer createTree() = 0;
|
||||
|
||||
OctreePointer _tree;
|
||||
bool _managedTree;
|
||||
bool _managedTree { false };
|
||||
|
||||
SimpleMovingAverage _elementsPerPacket;
|
||||
SimpleMovingAverage _entitiesPerPacket;
|
||||
|
|
|
@ -269,7 +269,8 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
}
|
||||
btQuaternion deltaRot = desiredRot * startRot.inverse();
|
||||
float angularSpeed = deltaRot.getAngle() / _followTimeRemaining;
|
||||
btQuaternion angularDisplacement = btQuaternion(deltaRot.getAxis(), angularSpeed * dt);
|
||||
glm::vec3 rotationAxis = glm::normalize(glm::axis(bulletToGLM(deltaRot))); // deltaRot.getAxis() is inaccurate
|
||||
btQuaternion angularDisplacement = btQuaternion(glmToBullet(rotationAxis), angularSpeed * dt);
|
||||
btQuaternion endRot = angularDisplacement * startRot;
|
||||
|
||||
// in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account.
|
||||
|
@ -378,9 +379,6 @@ void CharacterController::setState(State desiredState, const char* reason) {
|
|||
#else
|
||||
void CharacterController::setState(State desiredState) {
|
||||
#endif
|
||||
if (!_flyingAllowed && desiredState == State::Hover) {
|
||||
desiredState = State::InAir;
|
||||
}
|
||||
|
||||
if (desiredState != _state) {
|
||||
#ifdef DEBUG_STATE_CHANGE
|
||||
|
@ -746,7 +744,7 @@ void CharacterController::updateState() {
|
|||
const float JUMP_SPEED = _scaleFactor * DEFAULT_AVATAR_JUMP_SPEED;
|
||||
if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) {
|
||||
SET_STATE(State::Ground, "hit ground");
|
||||
} else {
|
||||
} else if (_flyingAllowed) {
|
||||
btVector3 desiredVelocity = _targetVelocity;
|
||||
if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||
desiredVelocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||
|
@ -760,14 +758,17 @@ void CharacterController::updateState() {
|
|||
// Transition to hover if we are above the fall threshold
|
||||
SET_STATE(State::Hover, "above fall threshold");
|
||||
}
|
||||
} else if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::Hover:
|
||||
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
||||
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
||||
|
||||
if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||
if (!_flyingAllowed && rayHasHit) {
|
||||
SET_STATE(State::InAir, "flying not allowed");
|
||||
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||
SET_STATE(State::InAir, "near ground");
|
||||
} else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) {
|
||||
SET_STATE(State::Ground, "touching ground");
|
||||
|
@ -826,9 +827,6 @@ bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPositio
|
|||
void CharacterController::setFlyingAllowed(bool value) {
|
||||
if (value != _flyingAllowed) {
|
||||
_flyingAllowed = value;
|
||||
if (!_flyingAllowed && _state == State::Hover) {
|
||||
SET_STATE(State::InAir, "flying not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -834,3 +834,14 @@ void EntityMotionState::clearObjectVelocities() const {
|
|||
}
|
||||
_entity->setAcceleration(glm::vec3(0.0f));
|
||||
}
|
||||
|
||||
void EntityMotionState::saveKinematicState(btScalar timeStep) {
|
||||
_body->saveKinematicState(timeStep);
|
||||
|
||||
// This is a WORKAROUND for a quirk in Bullet: due to floating point error slow spinning kinematic objects will
|
||||
// have a measured angular velocity of zero. This probably isn't a bug that the Bullet team is interested in
|
||||
// fixing since there is one very simple workaround: use double-precision math for the physics simulation.
|
||||
// We're not ready migrate to double-precision yet so we explicitly work around it by slamming the RigidBody's
|
||||
// angular velocity with the value in the entity.
|
||||
_body->setAngularVelocity(glmToBullet(_entity->getWorldAngularVelocity()));
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ public:
|
|||
OwnershipState getOwnershipState() const { return _ownershipState; }
|
||||
|
||||
void setRegion(uint8_t region);
|
||||
void saveKinematicState(btScalar timeStep) override;
|
||||
|
||||
protected:
|
||||
void updateSendVelocities();
|
||||
|
|
|
@ -347,7 +347,7 @@ void ObjectMotionState::updateLastKinematicStep() {
|
|||
}
|
||||
|
||||
void ObjectMotionState::updateBodyMassProperties() {
|
||||
float mass = getMass();
|
||||
btScalar mass = getMass();
|
||||
btVector3 inertia(1.0f, 1.0f, 1.0f);
|
||||
if (mass > 0.0f) {
|
||||
_body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
||||
|
@ -356,3 +356,7 @@ void ObjectMotionState::updateBodyMassProperties() {
|
|||
_body->updateInertiaTensor();
|
||||
}
|
||||
|
||||
void ObjectMotionState::saveKinematicState(btScalar timeStep) {
|
||||
_body->saveKinematicState(timeStep);
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,7 @@ public:
|
|||
|
||||
virtual bool isLocallyOwned() const { return false; }
|
||||
virtual bool isLocallyOwnedOrShouldBe() const { return false; } // aka shouldEmitCollisionEvents()
|
||||
virtual void saveKinematicState(btScalar timeStep);
|
||||
|
||||
friend class PhysicsEngine;
|
||||
|
||||
|
|
|
@ -162,7 +162,13 @@ void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) {
|
|||
for (int i=0;i<m_nonStaticRigidBodies.size();i++) {
|
||||
btRigidBody* body = m_nonStaticRigidBodies[i];
|
||||
if (body && body->isKinematicObject() && body->getActivationState() != ISLAND_SLEEPING) {
|
||||
body->saveKinematicState(timeStep);
|
||||
if (body->getMotionState()) {
|
||||
btMotionState* motionState = body->getMotionState();
|
||||
ObjectMotionState* objectMotionState = static_cast<ObjectMotionState*>(motionState);
|
||||
objectMotionState->saveKinematicState(timeStep);
|
||||
} else {
|
||||
body->saveKinematicState(timeStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,9 +393,6 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent,
|
|||
_sharedObject->setRootItem(newItem);
|
||||
}
|
||||
|
||||
qmlComponent->completeCreate();
|
||||
qmlComponent->deleteLater();
|
||||
|
||||
onItemCreated(qmlContext, newItem);
|
||||
|
||||
if (!rootCreated) {
|
||||
|
@ -405,6 +402,8 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent,
|
|||
// Call this callback after rootitem is set, otherwise VrMenu wont work
|
||||
callback(qmlContext, newItem);
|
||||
}
|
||||
qmlComponent->completeCreate();
|
||||
qmlComponent->deleteLater();
|
||||
}
|
||||
|
||||
QQmlContext* OffscreenSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {
|
||||
|
|
|
@ -198,7 +198,7 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl
|
|||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += (ambientSpecular + directionalSpecular) / opacity;
|
||||
color += evalSpecularWithOpacity(ambientSpecular + directionalSpecular, opacity);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
|
|||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += (ambientSpecular + directionalSpecular) / opacity;
|
||||
color += evalSpecularWithOpacity(ambientSpecular + directionalSpecular, opacity);
|
||||
|
||||
// Haze
|
||||
if ((isHazeEnabled() > 0.0) && (hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) {
|
||||
|
@ -269,7 +269,7 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
|
|||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
|
||||
color += ambientDiffuse + directionalDiffuse;
|
||||
color += (ambientSpecular + directionalSpecular) / opacity;
|
||||
color += evalSpecularWithOpacity(ambientSpecular + directionalSpecular, opacity);
|
||||
|
||||
// Haze
|
||||
if ((isHazeEnabled() > 0.0) && (hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) {
|
||||
|
|
|
@ -197,7 +197,7 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl
|
|||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += (ambientSpecular + directionalSpecular) / opacity;
|
||||
color += evalSpecularWithOpacity(ambientSpecular + directionalSpecular, opacity);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
|
|||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += (ambientSpecular + directionalSpecular) / opacity;
|
||||
color += evalSpecularWithOpacity(ambientSpecular + directionalSpecular, opacity);
|
||||
|
||||
// Haze
|
||||
// FIXME - temporarily removed until we support it for forward...
|
||||
|
|
|
@ -143,6 +143,6 @@ vec4 evalLocalLighting(ivec3 cluster, int numLights, vec3 fragWorldPos, SurfaceD
|
|||
fragSpecular *= isSpecularEnabled();
|
||||
|
||||
fragColor.rgb += fragDiffuse;
|
||||
fragColor.rgb += fragSpecular / opacity;
|
||||
fragColor.rgb += evalSpecularWithOpacity(fragSpecular, opacity);
|
||||
return fragColor;
|
||||
}
|
|
@ -314,6 +314,9 @@ void evalFragShadingGloss(out vec3 diffuse, out vec3 specular,
|
|||
specular = shading.xyz;
|
||||
}
|
||||
|
||||
vec3 evalSpecularWithOpacity(vec3 specular, float opacity) {
|
||||
return specular / opacity;
|
||||
}
|
||||
|
||||
<@if not GETFRESNEL0@>
|
||||
<@def GETFRESNEL0@>
|
||||
|
|
|
@ -267,7 +267,7 @@ vec3 fetchLightmapMap(vec2 uv) {
|
|||
|
||||
<@func discardTransparent(opacity)@>
|
||||
{
|
||||
if (<$opacity$> < 1.0) {
|
||||
if (<$opacity$> < 1e-6) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -44,6 +44,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -46,6 +46,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -45,6 +45,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -54,6 +54,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -31,6 +31,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -41,6 +41,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -64,3 +64,18 @@ const char* FACESHIFT_BLENDSHAPES[] = {
|
|||
};
|
||||
|
||||
const int NUM_FACESHIFT_BLENDSHAPES = sizeof(FACESHIFT_BLENDSHAPES) / sizeof(char*);
|
||||
|
||||
const int EYE_BLINK_L_INDEX = 0;
|
||||
const int EYE_BLINK_R_INDEX = 1;
|
||||
const int EYE_SQUINT_L_INDEX = 2;
|
||||
const int EYE_SQUINT_R_INDEX = 3;
|
||||
const int EYE_OPEN_L_INDEX = 8;
|
||||
const int EYE_OPEN_R_INDEX = 9;
|
||||
const int BROWS_U_L_INDEX = 17;
|
||||
const int BROWS_U_R_INDEX = 18;
|
||||
|
||||
|
||||
const int EYE_BLINK_INDICES[] = { EYE_BLINK_L_INDEX, EYE_BLINK_R_INDEX };
|
||||
const int EYE_SQUINT_INDICES[] = { EYE_SQUINT_L_INDEX, EYE_SQUINT_R_INDEX };
|
||||
const int EYE_OPEN_INDICES[] = { EYE_OPEN_L_INDEX, EYE_OPEN_R_INDEX };
|
||||
const int BROWS_U_INDICES[] = { BROWS_U_L_INDEX, BROWS_U_R_INDEX };
|
||||
|
|
|
@ -16,5 +16,10 @@
|
|||
extern const char* FACESHIFT_BLENDSHAPES[];
|
||||
/// The size of FACESHIFT_BLENDSHAPES
|
||||
extern const int NUM_FACESHIFT_BLENDSHAPES;
|
||||
// Eyes and Brows indices
|
||||
extern const int EYE_BLINK_INDICES[];
|
||||
extern const int EYE_OPEN_INDICES[];
|
||||
extern const int BROWS_U_INDICES[];
|
||||
extern const int EYE_SQUINT_INDICES[];
|
||||
|
||||
#endif // hifi_FaceshiftConstants_h
|
||||
#endif // hifi_FaceshiftConstants_h
|
||||
|
|
291
libraries/ui/src/InteractiveWindow.cpp
Normal file
291
libraries/ui/src/InteractiveWindow.cpp
Normal file
|
@ -0,0 +1,291 @@
|
|||
//
|
||||
// InteractiveWindow.cpp
|
||||
// libraries/ui/src
|
||||
//
|
||||
// Created by Thijs Wenker on 2018-06-25
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "InteractiveWindow.h"
|
||||
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "OffscreenUi.h"
|
||||
#include "shared/QtHelpers.h"
|
||||
|
||||
static auto CONTENT_WINDOW_QML = QUrl("InteractiveWindow.qml");
|
||||
|
||||
static const char* const FLAGS_PROPERTY = "flags";
|
||||
static const char* const SOURCE_PROPERTY = "source";
|
||||
static const char* const TITLE_PROPERTY = "title";
|
||||
static const char* const POSITION_PROPERTY = "position";
|
||||
static const char* const INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition";
|
||||
static const char* const SIZE_PROPERTY = "size";
|
||||
static const char* const INTERACTIVE_WINDOW_SIZE_PROPERTY = "interactiveWindowSize";
|
||||
static const char* const VISIBLE_PROPERTY = "visible";
|
||||
static const char* const INTERACTIVE_WINDOW_VISIBLE_PROPERTY = "interactiveWindowVisible";
|
||||
static const char* const EVENT_BRIDGE_PROPERTY = "eventBridge";
|
||||
static const char* const PRESENTATION_MODE_PROPERTY = "presentationMode";
|
||||
|
||||
static const QStringList KNOWN_SCHEMES = QStringList() << "http" << "https" << "file" << "about" << "atp" << "qrc";
|
||||
|
||||
void registerInteractiveWindowMetaType(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, interactiveWindowPointerToScriptValue, interactiveWindowPointerFromScriptValue);
|
||||
}
|
||||
|
||||
QScriptValue interactiveWindowPointerToScriptValue(QScriptEngine* engine, const InteractiveWindowPointer& in) {
|
||||
return engine->newQObject(in, QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
||||
void interactiveWindowPointerFromScriptValue(const QScriptValue& object, InteractiveWindowPointer& out) {
|
||||
if (const auto interactiveWindow = qobject_cast<InteractiveWindowPointer>(object.toQObject())) {
|
||||
out = interactiveWindow;
|
||||
}
|
||||
}
|
||||
|
||||
InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
offscreenUi->loadInNewContext(CONTENT_WINDOW_QML, [&](QQmlContext* context, QObject* object) {
|
||||
_qmlWindow = object;
|
||||
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
|
||||
if (properties.contains(FLAGS_PROPERTY)) {
|
||||
object->setProperty(FLAGS_PROPERTY, properties[FLAGS_PROPERTY].toUInt());
|
||||
}
|
||||
if (properties.contains(PRESENTATION_MODE_PROPERTY)) {
|
||||
object->setProperty(PRESENTATION_MODE_PROPERTY, properties[PRESENTATION_MODE_PROPERTY].toInt());
|
||||
}
|
||||
if (properties.contains(TITLE_PROPERTY)) {
|
||||
object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString());
|
||||
}
|
||||
if (properties.contains(SIZE_PROPERTY)) {
|
||||
const auto size = vec2FromVariant(properties[SIZE_PROPERTY]);
|
||||
object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y));
|
||||
}
|
||||
if (properties.contains(POSITION_PROPERTY)) {
|
||||
const auto position = vec2FromVariant(properties[POSITION_PROPERTY]);
|
||||
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y));
|
||||
}
|
||||
if (properties.contains(VISIBLE_PROPERTY)) {
|
||||
object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool());
|
||||
}
|
||||
|
||||
connect(object, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(interactiveWindowPositionChanged()), this, SIGNAL(positionChanged()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(interactiveWindowSizeChanged()), this, SIGNAL(sizeChanged()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SIGNAL(visibleChanged()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(presentationModeChanged()), this, SIGNAL(presentationModeChanged()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(titleChanged()), this, SIGNAL(titleChanged()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(windowClosed()), this, SIGNAL(closed()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(selfDestruct()), this, SLOT(close()), Qt::QueuedConnection);
|
||||
|
||||
QUrl sourceURL{ sourceUrl };
|
||||
// If the passed URL doesn't correspond to a known scheme, assume it's a local file path
|
||||
if (!KNOWN_SCHEMES.contains(sourceURL.scheme(), Qt::CaseInsensitive)) {
|
||||
sourceURL = QUrl::fromLocalFile(sourceURL.toString()).toString();
|
||||
}
|
||||
object->setProperty(SOURCE_PROPERTY, sourceURL);
|
||||
});
|
||||
}
|
||||
|
||||
InteractiveWindow::~InteractiveWindow() {
|
||||
close();
|
||||
}
|
||||
|
||||
void InteractiveWindow::sendToQml(const QVariant& message) {
|
||||
// Forward messages received from the script on to QML
|
||||
QMetaObject::invokeMethod(_qmlWindow, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||
}
|
||||
|
||||
void InteractiveWindow::emitScriptEvent(const QVariant& scriptMessage) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage));
|
||||
} else {
|
||||
emit scriptEventReceived(scriptMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveWindow::emitWebEvent(const QVariant& webMessage) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
|
||||
} else {
|
||||
emit webEventReceived(webMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveWindow::close() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "close");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_qmlWindow) {
|
||||
_qmlWindow->deleteLater();
|
||||
}
|
||||
_qmlWindow = nullptr;
|
||||
}
|
||||
|
||||
void InteractiveWindow::show() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "show");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_qmlWindow) {
|
||||
QMetaObject::invokeMethod(_qmlWindow, "show", Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveWindow::raise() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "raise");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_qmlWindow) {
|
||||
QMetaObject::invokeMethod(_qmlWindow, "raiseWindow", Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveWindow::qmlToScript(const QVariant& message) {
|
||||
if (message.canConvert<QJSValue>()) {
|
||||
emit fromQml(qvariant_cast<QJSValue>(message).toVariant());
|
||||
} else if (message.canConvert<QString>()) {
|
||||
emit fromQml(message.toString());
|
||||
} else {
|
||||
qWarning() << "Unsupported message type " << message;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveWindow::setVisible(bool visible) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setVisible", Q_ARG(bool, visible));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(INTERACTIVE_WINDOW_VISIBLE_PROPERTY, visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool InteractiveWindow::isVisible() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<InteractiveWindow*>(this), "isVisible", Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _qmlWindow->property(INTERACTIVE_WINDOW_VISIBLE_PROPERTY).toBool();
|
||||
}
|
||||
|
||||
glm::vec2 InteractiveWindow::getPosition() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec2 result;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<InteractiveWindow*>(this), "getPosition", Q_RETURN_ARG(glm::vec2, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return toGlm(_qmlWindow->property(INTERACTIVE_WINDOW_POSITION_PROPERTY).toPointF());
|
||||
}
|
||||
|
||||
void InteractiveWindow::setPosition(const glm::vec2& position) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(const glm::vec2&, position));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y));
|
||||
QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowPositionForMode", Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 InteractiveWindow::getSize() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec2 result;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<InteractiveWindow*>(this), "getSize", Q_RETURN_ARG(glm::vec2, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return {};
|
||||
}
|
||||
return toGlm(_qmlWindow->property(INTERACTIVE_WINDOW_SIZE_PROPERTY).toSize());
|
||||
}
|
||||
|
||||
void InteractiveWindow::setSize(const glm::vec2& size) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setSize", Q_ARG(const glm::vec2&, size));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y));
|
||||
QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowSizeForMode", Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
||||
QString InteractiveWindow::getTitle() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<InteractiveWindow*>(this), "getTitle", Q_RETURN_ARG(QString, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QString();
|
||||
}
|
||||
return _qmlWindow->property(TITLE_PROPERTY).toString();
|
||||
}
|
||||
|
||||
void InteractiveWindow::setTitle(const QString& title) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setTitle", Q_ARG(const QString&, title));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(TITLE_PROPERTY, title);
|
||||
}
|
||||
}
|
||||
|
||||
int InteractiveWindow::getPresentationMode() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
int result;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<InteractiveWindow*>(this), "getPresentationMode",
|
||||
Q_RETURN_ARG(int, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return Virtual;
|
||||
}
|
||||
return _qmlWindow->property(PRESENTATION_MODE_PROPERTY).toInt();
|
||||
}
|
||||
|
||||
void InteractiveWindow::setPresentationMode(int presentationMode) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setPresentationMode", Q_ARG(int, presentationMode));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(PRESENTATION_MODE_PROPERTY, presentationMode);
|
||||
}
|
||||
}
|
206
libraries/ui/src/InteractiveWindow.h
Normal file
206
libraries/ui/src/InteractiveWindow.h
Normal file
|
@ -0,0 +1,206 @@
|
|||
//
|
||||
// InteractiveWindow.h
|
||||
// libraries/ui/src
|
||||
//
|
||||
// Created by Thijs Wenker on 2018-06-25
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_InteractiveWindow_h
|
||||
#define hifi_InteractiveWindow_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
namespace InteractiveWindowEnums {
|
||||
Q_NAMESPACE
|
||||
|
||||
enum InteractiveWindowFlags : uint8_t {
|
||||
AlwaysOnTop = 1 << 0,
|
||||
CloseButtonHides = 1 << 1
|
||||
};
|
||||
Q_ENUM_NS(InteractiveWindowFlags);
|
||||
|
||||
enum InteractiveWindowPresentationMode {
|
||||
Virtual,
|
||||
Native
|
||||
};
|
||||
Q_ENUM_NS(InteractiveWindowPresentationMode);
|
||||
}
|
||||
|
||||
using namespace InteractiveWindowEnums;
|
||||
|
||||
/**jsdoc
|
||||
* @class InteractiveWindow
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-en
|
||||
*
|
||||
* @property {string} title
|
||||
* @property {Vec2} position
|
||||
* @property {Vec2} size
|
||||
* @property {boolean} visible
|
||||
* @property {Desktop.PresentationMode} presentationMode
|
||||
*
|
||||
*/
|
||||
class InteractiveWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
|
||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
|
||||
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
|
||||
|
||||
public:
|
||||
InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties);
|
||||
|
||||
~InteractiveWindow();
|
||||
|
||||
private:
|
||||
// define property getters and setters as private to not expose them to the JS API
|
||||
Q_INVOKABLE QString getTitle() const;
|
||||
Q_INVOKABLE void setTitle(const QString& title);
|
||||
|
||||
Q_INVOKABLE glm::vec2 getPosition() const;
|
||||
Q_INVOKABLE void setPosition(const glm::vec2& position);
|
||||
|
||||
Q_INVOKABLE glm::vec2 getSize() const;
|
||||
Q_INVOKABLE void setSize(const glm::vec2& size);
|
||||
|
||||
Q_INVOKABLE void setVisible(bool visible);
|
||||
Q_INVOKABLE bool isVisible() const;
|
||||
|
||||
Q_INVOKABLE void setPresentationMode(int presentationMode);
|
||||
Q_INVOKABLE int getPresentationMode() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.sendToQml
|
||||
* @param {object} message
|
||||
*/
|
||||
// Scripts can use this to send a message to the QML object
|
||||
void sendToQml(const QVariant& message);
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.emitScriptEvent
|
||||
* @param {object} message
|
||||
*/
|
||||
// QmlWindow content may include WebView requiring EventBridge.
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.emitWebEvent
|
||||
* @param {object} message
|
||||
*/
|
||||
void emitWebEvent(const QVariant& webMessage);
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.close
|
||||
*/
|
||||
Q_INVOKABLE void close();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.show
|
||||
*/
|
||||
Q_INVOKABLE void show();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.raise
|
||||
*/
|
||||
Q_INVOKABLE void raise();
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.visibleChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void visibleChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.positionChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void positionChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.sizeChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void sizeChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.presentationModeChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void presentationModeChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.titleChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void titleChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.closed
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void closed();
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.fromQml
|
||||
* @param {object} message
|
||||
* @returns {Signal}
|
||||
*/
|
||||
// Scripts can connect to this signal to receive messages from the QML object
|
||||
void fromQml(const QVariant& message);
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.scriptEventReceived
|
||||
* @param {object} message
|
||||
* @returns {Signal}
|
||||
*/
|
||||
// InteractiveWindow content may include WebView requiring EventBridge.
|
||||
void scriptEventReceived(const QVariant& message);
|
||||
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.webEventReceived
|
||||
* @param {object} message
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void webEventReceived(const QVariant& message);
|
||||
|
||||
protected slots:
|
||||
/**jsdoc
|
||||
* @function InteractiveWindow.qmlToScript
|
||||
* @param {object} message
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void qmlToScript(const QVariant& message);
|
||||
|
||||
private:
|
||||
QPointer<QObject> _qmlWindow;
|
||||
};
|
||||
|
||||
typedef InteractiveWindow* InteractiveWindowPointer;
|
||||
|
||||
QScriptValue interactiveWindowPointerToScriptValue(QScriptEngine* engine, const InteractiveWindowPointer& in);
|
||||
void interactiveWindowPointerFromScriptValue(const QScriptValue& object, InteractiveWindowPointer& out);
|
||||
|
||||
void registerInteractiveWindowMetaType(QScriptEngine* engine);
|
||||
|
||||
Q_DECLARE_METATYPE(InteractiveWindowPointer)
|
||||
|
||||
#endif // hifi_InteractiveWindow_h
|
|
@ -46,7 +46,7 @@ void Space::processResets(const Transaction::Resets& transactions) {
|
|||
auto proxyID = std::get<0>(reset);
|
||||
|
||||
// Guard against proxyID being past the end of the list.
|
||||
if (proxyID < 0 || proxyID >= (int32_t)_proxies.size() || proxyID >= (int32_t)_owners.size()) {
|
||||
if (!_IDAllocator.checkIndex(proxyID)) {
|
||||
continue;
|
||||
}
|
||||
auto& item = _proxies[proxyID];
|
||||
|
@ -61,6 +61,9 @@ void Space::processResets(const Transaction::Resets& transactions) {
|
|||
|
||||
void Space::processRemoves(const Transaction::Removes& transactions) {
|
||||
for (auto removedID : transactions) {
|
||||
if (!_IDAllocator.checkIndex(removedID)) {
|
||||
continue;
|
||||
}
|
||||
_IDAllocator.freeIndex(removedID);
|
||||
|
||||
// Access the true item
|
||||
|
@ -75,7 +78,7 @@ void Space::processRemoves(const Transaction::Removes& transactions) {
|
|||
void Space::processUpdates(const Transaction::Updates& transactions) {
|
||||
for (auto& update : transactions) {
|
||||
auto updateID = std::get<0>(update);
|
||||
if (updateID == INVALID_PROXY_ID) {
|
||||
if (!_IDAllocator.checkIndex(updateID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -141,6 +144,7 @@ uint8_t Space::getRegion(int32_t proxyID) const {
|
|||
}
|
||||
|
||||
void Space::clear() {
|
||||
Collection::clear();
|
||||
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||
_IDAllocator.clear();
|
||||
_proxies.clear();
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
uint32_t getNumViews() const { return (uint32_t)(_views.size()); }
|
||||
void copyViews(std::vector<View>& copy) const;
|
||||
|
||||
uint32_t getNumObjects() const { return _IDAllocator.getNumLiveIndices(); } // (uint32_t)(_proxies.size() - _freeIndices.size()); }
|
||||
uint32_t getNumObjects() const { return _IDAllocator.getNumLiveIndices(); }
|
||||
uint32_t getNumAllocatedProxies() const { return (uint32_t)(_IDAllocator.getNumAllocatedIndices()); }
|
||||
|
||||
void categorizeAndGetChanges(std::vector<Change>& changes);
|
||||
|
@ -51,7 +51,7 @@ public:
|
|||
const Owner getOwner(int32_t proxyID) const;
|
||||
uint8_t getRegion(int32_t proxyID) const;
|
||||
|
||||
void clear();
|
||||
void clear() override;
|
||||
private:
|
||||
|
||||
void processTransactionFrame(const Transaction& transaction) override;
|
||||
|
|
|
@ -111,6 +111,12 @@ Collection::Collection() {
|
|||
Collection::~Collection() {
|
||||
}
|
||||
|
||||
void Collection::clear() {
|
||||
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
|
||||
_transactionQueue.clear();
|
||||
_transactionFrames.clear();
|
||||
}
|
||||
|
||||
ProxyID Collection::allocateID() {
|
||||
// Just increment and return the previous value initialized at 0
|
||||
return _IDAllocator.allocateIndex();
|
||||
|
|
|
@ -137,6 +137,8 @@ public:
|
|||
Collection();
|
||||
~Collection();
|
||||
|
||||
virtual void clear();
|
||||
|
||||
// This call is thread safe, can be called from anywhere to allocate a new ID
|
||||
ProxyID allocateID();
|
||||
|
||||
|
|
34
scripts/developer/tests/interactiveWindowTest.js
Normal file
34
scripts/developer/tests/interactiveWindowTest.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// interactiveWindowTest.js
|
||||
//
|
||||
// Created by Thijs Wenker on 2018-07-03
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// An example of an interactive window that toggles presentation mode when toggling HMD on/off
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
function getPreferredPresentationMode() {
|
||||
return HMD.active ? Desktop.PresentationMode.VIRTUAL : Desktop.PresentationMode.NATIVE;
|
||||
}
|
||||
|
||||
function getPreferredTitle() {
|
||||
return HMD.active ? 'Virtual Desktop Window' : 'Native Desktop Window';
|
||||
}
|
||||
|
||||
var virtualWindow = Desktop.createWindow(Script.resourcesPath() + 'qml/OverlayWindowTest.qml', {
|
||||
title: getPreferredTitle(),
|
||||
flags: Desktop.ALWAYS_ON_TOP,
|
||||
presentationMode: getPreferredPresentationMode(),
|
||||
size: {x: 500, y: 400}
|
||||
});
|
||||
|
||||
HMD.displayModeChanged.connect(function() {
|
||||
virtualWindow.presentationMode = getPreferredPresentationMode();
|
||||
virtualWindow.title = getPreferredTitle();
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
virtualWindow.close();
|
||||
});
|
106
scripts/system/+android/clickWeb.js
Normal file
106
scripts/system/+android/clickWeb.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
"use strict";
|
||||
//
|
||||
// clickWeb.js
|
||||
// scripts/system/+android
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var logEnabled = false;
|
||||
var touchOverlayID;
|
||||
var touchEntityID;
|
||||
|
||||
function printd(str) {
|
||||
if (logEnabled)
|
||||
print("[clickWeb.js] " + str);
|
||||
}
|
||||
|
||||
function intersectsWebOverlay(intersection) {
|
||||
return intersection && intersection.intersects && intersection.overlayID &&
|
||||
Overlays.getOverlayType(intersection.overlayID) == "web3d";
|
||||
}
|
||||
|
||||
function intersectsWebEntity(intersection) {
|
||||
if (intersection && intersection.intersects && intersection.entityID) {
|
||||
var properties = Entities.getEntityProperties(intersection.entityID, ["type", "sourceUrl"]);
|
||||
return properties.type && properties.type == "Web" && properties.sourceUrl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function findRayIntersection(pickRay) {
|
||||
// Check 3D overlays and entities. Argument is an object with origin and direction.
|
||||
var overlayRayIntersection = Overlays.findRayIntersection(pickRay);
|
||||
var entityRayIntersection = Entities.findRayIntersection(pickRay, true);
|
||||
var isOverlayInters = intersectsWebOverlay(overlayRayIntersection);
|
||||
var isEntityInters = intersectsWebEntity(entityRayIntersection);
|
||||
if (isOverlayInters &&
|
||||
(!isEntityInters ||
|
||||
overlayRayIntersection.distance < entityRayIntersection.distance)) {
|
||||
return { type: 'overlay', obj: overlayRayIntersection };
|
||||
} else if (isEntityInters &&
|
||||
(!isOverlayInters ||
|
||||
entityRayIntersection.distance < overlayRayIntersection.distance)) {
|
||||
return { type: 'entity', obj: entityRayIntersection };
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function touchBegin(event) {
|
||||
var intersection = findRayIntersection(Camera.computePickRay(event.x, event.y));
|
||||
if (intersection && intersection.type == 'overlay') {
|
||||
touchOverlayID = intersection.obj.overlayID;
|
||||
touchEntityID = null;
|
||||
} else if (intersection && intersection.type == 'entity') {
|
||||
touchEntityID = intersection.obj.entityID;
|
||||
touchOverlayID = null;
|
||||
}
|
||||
}
|
||||
|
||||
function touchEnd(event) {
|
||||
var intersection = findRayIntersection(Camera.computePickRay(event.x, event.y));
|
||||
if (intersection && intersection.type == 'overlay' && touchOverlayID == intersection.obj.overlayID) {
|
||||
var propertiesToGet = {};
|
||||
propertiesToGet[overlayID] = ['url'];
|
||||
var properties = Overlays.getOverlaysProperties(propertiesToGet);
|
||||
if (properties[overlayID].url) {
|
||||
Window.openUrl(properties[overlayID].url);
|
||||
}
|
||||
} else if (intersection && intersection.type == 'entity' && touchEntityID == intersection.obj.entityID) {
|
||||
var properties = Entities.getEntityProperties(touchEntityID, ["sourceUrl"]);
|
||||
if (properties.sourceUrl) {
|
||||
Window.openUrl(properties.sourceUrl);
|
||||
}
|
||||
}
|
||||
|
||||
touchOverlayID = null;
|
||||
touchEntityID = null;
|
||||
}
|
||||
|
||||
function ending() {
|
||||
Controller.touchBeginEvent.disconnect(touchBegin);
|
||||
Controller.touchEndEvent.disconnect(touchEnd);
|
||||
}
|
||||
|
||||
function init() {
|
||||
Controller.touchBeginEvent.connect(touchBegin);
|
||||
Controller.touchEndEvent.connect(touchEnd);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
ending();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
ending: ending
|
||||
}
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
|
@ -29,6 +29,7 @@ var logEnabled = false;
|
|||
var radar = Script.require('./radar.js');
|
||||
var uniqueColor = Script.require('./uniqueColor.js');
|
||||
var displayNames = Script.require('./displayNames.js');
|
||||
var clickWeb = Script.require('./clickWeb.js');
|
||||
|
||||
function printd(str) {
|
||||
if (logEnabled) {
|
||||
|
@ -97,9 +98,11 @@ function switchToMode(newMode) {
|
|||
if (currentMode == MODE_RADAR) {
|
||||
radar.startRadarMode();
|
||||
displayNames.ending();
|
||||
clickWeb.ending();
|
||||
} else if (currentMode == MODE_MY_VIEW) {
|
||||
// nothing to do yet
|
||||
displayNames.init();
|
||||
clickWeb.init();
|
||||
} else {
|
||||
printd("Unknown view mode " + currentMode);
|
||||
}
|
||||
|
|
|
@ -108,14 +108,13 @@ createControllerDisplay = function(config) {
|
|||
for (var partName in controller.parts) {
|
||||
overlayID = this.overlays[i++];
|
||||
var part = controller.parts[partName];
|
||||
localPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition));
|
||||
localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition);
|
||||
var localRotation;
|
||||
var value = this.partValues[partName];
|
||||
var offset, rotation;
|
||||
if (value !== undefined) {
|
||||
if (part.type === "linear") {
|
||||
var axis = Vec3.multiplyQbyV(controller.rotation, part.axis);
|
||||
offset = Vec3.multiply(part.maxTranslation * value, axis);
|
||||
offset = Vec3.multiply(part.maxTranslation * value, part.axis);
|
||||
localPosition = Vec3.sum(localPosition, offset);
|
||||
localRotation = undefined;
|
||||
} else if (part.type === "joystick") {
|
||||
|
@ -126,8 +125,8 @@ createControllerDisplay = function(config) {
|
|||
} else {
|
||||
offset = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
localPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition)));
|
||||
localRotation = Quat.multiply(controller.rotation, rotation);
|
||||
localPosition = Vec3.sum(offset, localPosition);
|
||||
localRotation = rotation;
|
||||
} else if (part.type === "rotational") {
|
||||
value = clamp(value, part.minValue, part.maxValue);
|
||||
var pct = (value - part.minValue) / part.maxValue;
|
||||
|
@ -139,8 +138,8 @@ createControllerDisplay = function(config) {
|
|||
} else {
|
||||
offset = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
localPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition)));
|
||||
localRotation = Quat.multiply(controller.rotation, rotation);
|
||||
localPosition = Vec3.sum(offset, localPosition);
|
||||
localRotation = rotation;
|
||||
}
|
||||
}
|
||||
if (localRotation !== undefined) {
|
||||
|
@ -169,9 +168,11 @@ createControllerDisplay = function(config) {
|
|||
|
||||
if (controller.naturalPosition) {
|
||||
position = Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position);
|
||||
} else {
|
||||
controller.naturalPosition = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
|
||||
var overlayID = Overlays.addOverlay("model", {
|
||||
var baseOverlayID = Overlays.addOverlay("model", {
|
||||
url: controller.modelURL,
|
||||
dimensions: Vec3.multiply(sensorScaleFactor, controller.dimensions),
|
||||
localRotation: controller.rotation,
|
||||
|
@ -181,23 +182,21 @@ createControllerDisplay = function(config) {
|
|||
ignoreRayIntersection: true
|
||||
});
|
||||
|
||||
controllerDisplay.overlays.push(overlayID);
|
||||
overlayID = null;
|
||||
controllerDisplay.overlays.push(baseOverlayID);
|
||||
|
||||
if (controller.parts) {
|
||||
for (var partName in controller.parts) {
|
||||
var part = controller.parts[partName];
|
||||
var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition));
|
||||
var innerRotation = controller.rotation;
|
||||
var localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition);
|
||||
var localRotation = { x: 0, y: 0, z: 0, w: 1 }
|
||||
|
||||
controllerDisplay.parts[partName] = controller.parts[partName];
|
||||
|
||||
var properties = {
|
||||
url: part.modelURL,
|
||||
localPosition: partPosition,
|
||||
localRotation: innerRotation,
|
||||
parentID: MyAvatar.SELF_ID,
|
||||
parentJointIndex: controller.jointIndex,
|
||||
localPosition: localPosition,
|
||||
localRotation: localRotation,
|
||||
parentID: baseOverlayID,
|
||||
ignoreRayIntersection: true
|
||||
};
|
||||
|
||||
|
@ -207,11 +206,10 @@ createControllerDisplay = function(config) {
|
|||
properties['textures'] = textures;
|
||||
}
|
||||
|
||||
overlayID = Overlays.addOverlay("model", properties);
|
||||
var overlayID = Overlays.addOverlay("model", properties);
|
||||
|
||||
if (part.type === "rotational") {
|
||||
var input = resolveHardware(part.input);
|
||||
print("Mapping to: ", part.input, input);
|
||||
mapping.from([input]).peek().to(function(partName) {
|
||||
return function(value) {
|
||||
// insert the most recent controller value into controllerDisplay.partValues.
|
||||
|
|
|
@ -50,19 +50,19 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
return this.hand === RIGHT_HAND ? leftOverlayLaserInput : rightOverlayLaserInput;
|
||||
};
|
||||
|
||||
this.isPointingAtTabletOrWeb = function(controllerData, triggerPressed) {
|
||||
this.isPointingAtTriggerable = function(controllerData, triggerPressed) {
|
||||
// allow pointing at tablet, unlocked web entities, or web overlays automatically without pressing trigger,
|
||||
// but for pointing at locked web entities or non-web overlays user must be pressing trigger
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
if (intersection.type === Picks.INTERSECTED_OVERLAY) {
|
||||
var objectID = intersection.objectID;
|
||||
if ((HMD.tabletID && objectID === HMD.tabletID) ||
|
||||
(HMD.tabletScreenID && objectID === HMD.tabletScreenID) ||
|
||||
(HMD.tabletScreenID && objectID === HMD.tabletScreenID) ||
|
||||
(HMD.homeButtonID && objectID === HMD.homeButtonID)) {
|
||||
return true;
|
||||
} else {
|
||||
var overlayType = Overlays.getOverlayType(objectID);
|
||||
if (overlayType === "web3d") {
|
||||
return true;
|
||||
}
|
||||
return overlayType === "web3d" || triggerPressed;
|
||||
}
|
||||
} else if (intersection.type === Picks.INTERSECTED_ENTITY) {
|
||||
var entityProperty = Entities.getEntityProperties(intersection.objectID);
|
||||
|
@ -103,7 +103,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE &&
|
||||
controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
|
||||
var allowThisModule = !otherModuleRunning || isTriggerPressed;
|
||||
if (allowThisModule && this.isPointingAtTabletOrWeb(controllerData, isTriggerPressed)) {
|
||||
if (allowThisModule && this.isPointingAtTriggerable(controllerData, isTriggerPressed)) {
|
||||
this.updateAllwaysOn();
|
||||
if (isTriggerPressed) {
|
||||
this.dominantHandOverride = true; // Override dominant hand.
|
||||
|
@ -124,7 +124,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
var allowThisModule = !otherModuleRunning && !grabModuleNeedsToRun;
|
||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||
var laserOn = isTriggerPressed || this.parameters.handLaser.allwaysOn;
|
||||
if (allowThisModule && (laserOn && this.isPointingAtTabletOrWeb(controllerData, isTriggerPressed))) {
|
||||
if (allowThisModule && (laserOn && this.isPointingAtTriggerable(controllerData, isTriggerPressed))) {
|
||||
this.running = true;
|
||||
return makeRunningValues(true, [], []);
|
||||
}
|
||||
|
|
|
@ -1782,9 +1782,9 @@ var keyReleaseEvent = function (event) {
|
|||
// since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items
|
||||
if (event.text === "DELETE") {
|
||||
deleteSelectedEntities();
|
||||
} else if (event.text === "ESC") {
|
||||
} else if (event.text === 'd' && event.isControl) {
|
||||
selectionManager.clearSelections();
|
||||
} else if (event.text === "TAB") {
|
||||
} else if (event.text === "t") {
|
||||
selectionDisplay.toggleSpaceMode();
|
||||
} else if (event.text === "f") {
|
||||
if (isActive) {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/* global SelectionManager, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
|
||||
/* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
|
||||
getMainTabletIDs, getControllerWorldLocation */
|
||||
|
||||
var SPACE_LOCAL = "local";
|
||||
|
@ -72,7 +72,9 @@ SelectionManager = (function() {
|
|||
|
||||
subscribeToUpdateMessages();
|
||||
|
||||
var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 }
|
||||
// disabling this for now as it is causing rendering issues with the other handle overlays
|
||||
/*
|
||||
var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 };
|
||||
var editHandleOutlineStyle = {
|
||||
outlineUnoccludedColor: COLOR_ORANGE_HIGHLIGHT,
|
||||
outlineOccludedColor: COLOR_ORANGE_HIGHLIGHT,
|
||||
|
@ -85,8 +87,8 @@ SelectionManager = (function() {
|
|||
outlineWidth: 3,
|
||||
isOutlineSmooth: true
|
||||
};
|
||||
// disabling this for now as it is causing rendering issues with the other handle overlays
|
||||
//Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle);
|
||||
Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle);
|
||||
*/
|
||||
|
||||
that.savedProperties = {};
|
||||
that.selections = [];
|
||||
|
@ -180,18 +182,67 @@ SelectionManager = (function() {
|
|||
that.selections = [];
|
||||
that._update(true);
|
||||
};
|
||||
|
||||
that.addChildrenEntities = function(parentEntityID, entityList) {
|
||||
var children = Entities.getChildrenIDs(parentEntityID);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var childID = children[i];
|
||||
if (entityList.indexOf(childID) < 0) {
|
||||
entityList.push(childID);
|
||||
}
|
||||
that.addChildrenEntities(childID, entityList);
|
||||
}
|
||||
};
|
||||
|
||||
that.duplicateSelection = function() {
|
||||
var entitiesToDuplicate = [];
|
||||
var duplicatedEntityIDs = [];
|
||||
Object.keys(that.savedProperties).forEach(function(otherEntityID) {
|
||||
var properties = that.savedProperties[otherEntityID];
|
||||
var duplicatedChildrenWithOldParents = [];
|
||||
var originalEntityToNewEntityID = [];
|
||||
|
||||
// build list of entities to duplicate by including any unselected children of selected parent entities
|
||||
Object.keys(that.savedProperties).forEach(function(originalEntityID) {
|
||||
if (entitiesToDuplicate.indexOf(originalEntityID) < 0) {
|
||||
entitiesToDuplicate.push(originalEntityID);
|
||||
}
|
||||
that.addChildrenEntities(originalEntityID, entitiesToDuplicate);
|
||||
});
|
||||
|
||||
// duplicate entities from above and store their original to new entity mappings and children needing re-parenting
|
||||
for (var i = 0; i < entitiesToDuplicate.length; i++) {
|
||||
var originalEntityID = entitiesToDuplicate[i];
|
||||
var properties = that.savedProperties[originalEntityID];
|
||||
if (properties === undefined) {
|
||||
properties = Entities.getEntityProperties(originalEntityID);
|
||||
}
|
||||
if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) {
|
||||
var newEntityID = Entities.addEntity(properties);
|
||||
duplicatedEntityIDs.push({
|
||||
entityID: Entities.addEntity(properties),
|
||||
entityID: newEntityID,
|
||||
properties: properties
|
||||
});
|
||||
if (properties.parentID !== Uuid.NULL) {
|
||||
duplicatedChildrenWithOldParents[newEntityID] = properties.parentID;
|
||||
}
|
||||
originalEntityToNewEntityID[originalEntityID] = newEntityID;
|
||||
}
|
||||
}
|
||||
|
||||
// re-parent duplicated children to the duplicate entities of their original parents (if they were duplicated)
|
||||
Object.keys(duplicatedChildrenWithOldParents).forEach(function(childIDNeedingNewParent) {
|
||||
var originalParentID = duplicatedChildrenWithOldParents[childIDNeedingNewParent];
|
||||
var newParentID = originalEntityToNewEntityID[originalParentID];
|
||||
if (newParentID) {
|
||||
Entities.editEntity(childIDNeedingNewParent, { parentID: newParentID });
|
||||
for (var i = 0; i < duplicatedEntityIDs.length; i++) {
|
||||
var duplicatedEntity = duplicatedEntityIDs[i];
|
||||
if (duplicatedEntity.entityID === childIDNeedingNewParent) {
|
||||
duplicatedEntity.properties.parentID = newParentID;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return duplicatedEntityIDs;
|
||||
};
|
||||
|
||||
|
@ -215,8 +266,10 @@ SelectionManager = (function() {
|
|||
that.worldRotation = properties.boundingBox.rotation;
|
||||
|
||||
that.entityType = properties.type;
|
||||
|
||||
SelectionDisplay.setSpaceMode(SPACE_LOCAL);
|
||||
|
||||
if (selectionUpdated) {
|
||||
SelectionDisplay.setSpaceMode(SPACE_LOCAL);
|
||||
}
|
||||
} else {
|
||||
that.localRotation = null;
|
||||
that.localDimensions = null;
|
||||
|
@ -360,8 +413,6 @@ SelectionDisplay = (function() {
|
|||
|
||||
var spaceMode = SPACE_LOCAL;
|
||||
var overlayNames = [];
|
||||
var lastCameraPosition = Camera.getPosition();
|
||||
var lastCameraOrientation = Camera.getOrientation();
|
||||
var lastControllerPoses = [
|
||||
getControllerWorldLocation(Controller.Standard.LeftHand, true),
|
||||
getControllerWorldLocation(Controller.Standard.RightHand, true)
|
||||
|
@ -381,7 +432,7 @@ SelectionDisplay = (function() {
|
|||
|
||||
var ctrlPressed = false;
|
||||
|
||||
var replaceCollisionsAfterStretch = false;
|
||||
that.replaceCollisionsAfterStretch = false;
|
||||
|
||||
var handlePropertiesTranslateArrowCones = {
|
||||
shape: "Cone",
|
||||
|
@ -967,7 +1018,7 @@ SelectionDisplay = (function() {
|
|||
if (SelectionManager.hasSelection()) {
|
||||
var controllerPose = getControllerWorldLocation(activeHand, true);
|
||||
var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1;
|
||||
if (controllerPose.valid && lastControllerPoses[hand].valid) {
|
||||
if (controllerPose.valid && lastControllerPoses[hand].valid && that.triggered) {
|
||||
if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) ||
|
||||
!Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) {
|
||||
that.mouseMoveEvent({});
|
||||
|
@ -1017,9 +1068,6 @@ SelectionDisplay = (function() {
|
|||
that.select = function(entityID, event) {
|
||||
var properties = Entities.getEntityProperties(SelectionManager.selections[0]);
|
||||
|
||||
lastCameraPosition = Camera.getPosition();
|
||||
lastCameraOrientation = Camera.getOrientation();
|
||||
|
||||
if (event !== false) {
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
|
@ -1435,7 +1483,7 @@ SelectionDisplay = (function() {
|
|||
that.setHandleRotateYawVisible(!activeTool || isActiveTool(handleRotateYawRing));
|
||||
that.setHandleRotateRollVisible(!activeTool || isActiveTool(handleRotateRollRing));
|
||||
|
||||
var showScaleStretch = !activeTool && SelectionManager.selections.length === 1;
|
||||
var showScaleStretch = !activeTool && SelectionManager.selections.length === 1 && spaceMode === SPACE_LOCAL;
|
||||
that.setHandleStretchXVisible(showScaleStretch || isActiveTool(handleStretchXSphere));
|
||||
that.setHandleStretchYVisible(showScaleStretch || isActiveTool(handleStretchYSphere));
|
||||
that.setHandleStretchZVisible(showScaleStretch || isActiveTool(handleStretchZSphere));
|
||||
|
@ -1617,7 +1665,7 @@ SelectionDisplay = (function() {
|
|||
|
||||
translateXZTool.pickPlanePosition = pickResult.intersection;
|
||||
translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x,
|
||||
SelectionManager.worldDimensions.y),
|
||||
SelectionManager.worldDimensions.y),
|
||||
SelectionManager.worldDimensions.z);
|
||||
translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position);
|
||||
translateXZTool.startingElevation = translateXZTool.elevation(pickRay.origin, translateXZTool.pickPlanePosition);
|
||||
|
@ -1767,23 +1815,27 @@ SelectionDisplay = (function() {
|
|||
function addHandleTranslateTool(overlay, mode, direction) {
|
||||
var pickNormal = null;
|
||||
var lastPick = null;
|
||||
var initialPosition = null;
|
||||
var projectionVector = null;
|
||||
var previousPickRay = null;
|
||||
addHandleTool(overlay, {
|
||||
mode: mode,
|
||||
onBegin: function(event, pickRay, pickResult) {
|
||||
var axisVector;
|
||||
if (direction === TRANSLATE_DIRECTION.X) {
|
||||
pickNormal = { x: 0, y: 1, z: 1 };
|
||||
axisVector = { x: 1, y: 0, z: 0 };
|
||||
} else if (direction === TRANSLATE_DIRECTION.Y) {
|
||||
pickNormal = { x: 1, y: 0, z: 1 };
|
||||
axisVector = { x: 0, y: 1, z: 0 };
|
||||
} else if (direction === TRANSLATE_DIRECTION.Z) {
|
||||
pickNormal = { x: 1, y: 1, z: 0 };
|
||||
axisVector = { x: 0, y: 0, z: 1 };
|
||||
}
|
||||
|
||||
var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation;
|
||||
pickNormal = Vec3.multiplyQbyV(rotation, pickNormal);
|
||||
axisVector = Vec3.multiplyQbyV(rotation, axisVector);
|
||||
pickNormal = Vec3.cross(Vec3.cross(pickRay.direction, axisVector), axisVector);
|
||||
|
||||
lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
|
||||
initialPosition = SelectionManager.worldPosition;
|
||||
|
||||
SelectionManager.saveProperties();
|
||||
that.resetPreviousHandleColor();
|
||||
|
@ -1818,7 +1870,7 @@ SelectionDisplay = (function() {
|
|||
pickRay = previousPickRay;
|
||||
}
|
||||
|
||||
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
|
||||
var newIntersection = rayPlaneIntersection(pickRay, initialPosition, pickNormal);
|
||||
var vector = Vec3.subtract(newIntersection, lastPick);
|
||||
|
||||
if (direction === TRANSLATE_DIRECTION.X) {
|
||||
|
@ -1834,7 +1886,8 @@ SelectionDisplay = (function() {
|
|||
|
||||
var dotVector = Vec3.dot(vector, projectionVector);
|
||||
vector = Vec3.multiply(dotVector, projectionVector);
|
||||
vector = grid.snapToGrid(vector);
|
||||
var gridOrigin = grid.getOrigin();
|
||||
vector = Vec3.subtract(grid.snapToGrid(Vec3.sum(vector, gridOrigin)), gridOrigin);
|
||||
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
|
@ -2061,7 +2114,7 @@ SelectionDisplay = (function() {
|
|||
};
|
||||
|
||||
var onMove = function(event) {
|
||||
var proportional = (spaceMode === SPACE_WORLD) || directionEnum === STRETCH_DIRECTION.ALL;
|
||||
var proportional = directionEnum === STRETCH_DIRECTION.ALL;
|
||||
|
||||
var position, rotation;
|
||||
if (spaceMode === SPACE_LOCAL) {
|
||||
|
@ -2134,8 +2187,8 @@ SelectionDisplay = (function() {
|
|||
newDimensions = Vec3.sum(initialDimensions, changeInDimensions);
|
||||
}
|
||||
|
||||
var minimumDimension = directionEnum === STRETCH_DIRECTION.ALL ? STRETCH_ALL_MINIMUM_DIMENSION :
|
||||
STRETCH_MINIMUM_DIMENSION;
|
||||
var minimumDimension = directionEnum ===
|
||||
STRETCH_DIRECTION.ALL ? STRETCH_ALL_MINIMUM_DIMENSION : STRETCH_MINIMUM_DIMENSION;
|
||||
if (newDimensions.x < minimumDimension) {
|
||||
newDimensions.x = minimumDimension;
|
||||
changeInDimensions.x = minimumDimension - initialDimensions.x;
|
||||
|
@ -2229,8 +2282,7 @@ SelectionDisplay = (function() {
|
|||
selectedHandle = handleScaleRTFCube;
|
||||
}
|
||||
offset = Vec3.multiply(directionVector, NEGATE_VECTOR);
|
||||
var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVector,
|
||||
directionVector, offset, null, selectedHandle);
|
||||
var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVector, directionVector, offset, null, selectedHandle);
|
||||
return addHandleTool(overlay, tool);
|
||||
}
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ function printToPolaroid(image_url) {
|
|||
"gravity": { "x": 0, "y": -2.5, "z": 0 },
|
||||
|
||||
"velocity": { "x": 0, "y": 1.95, "z": 0 },
|
||||
"angularVelocity": { "x": -1.0, "y": 0, "z": -1.3 },
|
||||
"angularVelocity": Vec3.multiplyQbyV(MyAvatar.orientation, { "x": -1.0, "y": 0, "z": -1.3 }),
|
||||
|
||||
"dynamic": true,
|
||||
"collisionsWillMove": true,
|
||||
|
|
|
@ -87,7 +87,7 @@ local packet_types = {
|
|||
[23] = "ICEServerQuery",
|
||||
[24] = "OctreeStats",
|
||||
[25] = "Jurisdiction",
|
||||
[26] = "JurisdictionRequest",
|
||||
[26] = "AvatarIdentityRequest",
|
||||
[27] = "AssignmentClientStatus",
|
||||
[28] = "NoisyMute",
|
||||
[29] = "AvatarIdentity",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue