Merge remote-tracking branch 'upstream/master' into integratedStepping

This commit is contained in:
amantley 2018-07-09 13:55:59 -07:00
commit 55ae08c071
101 changed files with 2358 additions and 436 deletions

View file

@ -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

View file

@ -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!

View file

@ -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 {

View file

@ -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">

View file

@ -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));
}
}

View file

@ -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;
}
}

View file

@ -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() { }
}

View file

@ -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);
}
}
}
}

View file

@ -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();
}
}
//---------------------------------------------------------------------------

View 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>

View 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>

View file

@ -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>

View 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>

View 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>

View file

@ -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' : ''

View file

@ -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);

View file

@ -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();

View file

@ -17,6 +17,9 @@ EntityTreeHeadlessViewer::EntityTreeHeadlessViewer()
}
EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
if (_simulation) {
_simulation->setEntityTree(nullptr); // Break shared_ptr cycle.
}
}
void EntityTreeHeadlessViewer::init() {

View file

@ -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>();

View file

@ -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 ""

View file

@ -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 ()

View file

@ -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",

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 229 KiB

View 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

View 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
}
}

View 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
}
}

View file

@ -12,7 +12,7 @@ Rectangle {
}
Label {
text: OverlayWindowTestString
text: "OverlayWindowTestString"
anchors.centerIn: parent
}
}

View 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
}
}

View file

@ -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 {

View file

@ -28,6 +28,7 @@ Flickable {
onPluginNameChanged: {
if (page !== null) {
page.pluginName = flick.pluginName;
page.displayConfiguration();
}
}

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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(),

View file

@ -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();

View file

@ -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()));

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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()) {

View file

@ -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);;
}

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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.

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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) {

View file

@ -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:

View file

@ -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];

View file

@ -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

View file

@ -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());
}

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -96,7 +96,7 @@ public:
void softReset();
int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
void sentCheckInPacket();
bool checkInPacketTimeout();
void clearPendingCheckins() { _checkInPacketsSinceLastReply = 0; }
/**jsdoc

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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;
}

View file

@ -57,7 +57,7 @@ public:
ICEServerQuery,
OctreeStats,
UNUSED_PACKET_TYPE_1,
UNUSED_PACKET_TYPE_2,
AvatarIdentityRequest,
AssignmentClientStatus,
NoisyMute,
AvatarIdentity,

View file

@ -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) {

View file

@ -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;

View file

@ -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");
}
}
}

View file

@ -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()));
}

View file

@ -97,6 +97,7 @@ public:
OwnershipState getOwnershipState() const { return _ownershipState; }
void setRegion(uint8_t region);
void saveKinematicState(btScalar timeStep) override;
protected:
void updateSendVelocities();

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}
}
}
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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...

View file

@ -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;
}

View file

@ -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@>

View file

@ -267,7 +267,7 @@ vec3 fetchLightmapMap(vec2 uv) {
<@func discardTransparent(opacity)@>
{
if (<$opacity$> < 1.0) {
if (<$opacity$> < 1e-6) {
discard;
}
}

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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 };

View file

@ -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

View 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);
}
}

View 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

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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();

View 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();
});

View 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

View file

@ -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);
}

View file

@ -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.

View file

@ -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, [], []);
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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,

View file

@ -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