diff --git a/android/app/build.gradle b/android/app/build.gradle
index 97267258e2..7a6f34b58b 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -98,6 +98,13 @@ android {
dependencies {
implementation 'com.google.vr:sdk-audio:1.80.0'
implementation 'com.google.vr:sdk-base:1.80.0'
+
+
+ implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+ implementation 'com.android.support:design:26.1.0'
+ implementation 'com.android.support:appcompat-v7:26.1.0'
+ compile 'com.android.support:recyclerview-v7:26.1.0'
+
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index b3a8c87649..ed9caee58b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -38,6 +38,17 @@
-->
+
+
+
+
+
+
diff --git a/android/app/src/main/assets/hifi_domains.json b/android/app/src/main/assets/hifi_domains.json
new file mode 100644
index 0000000000..a63ef7b6aa
--- /dev/null
+++ b/android/app/src/main/assets/hifi_domains.json
@@ -0,0 +1,24 @@
+{
+ "hifi_domains" : [
+ {
+ "name": "Your last location",
+ "url": "",
+ "thumbnail": ""
+ },
+ {
+ "name": "dev-mobile-master",
+ "url": "hifi://dev-mobile-master",
+ "thumbnail": ""
+ },
+ {
+ "name": "dev-mobile",
+ "url": "hifi://dev-mobile",
+ "thumbnail": ""
+ },
+ {
+ "name": "dev-welcome",
+ "url": "hifi://dev-welcome",
+ "thumbnail": ""
+ }
+ ]
+}
\ No newline at end of file
diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp
index 13daf4c471..facf6bd4bd 100644
--- a/android/app/src/main/cpp/native.cpp
+++ b/android/app/src/main/cpp/native.cpp
@@ -17,6 +17,12 @@
#include
#include
+#include
+
+#include
+#include "AndroidHelper.h"
+
+QAndroidJniObject __activity;
void tempMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
@@ -138,9 +144,26 @@ extern "C" {
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) {
qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId();
g_assetManager = AAssetManager_fromJava(env, asset_mgr);
+ __activity = QAndroidJniObject(instance);
auto oldMessageHandler = qInstallMessageHandler(tempMessageHandler);
unpackAndroidAssets();
qInstallMessageHandler(oldMessageHandler);
+
+ QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [](const QString& a) {
+ QAndroidJniObject string = QAndroidJniObject::fromString(a);
+ __activity.callMethod("openAndroidActivity", "(Ljava/lang/String;)V", string.object());
+ });
+}
+
+JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnDestroy(JNIEnv* env, jobject obj) {
+ QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested,
+ nullptr, nullptr);
+
+}
+
+JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUrl(JNIEnv* env, jobject obj, jstring url) {
+ QAndroidJniObject jniUrl("java/lang/String", "(Ljava/lang/String;)V", url);
+ DependencyManager::get()->handleLookupString(jniUrl.toString());
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
@@ -155,6 +178,30 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnExit
qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId();
}
+JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGoBackFromAndroidActivity(JNIEnv *env, jobject instance) {
+ AndroidHelper::instance().goBackFromAndroidActivity();
}
+// HifiUtils
+JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_getCurrentAddress(JNIEnv *env, jobject instance) {
+ QSharedPointer addressManager = DependencyManager::get();
+ if (!addressManager) {
+ return env->NewString(nullptr, 0);
+ }
+ QString str;
+ if (!addressManager->getPlaceName().isEmpty()) {
+ str = addressManager->getPlaceName();
+ } else if (!addressManager->getHost().isEmpty()) {
+ str = addressManager->getHost();
+ }
+
+ QRegExp pathRegEx("(\\/[^\\/]+)");
+ if (!addressManager->currentPath().isEmpty() && addressManager->currentPath().contains(pathRegEx) && pathRegEx.matchedLength() > 0) {
+ str += pathRegEx.cap(0);
+ }
+
+ return env->NewStringUTF(str.toLatin1().data());
+}
+
+}
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/GotoActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/GotoActivity.java
new file mode 100644
index 0000000000..995e64c2a5
--- /dev/null
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/GotoActivity.java
@@ -0,0 +1,94 @@
+package io.highfidelity.hifiinterface;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.AppCompatButton;
+import android.support.v7.widget.Toolbar;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class GotoActivity extends AppCompatActivity {
+
+ public static final String PARAM_DOMAIN_URL = "domain_url";
+
+ private EditText mUrlEditText;
+ private AppCompatButton mGoBtn;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_goto);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionbar = getSupportActionBar();
+ actionbar.setDisplayHomeAsUpEnabled(true);
+
+ mUrlEditText = (EditText) findViewById(R.id.url_text);
+ mUrlEditText.setOnKeyListener(new View.OnKeyListener() {
+ @Override
+ public boolean onKey(View view, int i, KeyEvent keyEvent) {
+ if (i == KeyEvent.KEYCODE_ENTER) {
+ actionGo();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ mUrlEditText.setText(HifiUtils.getInstance().getCurrentAddress());
+
+ mGoBtn = (AppCompatButton) findViewById(R.id.go_btn);
+
+ mGoBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ actionGo();
+ }
+ });
+ }
+
+ private void actionGo() {
+ String urlString = mUrlEditText.getText().toString();
+ if (!urlString.trim().isEmpty()) {
+ URI uri;
+ try {
+ uri = new URI(urlString);
+ } catch (URISyntaxException e) {
+ return;
+ }
+ if (uri.getScheme() == null || uri.getScheme().isEmpty()) {
+ urlString = "hifi://" + urlString;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(GotoActivity.PARAM_DOMAIN_URL, urlString);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+ switch (id) {
+ case android.R.id.home:
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java b/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java
new file mode 100644
index 0000000000..15d716548f
--- /dev/null
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java
@@ -0,0 +1,23 @@
+package io.highfidelity.hifiinterface;
+
+/**
+ * Created by Gabriel Calero & Cristian Duarte on 4/13/18.
+ */
+
+public class HifiUtils {
+
+ private static HifiUtils instance;
+
+ private HifiUtils() {
+ }
+
+ public static HifiUtils getInstance() {
+ if (instance == null) {
+ instance = new HifiUtils();
+ }
+ return instance;
+ }
+
+ public native String getCurrentAddress();
+
+}
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/HomeActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/HomeActivity.java
new file mode 100644
index 0000000000..611c8f50cc
--- /dev/null
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/HomeActivity.java
@@ -0,0 +1,220 @@
+package io.highfidelity.hifiinterface;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.SearchView;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+import android.widget.TextView;
+
+import io.highfidelity.hifiinterface.QtPreloader.QtPreloader;
+import io.highfidelity.hifiinterface.view.DomainAdapter;
+
+public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
+
+ /**
+ * Set this intent extra param to NOT start a new InterfaceActivity after a domain is selected"
+ */
+ public static final String PARAM_NOT_START_INTERFACE_ACTIVITY = "not_start_interface_activity";
+
+ public static final int ENTER_DOMAIN_URL = 1;
+
+ private DomainAdapter domainAdapter;
+ private DrawerLayout mDrawerLayout;
+ private ProgressDialog mDialog;
+ private NavigationView mNavigationView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_home);
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionbar = getSupportActionBar();
+ actionbar.setDisplayHomeAsUpEnabled(true);
+ actionbar.setHomeAsUpIndicator(R.drawable.ic_menu);
+
+ mDrawerLayout = findViewById(R.id.drawer_layout);
+
+ mNavigationView = (NavigationView) findViewById(R.id.nav_view);
+ mNavigationView.setNavigationItemSelectedListener(this);
+
+ TabHost tabs = (TabHost)findViewById(R.id.tabhost);
+ tabs.setup();
+
+ TabHost.TabSpec spec=tabs.newTabSpec("featured");
+ spec.setContent(R.id.featured);
+ spec.setIndicator(getString(R.string.featured));
+ tabs.addTab(spec);
+
+ spec = tabs.newTabSpec("popular");
+ spec.setContent(R.id.popular);
+ spec.setIndicator(getString(R.string.popular));
+ tabs.addTab(spec);
+
+ spec = tabs.newTabSpec("bookmarks");
+ spec.setContent(R.id.bookmarks);
+ spec.setIndicator(getString(R.string.bookmarks));
+ tabs.addTab(spec);
+
+ tabs.setCurrentTab(0);
+
+ TabWidget tabwidget=tabs.getTabWidget();
+ for(int i=0; i libraryList = new ArrayList<>();
+ String localPrefix = m_context.getApplicationInfo().dataDir + "/";
+ String pluginsPrefix = localPrefix + "qt-reserved-files/";
+ cleanOldCacheIfNecessary(localPrefix, pluginsPrefix);
+ extractBundledPluginsAndImports(pluginsPrefix);
+
+ for (String lib : m_qtLibs) {
+ libraryList.add(localPrefix + "lib/lib" + lib + ".so");
+ }
+
+ if (m_contextInfo.metaData.containsKey("android.app.load_local_libs")) {
+ String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":");
+ for (String lib : extraLibs) {
+ if (lib.length() > 0) {
+ if (lib.startsWith("lib/")) {
+ libraryList.add(localPrefix + lib);
+ } else {
+ libraryList.add(pluginsPrefix + lib);
+ }
+ }
+ }
+ }
+
+ Bundle loaderParams = new Bundle();
+ loaderParams.putString(DEX_PATH_KEY, new String());
+
+ loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
+
+ loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES
+ + "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml"
+ + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports"
+ + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins");
+
+
+ // add all bundled Qt libs to loader params
+ ArrayList libs = new ArrayList<>();
+
+ String libName = m_contextInfo.metaData.getString("android.app.lib_name");
+ loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
+ loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs);
+
+ // load and start QtLoader class
+ DexClassLoader classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files
+ m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written.
+ loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists)
+ m_context.getClassLoader()); // parent loader
+
+ Class> loaderClass = classLoader.loadClass(loaderClassName()); // load QtLoader class
+ Object qtLoader = loaderClass.newInstance(); // create an instance
+ Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication",
+ contextClassName(),
+ ClassLoader.class,
+ Bundle.class);
+ prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams);
+
+ // now load the application library so it's accessible from this class loader
+ if (libName != null) {
+ System.loadLibrary(libName);
+ }
+ } catch (Exception e) {
+ Log.e(QtApplication.QtTAG, "Error pre-loading HiFi Qt app", e);
+ }
+ }
+
+ protected String loaderClassName() {
+ return "org.qtproject.qt5.android.QtActivityDelegate";
+ }
+
+ protected Class> contextClassName() {
+ return android.app.Activity.class;
+ }
+
+
+ private void deleteRecursively(File directory) {
+ File[] files = directory.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteRecursively(file);
+ } else {
+ file.delete();
+ }
+ }
+
+ directory.delete();
+ }
+ }
+
+ private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) {
+ File newCache = new File(localPrefix);
+ if (!newCache.exists()) {
+ {
+ File oldPluginsCache = new File(oldLocalPrefix + "plugins/");
+ if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) {
+ deleteRecursively(oldPluginsCache);
+ }
+ }
+
+ {
+ File oldImportsCache = new File(oldLocalPrefix + "imports/");
+ if (oldImportsCache.exists() && oldImportsCache.isDirectory()) {
+ deleteRecursively(oldImportsCache);
+ }
+ }
+
+ {
+ File oldQmlCache = new File(oldLocalPrefix + "qml/");
+ if (oldQmlCache.exists() && oldQmlCache.isDirectory()) {
+ deleteRecursively(oldQmlCache);
+ }
+ }
+ }
+ }
+
+ static private void copyFile(InputStream inputStream, OutputStream outputStream)
+ throws IOException {
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ int count;
+ while ((count = inputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, count);
+ }
+ }
+
+ private void copyAsset(String source, String destination)
+ throws IOException {
+ // Already exists, we don't have to do anything
+ File destinationFile = new File(destination);
+ if (destinationFile.exists()) {
+ return;
+ }
+
+ File parentDirectory = destinationFile.getParentFile();
+ if (!parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+
+ destinationFile.createNewFile();
+
+ AssetManager assetsManager = m_context.getAssets();
+ InputStream inputStream = assetsManager.open(source);
+ OutputStream outputStream = new FileOutputStream(destinationFile);
+ copyFile(inputStream, outputStream);
+
+ inputStream.close();
+ outputStream.close();
+ }
+
+ private static void createBundledBinary(String source, String destination)
+ throws IOException {
+ // Already exists, we don't have to do anything
+ File destinationFile = new File(destination);
+ if (destinationFile.exists()) {
+ return;
+ }
+
+ File parentDirectory = destinationFile.getParentFile();
+ if (!parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+
+ destinationFile.createNewFile();
+
+ InputStream inputStream = new FileInputStream(source);
+ OutputStream outputStream = new FileOutputStream(destinationFile);
+ copyFile(inputStream, outputStream);
+
+ inputStream.close();
+ outputStream.close();
+ }
+
+ private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) {
+ File versionFile = new File(pluginsPrefix + "cache.version");
+
+ long cacheVersion = 0;
+ if (versionFile.exists() && versionFile.canRead()) {
+ try {
+ DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile));
+ cacheVersion = inputStream.readLong();
+ inputStream.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (cacheVersion != packageVersion) {
+ deleteRecursively(new File(pluginsPrefix));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void extractBundledPluginsAndImports(String pluginsPrefix) throws IOException {
+ String libsDir = m_context.getApplicationInfo().nativeLibraryDir + "/";
+ long packageVersion = -1;
+ try {
+ PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.getPackageName(), 0);
+ packageVersion = packageInfo.lastUpdateTime;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) {
+ return;
+ }
+
+ {
+ File versionFile = new File(pluginsPrefix + "cache.version");
+
+ File parentDirectory = versionFile.getParentFile();
+ if (!parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+
+ versionFile.createNewFile();
+
+ DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile));
+ outputStream.writeLong(packageVersion);
+ outputStream.close();
+ }
+
+ {
+ String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
+ if (m_contextInfo.metaData.containsKey(key)) {
+ String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
+
+ for (String bundledImportBinary : list) {
+ String[] split = bundledImportBinary.split(":");
+ String sourceFileName = libsDir + split[0];
+ String destinationFileName = pluginsPrefix + split[1];
+ createBundledBinary(sourceFileName, destinationFileName);
+ }
+ }
+ }
+
+ {
+ String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY;
+ if (m_contextInfo.metaData.containsKey(key)) {
+ String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
+
+ for (String fileName : list) {
+ String[] split = fileName.split(":");
+ String sourceFileName = split[0];
+ String destinationFileName = pluginsPrefix + split[1];
+ copyAsset(sourceFileName, destinationFileName);
+ }
+ }
+
+ }
+ }
+}
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java
new file mode 100644
index 0000000000..1129ec586f
--- /dev/null
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java
@@ -0,0 +1,131 @@
+package io.highfidelity.hifiinterface.view;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import io.highfidelity.hifiinterface.R;
+
+/**
+ * Created by Gabriel Calero & Cristian Duarte on 3/20/18.
+ */
+public class DomainAdapter extends RecyclerView.Adapter {
+
+ private static final java.lang.String DOMAINS_FILE = "hifi_domains.json";
+ private static final String TAG = "HiFi Interface";
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private ItemClickListener mClickListener;
+
+ public class Domain {
+ public String name;
+ public String url;
+ public String thumbnail;
+ Domain(String name, String url, String thumbnail) {
+ this.name = name;
+ this.thumbnail = thumbnail;
+ this.url = url;
+ }
+ }
+
+ // references to our domains
+ private Domain[] mDomains = {};
+
+ public DomainAdapter(Context c) {
+ mContext = c;
+ this.mInflater = LayoutInflater.from(mContext);
+ loadDomains();
+ }
+
+ private void loadDomains() {
+ try {
+ JSONObject json = new JSONObject(loadJSONFromAsset());
+ JSONArray hifiDomains = json.getJSONArray("hifi_domains");
+ List domains = new ArrayList<>();
+ for (int i = 0; i < hifiDomains.length(); i++) {
+ JSONObject jDomain = hifiDomains.getJSONObject(i);
+
+ String domainName = jDomain.getString("name");
+ String domainUrl = jDomain.getString("url");
+ String domainThumb = jDomain.getString("thumbnail");
+
+ domains.add(new Domain(domainName, domainUrl, domainThumb));
+ }
+ mDomains = domains.toArray(mDomains);
+ } catch (IOException e) {
+ Log.e(TAG, "Error loading domains from local file", e);
+ } catch (JSONException e) {
+ Log.e(TAG, "Error loading domains from local file", e);
+ }
+ }
+
+ public String loadJSONFromAsset() throws IOException {
+ String json = null;
+ InputStream is = mContext.getAssets().open(DOMAINS_FILE);
+ int size = is.available();
+ byte[] buffer = new byte[size];
+ is.read(buffer);
+ is.close();
+ json = new String(buffer, "UTF-8");
+ return json;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = mInflater.inflate(R.layout.domain_view, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ // TODO
+ //holder.thumbnail.setImageResource(mDomains[position].thumbnail);
+ holder.mDomainName.setText(mDomains[position].name);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDomains.length;
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+ TextView mDomainName;
+ ImageView mThumbnail;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+ mThumbnail = (ImageView) itemView.findViewById(R.id.domainThumbnail);
+ mDomainName = (TextView) itemView.findViewById(R.id.domainName);
+ itemView.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ int position = getAdapterPosition();
+ if (mClickListener != null) mClickListener.onItemClick(view, position, mDomains[position]);
+ }
+ }
+
+ // allows clicks events to be caught
+ public void setClickListener(ItemClickListener itemClickListener) {
+ this.mClickListener = itemClickListener;
+ }
+ // parent activity will implement this method to respond to click events
+ public interface ItemClickListener {
+ void onItemClick(View view, int position, Domain domain);
+ }
+}
diff --git a/android/app/src/main/res/drawable/domain_bg.xml b/android/app/src/main/res/drawable/domain_bg.xml
new file mode 100644
index 0000000000..d30d6413e1
--- /dev/null
+++ b/android/app/src/main/res/drawable/domain_bg.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ic_bookmark.xml b/android/app/src/main/res/drawable/ic_bookmark.xml
new file mode 100644
index 0000000000..ddf03e340b
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_bookmark.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_menu.xml b/android/app/src/main/res/drawable/ic_menu.xml
new file mode 100644
index 0000000000..cf37e2a393
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_menu.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_person.xml b/android/app/src/main/res/drawable/ic_person.xml
new file mode 100644
index 0000000000..cf57059c77
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_person.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_share.xml b/android/app/src/main/res/drawable/ic_share.xml
new file mode 100644
index 0000000000..91b01694da
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_share.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/search_bg.xml b/android/app/src/main/res/drawable/search_bg.xml
new file mode 100644
index 0000000000..fd1a9ea42e
--- /dev/null
+++ b/android/app/src/main/res/drawable/search_bg.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/font/raleway.ttf b/android/app/src/main/res/font/raleway.ttf
new file mode 100644
index 0000000000..e570a2d5c3
Binary files /dev/null and b/android/app/src/main/res/font/raleway.ttf differ
diff --git a/android/app/src/main/res/font/raleway_bold.xml b/android/app/src/main/res/font/raleway_bold.xml
new file mode 100644
index 0000000000..136472c0b3
--- /dev/null
+++ b/android/app/src/main/res/font/raleway_bold.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/android/app/src/main/res/font/raleway_light_italic.xml b/android/app/src/main/res/font/raleway_light_italic.xml
new file mode 100644
index 0000000000..4acab05089
--- /dev/null
+++ b/android/app/src/main/res/font/raleway_light_italic.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/android/app/src/main/res/font/raleway_medium.xml b/android/app/src/main/res/font/raleway_medium.xml
new file mode 100644
index 0000000000..3894c95b10
--- /dev/null
+++ b/android/app/src/main/res/font/raleway_medium.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/android/app/src/main/res/font/raleway_semibold.xml b/android/app/src/main/res/font/raleway_semibold.xml
new file mode 100644
index 0000000000..39cde5a30b
--- /dev/null
+++ b/android/app/src/main/res/font/raleway_semibold.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/android/app/src/main/res/layout/activity_goto.xml b/android/app/src/main/res/layout/activity_goto.xml
new file mode 100644
index 0000000000..06e1383da5
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_goto.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/activity_home.xml b/android/app/src/main/res/layout/activity_home.xml
new file mode 100644
index 0000000000..144ca84a0f
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_home.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/content_home.xml b/android/app/src/main/res/layout/content_home.xml
new file mode 100644
index 0000000000..f25d9d8f7b
--- /dev/null
+++ b/android/app/src/main/res/layout/content_home.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/domain_view.xml b/android/app/src/main/res/layout/domain_view.xml
new file mode 100644
index 0000000000..d0ed2d3a44
--- /dev/null
+++ b/android/app/src/main/res/layout/domain_view.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/menu/menu_home.xml b/android/app/src/main/res/menu/menu_home.xml
new file mode 100644
index 0000000000..c27233a6c3
--- /dev/null
+++ b/android/app/src/main/res/menu/menu_home.xml
@@ -0,0 +1,15 @@
+
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
index 344907f039..0325881f1b 100644
--- a/android/app/src/main/res/values/colors.xml
+++ b/android/app/src/main/res/values/colors.xml
@@ -1,4 +1,13 @@
#ffffff
+ #272727
+ #000000
+ #54D7FD
+ #1EB5EC
+ #333333
+ #4F4F4F
+ #33999999
+ #212121
+ #9e9e9e
diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml
index a9ec657aa9..440adcf6b9 100644
--- a/android/app/src/main/res/values/dimens.xml
+++ b/android/app/src/main/res/values/dimens.xml
@@ -9,4 +9,6 @@
14dp
12dp
+ 12dp
+ 8dp
\ No newline at end of file
diff --git a/android/app/src/main/res/values/font_certs.xml b/android/app/src/main/res/values/font_certs.xml
new file mode 100644
index 0000000000..d2226ac01c
--- /dev/null
+++ b/android/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,17 @@
+
+
+
+ - @array/com_google_android_gms_fonts_certs_dev
+ - @array/com_google_android_gms_fonts_certs_prod
+
+
+ -
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+ -
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/android/app/src/main/res/values/preloaded_fonts.xml b/android/app/src/main/res/values/preloaded_fonts.xml
new file mode 100644
index 0000000000..6f9d0e424e
--- /dev/null
+++ b/android/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,9 @@
+
+
+
+ - @font/raleway_bold
+ - @font/raleway_light_italic
+ - @font/raleway_medium
+ - @font/raleway_semibold
+
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index b8080fae0f..6ce0670dd8 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,8 +1,17 @@
Interface
+ Home
+ Go To
Open in browser
Share link
Shared a link
Share link
+ FEATURED
+ POPULAR
+ BOOKMARKS
+ Settings
+ Go To
+ Type a domain url
+ Go
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
index 23fe67f029..55c9b2af11 100644
--- a/android/app/src/main/res/values/styles.xml
+++ b/android/app/src/main/res/values/styles.xml
@@ -1,14 +1,41 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -21,5 +48,11 @@
- @dimen/text_size_subtitle_material_toolbar
+
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 1df901dd98..42924a8487 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -548,16 +548,21 @@ void Agent::setIsAvatar(bool isAvatar) {
if (_isAvatar && !_avatarIdentityTimer) {
// set up the avatar timers
_avatarIdentityTimer = new QTimer(this);
+ _avatarQueryTimer = new QTimer(this);
// connect our slot
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
+ connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
+
+ static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
+ static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
// start the timers
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
+ _avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
// tell the avatarAudioTimer to start ticking
QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
-
}
if (!_isAvatar) {
@@ -567,6 +572,10 @@ void Agent::setIsAvatar(bool isAvatar) {
delete _avatarIdentityTimer;
_avatarIdentityTimer = nullptr;
+ _avatarQueryTimer->stop();
+ delete _avatarQueryTimer;
+ _avatarQueryTimer = nullptr;
+
// The avatar mixer never times out a connection (e.g., based on identity or data packets)
// but rather keeps avatars in its list as long as "connected". As a result, clients timeout
// when we stop sending identity, but then get woken up again by the mixer itself, which sends
@@ -585,6 +594,7 @@ void Agent::setIsAvatar(bool isAvatar) {
nodeList->sendPacket(std::move(packet), *node);
});
}
+
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
}
}
@@ -597,6 +607,31 @@ void Agent::sendAvatarIdentityPacket() {
}
}
+void Agent::queryAvatars() {
+ auto scriptedAvatar = DependencyManager::get();
+
+ ViewFrustum view;
+ view.setPosition(scriptedAvatar->getWorldPosition());
+ view.setOrientation(scriptedAvatar->getHeadOrientation());
+ view.calculate();
+ ConicalViewFrustum conicalView { view };
+
+ auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
+ auto destinationBuffer = reinterpret_cast(avatarPacket->getPayload());
+ auto bufferStart = destinationBuffer;
+
+ uint8_t numFrustums = 1;
+ memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
+ destinationBuffer += sizeof(numFrustums);
+
+ destinationBuffer += conicalView.serialize(destinationBuffer);
+
+ avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
+
+ DependencyManager::get()->broadcastToNodes(std::move(avatarPacket),
+ { NodeType::AvatarMixer });
+}
+
void Agent::processAgentAvatar() {
if (!_scriptEngine->isFinished() && _isAvatar) {
auto scriptedAvatar = DependencyManager::get();
diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h
index 1229f06276..0cdc9e0029 100644
--- a/assignment-client/src/Agent.h
+++ b/assignment-client/src/Agent.h
@@ -97,6 +97,7 @@ private:
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
void sendAvatarIdentityPacket();
+ void queryAvatars();
QString _scriptContents;
QTimer* _scriptRequestTimeout { nullptr };
@@ -106,6 +107,7 @@ private:
int _numAvatarSoundSentBytes = 0;
bool _isAvatar = false;
QTimer* _avatarIdentityTimer = nullptr;
+ QTimer* _avatarQueryTimer = nullptr;
QHash _outgoingScriptAudioSequenceNumbers;
AudioGate _audioGate;
diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index efced972a0..41e42aa0a1 100644
--- a/assignment-client/src/AssignmentClient.cpp
+++ b/assignment-client/src/AssignmentClient.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AssignmentClient.h"
+
#include
#include
@@ -32,16 +34,14 @@
#include
#include
#include
-
-#include "AssignmentFactory.h"
-#include "AssignmentDynamicFactory.h"
-
-#include "AssignmentClient.h"
-#include "AssignmentClientLogging.h"
-#include "avatars/ScriptableAvatar.h"
#include
#include
+#include "AssignmentClientLogging.h"
+#include "AssignmentDynamicFactory.h"
+#include "AssignmentFactory.h"
+#include "avatars/ScriptableAvatar.h"
+
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp
index 1868ccfafe..2847d4ebf1 100644
--- a/assignment-client/src/AssignmentClientMonitor.cpp
+++ b/assignment-client/src/AssignmentClientMonitor.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AssignmentClientMonitor.h"
+
#include
#include
@@ -19,7 +21,6 @@
#include
#include
-#include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h"
#include "AssignmentClientChildData.h"
#include "SharedUtil.h"
diff --git a/assignment-client/src/AssignmentDynamic.cpp b/assignment-client/src/AssignmentDynamic.cpp
index 7adbd55c39..447097ac74 100644
--- a/assignment-client/src/AssignmentDynamic.cpp
+++ b/assignment-client/src/AssignmentDynamic.cpp
@@ -9,10 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#include "EntitySimulation.h"
-
#include "AssignmentDynamic.h"
+#include "EntitySimulation.h"
+
AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
EntityDynamicInterface(type, id),
_data(QByteArray()),
diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp
index 38eb72649f..405039d833 100644
--- a/assignment-client/src/AssignmentFactory.cpp
+++ b/assignment-client/src/AssignmentFactory.cpp
@@ -9,11 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AssignmentFactory.h"
+
#include
#include "Agent.h"
#include "assets/AssetServer.h"
-#include "AssignmentFactory.h"
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
#include "entities/EntityServer.h"
diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h
index f83545c25c..96a220d64d 100644
--- a/assignment-client/src/assets/AssetServer.h
+++ b/assignment-client/src/assets/AssetServer.h
@@ -36,10 +36,11 @@ enum class BakedAssetType : int {
Undefined
};
-// ATTENTION! If you change the current version for an asset type, you will also
-// need to update the function currentBakeVersionForAssetType() inside of AssetServer.cpp.
+// ATTENTION! Do not remove baking versions, and do not reorder them. If you add
+// a new value, it will immediately become the "current" version.
enum class ModelBakeVersion : BakeVersion {
Initial = INITIAL_BAKE_VERSION,
+ MetaTextureJson,
COUNT
};
@@ -47,6 +48,7 @@ enum class ModelBakeVersion : BakeVersion {
// ATTENTION! See above.
enum class TextureBakeVersion : BakeVersion {
Initial = INITIAL_BAKE_VERSION,
+ MetaTextureJson,
COUNT
};
diff --git a/assignment-client/src/audio/AudioMixerSlavePool.cpp b/assignment-client/src/audio/AudioMixerSlavePool.cpp
index e28c96e259..dfe7ef56aa 100644
--- a/assignment-client/src/audio/AudioMixerSlavePool.cpp
+++ b/assignment-client/src/audio/AudioMixerSlavePool.cpp
@@ -9,11 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AudioMixerSlavePool.h"
+
#include
#include
-#include "AudioMixerSlavePool.h"
-
void AudioMixerSlaveThread::run() {
while (true) {
wait();
diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp
index 42495b4dd0..22ea8c0617 100644
--- a/assignment-client/src/audio/AvatarAudioStream.cpp
+++ b/assignment-client/src/audio/AvatarAudioStream.cpp
@@ -9,10 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AvatarAudioStream.h"
+
#include
#include "AudioLogging.h"
-#include "AvatarAudioStream.h"
AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) :
PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, numStaticJitterFrames) {}
diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index 29340f6474..9b5c4d4f30 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AvatarMixer.h"
+
#include
#include
#include
@@ -31,8 +33,6 @@
#include
#include
-#include "AvatarMixer.h"
-
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
@@ -47,7 +47,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
auto& packetReceiver = DependencyManager::get()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket");
packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting");
- packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket");
+ packetReceiver.registerListener(PacketType::AvatarQuery, this, "handleAvatarQueryPacket");
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
@@ -517,15 +517,13 @@ void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer mess
}
-void AvatarMixer::handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode) {
+void AvatarMixer::handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode) {
auto start = usecTimestampNow();
getOrCreateClientData(senderNode);
- if (senderNode->getLinkedData()) {
- AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData());
- if (nodeData != nullptr) {
- nodeData->readViewFrustumPacket(message->getMessage());
- }
+ AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData());
+ if (nodeData) {
+ nodeData->readViewFrustumPacket(message->getMessage());
}
auto end = usecTimestampNow();
@@ -685,7 +683,7 @@ void AvatarMixer::sendStatsPacket() {
incomingPacketStats["handleNodeIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleNodeIgnoreRequestPacketElapsedTime);
incomingPacketStats["handleRadiusIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRadiusIgnoreRequestPacketElapsedTime);
incomingPacketStats["handleRequestsDomainListDataPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRequestsDomainListDataPacketElapsedTime);
- incomingPacketStats["handleViewFrustumPacket"] = TIGHT_LOOP_STAT_UINT64(_handleViewFrustumPacketElapsedTime);
+ incomingPacketStats["handleAvatarQueryPacket"] = TIGHT_LOOP_STAT_UINT64(_handleViewFrustumPacketElapsedTime);
singleCoreTasks["incoming_packets"] = incomingPacketStats;
singleCoreTasks["sendStats"] = (float)_sendStatsElapsedTime;
diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h
index 1fbfd7338b..9ef5903eec 100644
--- a/assignment-client/src/avatars/AvatarMixer.h
+++ b/assignment-client/src/avatars/AvatarMixer.h
@@ -46,7 +46,7 @@ public slots:
private slots:
void queueIncomingPacket(QSharedPointer message, SharedNodePointer node);
void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode);
- void handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode);
+ void handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode);
void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode);
void handleKillAvatarPacket(QSharedPointer message, SharedNodePointer senderNode);
void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode);
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 268aba62d6..e185fe9167 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -9,18 +9,16 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AvatarMixerClientData.h"
+
#include
#include
#include
-#include "AvatarMixerClientData.h"
-
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) :
NodeData(nodeID)
{
- _currentViewFrustum.invalidate();
-
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
_avatar->setID(nodeID);
}
@@ -129,11 +127,27 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self,
}
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
- _currentViewFrustum.fromByteArray(message);
+ _currentViewFrustums.clear();
+
+ auto sourceBuffer = reinterpret_cast(message.constData());
+
+ uint8_t numFrustums = 0;
+ memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
+ sourceBuffer += sizeof(numFrustums);
+
+ for (uint8_t i = 0; i < numFrustums; ++i) {
+ ConicalViewFrustum frustum;
+ sourceBuffer += frustum.deserialize(sourceBuffer);
+
+ _currentViewFrustums.push_back(frustum);
+ }
}
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
- return _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox);
+ return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
+ [&](const ConicalViewFrustum& viewFrustum) {
+ return viewFrustum.intersects(otherAvatarBox);
+ });
}
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 6963f4df0d..e038e81505 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -28,7 +28,7 @@
#include
#include
#include
-#include
+#include
const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
@@ -110,7 +110,7 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
- ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
+ const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
@@ -150,7 +150,7 @@ private:
SimpleMovingAverage _avgOtherAvatarDataRate;
std::unordered_set _radiusIgnoredOthers;
- ViewFrustum _currentViewFrustum;
+ ConicalViewFrustums _currentViewFrustums;
int _recentOtherAvatarsInView { 0 };
int _recentOtherAvatarsOutOfView { 0 };
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 6f19b73cc5..984884adb2 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AvatarMixerSlave.h"
+
#include
#include
@@ -28,10 +30,8 @@
#include
#include
-
#include "AvatarMixer.h"
#include "AvatarMixerClientData.h"
-#include "AvatarMixerSlave.h"
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
_begin = begin;
@@ -222,8 +222,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
};
// prepare to sort
- ViewFrustum cameraView = nodeData->getViewFrustum();
- PrioritySortUtil::PriorityQueue sortedAvatars(cameraView,
+ const auto& cameraViews = nodeData->getViewFrustums();
+ PrioritySortUtil::PriorityQueue sortedAvatars(cameraViews,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h
index bdddd5ceab..7be119c4b2 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.h
+++ b/assignment-client/src/avatars/AvatarMixerSlave.h
@@ -12,6 +12,8 @@
#ifndef hifi_AvatarMixerSlave_h
#define hifi_AvatarMixerSlave_h
+#include
+
class AvatarMixerClientData;
class AvatarMixerSlaveStats {
diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp
index 25b88686b7..962bba21d2 100644
--- a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp
@@ -9,11 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AvatarMixerSlavePool.h"
+
#include
#include
-#include "AvatarMixerSlavePool.h"
-
void AvatarMixerSlaveThread::run() {
while (true) {
wait();
diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp
index 1f3f770867..e7210db83a 100644
--- a/assignment-client/src/avatars/ScriptableAvatar.cpp
+++ b/assignment-client/src/avatars/ScriptableAvatar.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "ScriptableAvatar.h"
+
#include
#include
#include
@@ -16,7 +18,6 @@
#include
#include
#include
-#include "ScriptableAvatar.h"
QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) {
diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp
deleted file mode 100644
index 999a05f2e2..0000000000
--- a/assignment-client/src/entities/EntityPriorityQueue.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// EntityPriorityQueue.cpp
-// assignment-client/src/entities
-//
-// Created by Andrew Meadows 2017.08.08
-// Copyright 2017 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include "EntityPriorityQueue.h"
-
-const float PrioritizedEntity::DO_NOT_SEND = -1.0e-6f;
-const float PrioritizedEntity::FORCE_REMOVE = -1.0e-5f;
-const float PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY = 1.0f;
-
-void ConicalView::set(const ViewFrustum& viewFrustum) {
- // The ConicalView has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part.
- // Why? Because approximate intersection tests are much faster to compute for a cone than for a frustum.
- _position = viewFrustum.getPosition();
- _direction = viewFrustum.getDirection();
-
- // We cache the sin and cos of the half angle of the cone that bounds the frustum.
- // (the math here is left as an exercise for the reader)
- float A = viewFrustum.getAspectRatio();
- float t = tanf(0.5f * viewFrustum.getFieldOfView());
- _cosAngle = 1.0f / sqrtf(1.0f + (A * A + 1.0f) * (t * t));
- _sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
-
- _radius = viewFrustum.getCenterRadius();
-}
-
-float ConicalView::computePriority(const AACube& cube) const {
- glm::vec3 p = cube.calcCenter() - _position; // position of bounding sphere in view-frame
- float d = glm::length(p); // distance to center of bounding sphere
- float r = 0.5f * cube.getScale(); // radius of bounding sphere
- if (d < _radius + r) {
- return r;
- }
- // We check the angle between the center of the cube and the _direction of the view.
- // If it is less than the sum of the half-angle from center of cone to outer edge plus
- // the half apparent angle of the bounding sphere then it is in view.
- //
- // The math here is left as an exercise for the reader with the following hints:
- // (1) We actually check the dot product of the cube's local position rather than the angle and
- // (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
- if (glm::dot(p, _direction) > sqrtf(d * d - r * r) * _cosAngle - r * _sinAngle) {
- const float AVOID_DIVIDE_BY_ZERO = 0.001f;
- return r / (d + AVOID_DIVIDE_BY_ZERO);
- }
- return PrioritizedEntity::DO_NOT_SEND;
-}
diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h
deleted file mode 100644
index e308d9b549..0000000000
--- a/assignment-client/src/entities/EntityPriorityQueue.h
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// EntityPriorityQueue.h
-// assignment-client/src/entities
-//
-// Created by Andrew Meadows 2017.08.08
-// Copyright 2017 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_EntityPriorityQueue_h
-#define hifi_EntityPriorityQueue_h
-
-#include
-
-#include
-#include
-
-const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
-const float DEFAULT_VIEW_RADIUS = 10.0f;
-
-// ConicalView is an approximation of a ViewFrustum for fast calculation of sort priority.
-class ConicalView {
-public:
- ConicalView() {}
- ConicalView(const ViewFrustum& viewFrustum) { set(viewFrustum); }
- void set(const ViewFrustum& viewFrustum);
- float computePriority(const AACube& cube) const;
-private:
- glm::vec3 _position { 0.0f, 0.0f, 0.0f };
- glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
- float _sinAngle { SQRT_TWO_OVER_TWO };
- float _cosAngle { SQRT_TWO_OVER_TWO };
- float _radius { DEFAULT_VIEW_RADIUS };
-};
-
-// PrioritizedEntity is a placeholder in a sorted queue.
-class PrioritizedEntity {
-public:
- static const float DO_NOT_SEND;
- static const float FORCE_REMOVE;
- static const float WHEN_IN_DOUBT_PRIORITY;
-
- PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
- EntityItemPointer getEntity() const { return _weakEntity.lock(); }
- EntityItem* getRawEntityPointer() const { return _rawEntityPointer; }
- float getPriority() const { return _priority; }
- bool shouldForceRemove() const { return _forceRemove; }
-
- class Compare {
- public:
- bool operator() (const PrioritizedEntity& A, const PrioritizedEntity& B) { return A._priority < B._priority; }
- };
- friend class Compare;
-
-private:
- EntityItemWeakPointer _weakEntity;
- EntityItem* _rawEntityPointer;
- float _priority;
- bool _forceRemove;
-};
-
-using EntityPriorityQueue = std::priority_queue< PrioritizedEntity, std::vector, PrioritizedEntity::Compare >;
-
-#endif // hifi_EntityPriorityQueue_h
diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp
index 70fad03d67..c108dad6cf 100644
--- a/assignment-client/src/entities/EntityServer.cpp
+++ b/assignment-client/src/entities/EntityServer.cpp
@@ -9,21 +9,23 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "EntityServer.h"
+
#include
#include
+#include
+#include
+
#include
#include
#include
#include
#include
#include
-#include
-#include
#include
#include "AssignmentParentFinder.h"
#include "EntityNodeData.h"
-#include "EntityServer.h"
#include "EntityServerConsts.h"
#include "EntityTreeSendThread.h"
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index 4aa52922c0..f008ef9925 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -103,48 +103,41 @@ void EntityTreeSendThread::preDistributionProcessing() {
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) {
if (viewFrustumChanged || _traversal.finished()) {
- ViewFrustum viewFrustum;
- nodeData->copyCurrentViewFrustum(viewFrustum);
EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot());
+
+
+ DiffTraversal::View newView;
+ newView.viewFrustums = nodeData->getCurrentViews();
+
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
- startNewTraversal(viewFrustum, root, lodLevelOffset, nodeData->getUsesFrustum());
+ newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
+
+ startNewTraversal(newView, root);
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
// and also use the opportunity to cull anything no longer in view
if (viewFrustumChanged && !_sendQueue.empty()) {
EntityPriorityQueue prevSendQueue;
- _sendQueue.swap(prevSendQueue);
- _entitiesInQueue.clear();
+ std::swap(_sendQueue, prevSendQueue);
+ assert(_sendQueue.empty());
+
// Re-add elements from previous traversal if they still need to be sent
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
while (!prevSendQueue.empty()) {
EntityItemPointer entity = prevSendQueue.top().getEntity();
bool forceRemove = prevSendQueue.top().shouldForceRemove();
prevSendQueue.pop();
if (entity) {
- if (!forceRemove) {
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
- float priority = _conicalView.computePriority(cube);
- if (priority != PrioritizedEntity::DO_NOT_SEND) {
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
- }
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+ if (forceRemove) {
+ priority = PrioritizedEntity::FORCE_REMOVE;
} else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
- _entitiesInQueue.insert(entity.get());
+ const auto& view = _traversal.getCurrentView();
+ priority = view.computePriority(entity);
+ }
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority, forceRemove);
}
}
}
@@ -215,10 +208,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
-void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset,
- bool usesViewFrustum) {
+void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
- DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
+ DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
// there are three types of traversal:
//
// (1) FirstTime = at login --> find everything in view
@@ -226,171 +218,80 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
// (3) Differential = view has changed --> find what has changed or in new view but not old
//
// The "scanCallback" we provide to the traversal depends on the type:
- //
- // The _conicalView is updated here as a cached view approximation used by the lambdas for efficient
- // computation of entity sorting priorities.
- //
- _conicalView.set(_traversal.getCurrentView());
switch (type) {
case DiffTraversal::First:
// When we get to a First traversal, clear the _knownState
_knownState.clear();
- if (usesViewFrustum) {
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
- _traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([=](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
- // Check the size of the entity, it's possible that a "too small to see" entity is included in a
- // larger octree cell because of its position (for example if it crosses the boundary of a cell it
- // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
- // before we consider including it.
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- });
- });
- } else {
- _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([this](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- });
- });
- }
- break;
- case DiffTraversal::Repeat:
- if (usesViewFrustum) {
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
- _traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
- uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
- if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
- next.element->forEachEntity([=](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- auto knownTimestamp = _knownState.find(entity.get());
- if (knownTimestamp == _knownState.end()) {
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
- // See the DiffTraversal::First case for an explanation of the "entity is too small" check
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- } else if (entity->getLastEdited() > knownTimestamp->second
- || entity->getLastChangedOnServer() > knownTimestamp->second) {
- // it is known and it changed --> put it on the queue with any priority
- // TODO: sort these correctly
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- });
- }
- });
- } else {
- _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
- uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
- if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
- next.element->forEachEntity([this](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- auto knownTimestamp = _knownState.find(entity.get());
- if (knownTimestamp == _knownState.end()
- || entity->getLastEdited() > knownTimestamp->second
- || entity->getLastChangedOnServer() > knownTimestamp->second) {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- });
- }
- });
- }
- break;
- case DiffTraversal::Differential:
- assert(usesViewFrustum);
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
- float completedLODScaleFactor = _traversal.getCompletedLODScaleFactor();
- glm::vec3 completedViewPosition = _traversal.getCompletedView().getPosition();
- _traversal.setScanCallback([=] (DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([=](EntityItemPointer entity) {
+ _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
+ if (_sendQueue.contains(entity.get())) {
return;
}
+ const auto& view = _traversal.getCurrentView();
+ float priority = view.computePriority(entity);
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority);
+ }
+ });
+ });
+ break;
+ case DiffTraversal::Repeat:
+ _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+ uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
+ if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
+ // Bail early if we've already checked this entity this frame
+ if (_sendQueue.contains(entity.get())) {
+ return;
+ }
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+ auto knownTimestamp = _knownState.find(entity.get());
+ if (knownTimestamp == _knownState.end()) {
+ const auto& view = _traversal.getCurrentView();
+ priority = view.computePriority(entity);
+
+ } else if (entity->getLastEdited() > knownTimestamp->second ||
+ entity->getLastChangedOnServer() > knownTimestamp->second) {
+ // it is known and it changed --> put it on the queue with any priority
+ // TODO: sort these correctly
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+ }
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority);
+ }
+ });
+ }
+ });
+ break;
+ case DiffTraversal::Differential:
+ assert(view.usesViewFrustums());
+ _traversal.setScanCallback([this] (DiffTraversal::VisibleElement& next) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
+ // Bail early if we've already checked this entity this frame
+ if (_sendQueue.contains(entity.get())) {
+ return;
+ }
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end()) {
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
- // See the DiffTraversal::First case for an explanation of the "entity is too small" check
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
- if (!_traversal.getCompletedView().cubeIntersectsKeyhole(cube)) {
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- } else {
- // If this entity was skipped last time because it was too small, we still need to send it
- distance = glm::distance(cube.calcCenter(), completedViewPosition) + MIN_VISIBLE_DISTANCE;
- angularDiameter = cube.getScale() / distance;
- if (angularDiameter <= MIN_ENTITY_ANGULAR_DIAMETER * completedLODScaleFactor) {
- // this object was skipped in last completed traversal
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
- }
- }
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- } else if (entity->getLastEdited() > knownTimestamp->second
- || entity->getLastChangedOnServer() > knownTimestamp->second) {
+ const auto& view = _traversal.getCurrentView();
+ priority = view.computePriority(entity);
+
+ } else if (entity->getLastEdited() > knownTimestamp->second ||
+ entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+ }
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority);
}
});
});
@@ -479,11 +380,10 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
}
}
_sendQueue.pop();
- _entitiesInQueue.erase(entity.get());
}
nodeData->stats.encodeStopped();
if (_sendQueue.empty()) {
- assert(_entitiesInQueue.empty());
+ assert(_sendQueue.empty());
params.stopReason = EncodeBitstreamParams::FINISHED;
_extraEncodeData->entities.clear();
}
@@ -501,18 +401,15 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
if (entity) {
- if (_entitiesInQueue.find(entity.get()) == _entitiesInQueue.end() && _knownState.find(entity.get()) != _knownState.end()) {
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- // We can force a removal from _knownState if the current view is used and entity is out of view
- if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
- _entitiesInQueue.insert(entity.get());
- }
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true));
- _entitiesInQueue.insert(entity.get());
+ if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
+ const auto& view = _traversal.getCurrentView();
+ float priority = view.computePriority(entity);
+
+ // We can force a removal from _knownState if the current view is used and entity is out of view
+ if (priority == PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
+ } else if (priority == PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY) {
+ _sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
}
}
}
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 1e2bd15429..1305d7bfc7 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -17,8 +17,9 @@
#include "../octree/OctreeSendThread.h"
#include
+#include
+#include
-#include "EntityPriorityQueue.h"
class EntityNodeData;
class EntityItem;
@@ -41,8 +42,7 @@ private:
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
- void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset,
- bool usesViewFrustum);
+ void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;
@@ -51,9 +51,7 @@ private:
DiffTraversal _traversal;
EntityPriorityQueue _sendQueue;
- std::unordered_set _entitiesInQueue;
std::unordered_map _knownState;
- ConicalView _conicalView; // cached optimized view for fast priority calculations
// packet construction stuff
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp
index 4bf708cf34..c11c8f40a0 100644
--- a/assignment-client/src/messages/MessagesMixer.cpp
+++ b/assignment-client/src/messages/MessagesMixer.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "MessagesMixer.h"
+
#include
#include
#include
@@ -16,7 +18,6 @@
#include
#include
#include
-#include "MessagesMixer.h"
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
index d3b20fb623..039dbdab78 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.cpp
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
@@ -14,32 +14,21 @@
#include
#include
-
-OctreeHeadlessViewer::OctreeHeadlessViewer() {
- _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
-}
-
void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType();
- _octreeQuery.setCameraPosition(_viewFrustum.getPosition());
- _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
- _octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
- _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
- _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
- _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
- _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
- _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
- _octreeQuery.setOctreeSizeScale(_voxelSizeScale);
- _octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
+ if (_hasViewFrustum) {
+ ConicalViewFrustums views { _viewFrustum };
+ _octreeQuery.setConicalViews(views);
+ } else {
+ _octreeQuery.clearConicalViews();
+ }
auto nodeList = DependencyManager::get();
auto node = nodeList->soloNodeOfType(serverType);
if (node && node->getActiveSocket()) {
- _octreeQuery.setMaxQueryPacketsPerSecond(getMaxPacketsPerSecond());
-
auto queryPacket = NLPacket::create(packetType);
// encode the query data
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.h b/assignment-client/src/octree/OctreeHeadlessViewer.h
index feb8211c39..dea91ce66f 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.h
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.h
@@ -20,9 +20,6 @@
class OctreeHeadlessViewer : public OctreeProcessor {
Q_OBJECT
public:
- OctreeHeadlessViewer();
- virtual ~OctreeHeadlessViewer() {};
-
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
static int parseOctreeStats(QSharedPointer message, SharedNodePointer sourceNode);
@@ -32,34 +29,32 @@ public slots:
void queryOctree();
// setters for camera attributes
- void setPosition(const glm::vec3& position) { _viewFrustum.setPosition(position); }
- void setOrientation(const glm::quat& orientation) { _viewFrustum.setOrientation(orientation); }
- void setCenterRadius(float radius) { _viewFrustum.setCenterRadius(radius); }
- void setKeyholeRadius(float radius) { _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
+ void setPosition(const glm::vec3& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); }
+ void setOrientation(const glm::quat& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); }
+ void setCenterRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); }
+ void setKeyholeRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
// setters for LOD and PPS
- void setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; }
- void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
- void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _maxPacketsPerSecond = maxPacketsPerSecond; }
+ void setVoxelSizeScale(float sizeScale) { _octreeQuery.setOctreeSizeScale(sizeScale) ; }
+ void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _octreeQuery.setBoundaryLevelAdjust(boundaryLevelAdjust); }
+ void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _octreeQuery.setMaxQueryPacketsPerSecond(maxPacketsPerSecond); }
// getters for camera attributes
const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); }
const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); }
// getters for LOD and PPS
- float getVoxelSizeScale() const { return _voxelSizeScale; }
- int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
- int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; }
+ float getVoxelSizeScale() const { return _octreeQuery.getOctreeSizeScale(); }
+ int getBoundaryLevelAdjust() const { return _octreeQuery.getBoundaryLevelAdjust(); }
+ int getMaxPacketsPerSecond() const { return _octreeQuery.getMaxQueryPacketsPerSecond(); }
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
private:
OctreeQuery _octreeQuery;
+ bool _hasViewFrustum { false };
ViewFrustum _viewFrustum;
- float _voxelSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
- int _boundaryLevelAdjust { 0 };
- int _maxPacketsPerSecond { DEFAULT_MAX_OCTREE_PPS };
};
#endif // hifi_OctreeHeadlessViewer_h
diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
index bce6e7fe44..ef532bb33f 100644
--- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
+++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "OctreeInboundPacketProcessor.h"
+
#include
#include
@@ -17,7 +19,6 @@
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
-#include "OctreeInboundPacketProcessor.h"
static QUuid DEFAULT_NODE_ID_REF;
const quint64 TOO_LONG_SINCE_LAST_NACK = 1 * USECS_PER_SECOND;
diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index de49bd461c..e9aa44b970 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "OctreeSendThread.h"
+
#include
#include
@@ -17,7 +19,6 @@
#include
#include
-#include "OctreeSendThread.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
#include "OctreeLogging.h"
@@ -330,8 +331,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
} else {
// we aren't forcing a full scene, check if something else suggests we should
isFullScene = nodeData->haveJSONParametersChanged() ||
- (nodeData->getUsesFrustum()
- && ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
+ (nodeData->hasConicalViews() &&
+ (nodeData->getViewFrustumJustStoppedChanging() ||
+ nodeData->hasLodChanged()));
}
if (nodeData->isPacketWaiting()) {
@@ -445,7 +447,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
};
- nodeData->copyCurrentViewFrustum(params.viewFrustum);
bool somethingToSend = true; // assume we have something
bool hadSomething = hasSomethingToSend(nodeData);
diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp
index d242b393bf..eea8e8b470 100644
--- a/assignment-client/src/scripts/EntityScriptServer.cpp
+++ b/assignment-client/src/scripts/EntityScriptServer.cpp
@@ -294,7 +294,6 @@ void EntityScriptServer::run() {
queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags;
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
- _entityViewer.getOctreeQuery().setUsesFrustum(false);
_entityViewer.getOctreeQuery().setJSONParameters(queryJSONParameters);
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
diff --git a/domain-server/resources/web/wizard/index.shtml b/domain-server/resources/web/wizard/index.shtml
index 5a3286296d..3bc7503b44 100644
--- a/domain-server/resources/web/wizard/index.shtml
+++ b/domain-server/resources/web/wizard/index.shtml
@@ -26,7 +26,7 @@
Place names are similar to web addresses. Users who want to visit your domain can
enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.
- People can also use your domain's IP address (shown below) to visit your High Fidelity domain.
+ Your domain may also be reachable by IP address.
@@ -35,10 +35,10 @@
diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp
index 974d4a59c3..486b51f9eb 100644
--- a/domain-server/src/DomainServerNodeData.cpp
+++ b/domain-server/src/DomainServerNodeData.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "DomainServerNodeData.h"
+
#include
#include
#include
@@ -17,8 +19,6 @@
#include
-#include "DomainServerNodeData.h"
-
DomainServerNodeData::StringPairHash DomainServerNodeData::_overrideHash;
DomainServerNodeData::DomainServerNodeData() {
diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h
index db41c77cf2..6b8e9a1718 100644
--- a/domain-server/src/DomainServerNodeData.h
+++ b/domain-server/src/DomainServerNodeData.h
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp
index 3744af77f3..90eea17bec 100644
--- a/domain-server/src/DomainServerWebSessionData.cpp
+++ b/domain-server/src/DomainServerWebSessionData.cpp
@@ -9,13 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "DomainServerWebSessionData.h"
+
#include
#include
#include
#include
-#include "DomainServerWebSessionData.h"
-
DomainServerWebSessionData::DomainServerWebSessionData() :
_username(),
_roles()
diff --git a/gvr-interface/src/Client.cpp b/gvr-interface/src/Client.cpp
index 65238ad784..8f064c7fd5 100644
--- a/gvr-interface/src/Client.cpp
+++ b/gvr-interface/src/Client.cpp
@@ -9,14 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "Client.h"
+
#include
#include
#include
#include
#include
-#include "Client.h"
-
Client::Client(QObject* parent) :
QObject(parent)
{
@@ -70,4 +70,4 @@ void Client::processDatagrams() {
processVerifiedPacket(senderSockAddr, incomingPacket);
}
}
-}
\ No newline at end of file
+}
diff --git a/gvr-interface/src/GVRInterface.cpp b/gvr-interface/src/GVRInterface.cpp
index 3d58396322..f9a29d4ac4 100644
--- a/gvr-interface/src/GVRInterface.cpp
+++ b/gvr-interface/src/GVRInterface.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "GVRInterface.h"
+
#ifdef ANDROID
#include
@@ -32,8 +34,6 @@
#include "GVRMainWindow.h"
#include "RenderingClient.h"
-#include "GVRInterface.h"
-
static QString launchURLString = QString();
#ifdef ANDROID
diff --git a/gvr-interface/src/GVRMainWindow.cpp b/gvr-interface/src/GVRMainWindow.cpp
index 7a36aba66e..5495354233 100644
--- a/gvr-interface/src/GVRMainWindow.cpp
+++ b/gvr-interface/src/GVRMainWindow.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "GVRMainWindow.h"
+
#include
#include
#include
@@ -37,8 +39,6 @@ const float LIBOVR_LONG_PRESS_DURATION = 0.75f;
#include "LoginDialog.h"
#include "RenderingClient.h"
-#include "GVRMainWindow.h"
-
GVRMainWindow::GVRMainWindow(QWidget* parent) :
diff --git a/gvr-interface/src/LoginDialog.cpp b/gvr-interface/src/LoginDialog.cpp
index 95b7451bcb..d4efd425bd 100644
--- a/gvr-interface/src/LoginDialog.cpp
+++ b/gvr-interface/src/LoginDialog.cpp
@@ -9,14 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "LoginDialog.h"
+
#include
#include
#include
#include
#include
-#include "LoginDialog.h"
-
LoginDialog::LoginDialog(QWidget* parent) :
QDialog(parent)
{
@@ -66,4 +66,4 @@ void LoginDialog::setupGUI() {
void LoginDialog::loginButtonClicked() {
emit credentialsEntered(_usernameLineEdit->text(), _passwordLineEdit->text());
close();
-}
\ No newline at end of file
+}
diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp
index f04be5cb7f..4c691a48e6 100644
--- a/gvr-interface/src/RenderingClient.cpp
+++ b/gvr-interface/src/RenderingClient.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "RenderingClient.h"
+
#include
#include
@@ -17,8 +19,6 @@
#include
#include
-#include "RenderingClient.h"
-
RenderingClient* RenderingClient::_instance = NULL;
RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString) :
diff --git a/interface/resources/icons/+android/avatar-a.svg b/interface/resources/icons/+android/avatar-a.svg
deleted file mode 100755
index 165b39943e..0000000000
--- a/interface/resources/icons/+android/avatar-a.svg
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
diff --git a/interface/resources/icons/+android/avatar-i.svg b/interface/resources/icons/+android/avatar-i.svg
deleted file mode 100755
index c1557487ea..0000000000
--- a/interface/resources/icons/+android/avatar-i.svg
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
diff --git a/interface/resources/icons/+android/backward.svg b/interface/resources/icons/+android/backward.svg
old mode 100644
new mode 100755
index ae0893fc66..6b4c560768
--- a/interface/resources/icons/+android/backward.svg
+++ b/interface/resources/icons/+android/backward.svg
@@ -1,70 +1,13 @@
-
-
-
-
\ No newline at end of file
+
+
+
diff --git a/interface/resources/icons/+android/goto-a.svg b/interface/resources/icons/+android/goto-a.svg
deleted file mode 100755
index 5fb3e52e4c..0000000000
--- a/interface/resources/icons/+android/goto-a.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
diff --git a/interface/resources/icons/+android/goto-i.svg b/interface/resources/icons/+android/goto-i.svg
deleted file mode 100644
index 7613beb9e7..0000000000
--- a/interface/resources/icons/+android/goto-i.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
diff --git a/interface/resources/icons/+android/home.svg b/interface/resources/icons/+android/home.svg
deleted file mode 100644
index 414c179e79..0000000000
--- a/interface/resources/icons/+android/home.svg
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/interface/resources/icons/+android/login-a.svg b/interface/resources/icons/+android/login-a.svg
deleted file mode 100755
index 8a7f097ed7..0000000000
--- a/interface/resources/icons/+android/login-a.svg
+++ /dev/null
@@ -1,990 +0,0 @@
-
-
-
-
diff --git a/interface/resources/icons/+android/login-i.svg b/interface/resources/icons/+android/login-i.svg
deleted file mode 100755
index 6f011e1d13..0000000000
--- a/interface/resources/icons/+android/login-i.svg
+++ /dev/null
@@ -1,990 +0,0 @@
-
-
-
-
diff --git a/interface/resources/icons/+android/mic-unmute-a.svg b/interface/resources/icons/+android/mic-unmute-a.svg
old mode 100644
new mode 100755
index bb28dc0f2b..8717636a34
--- a/interface/resources/icons/+android/mic-unmute-a.svg
+++ b/interface/resources/icons/+android/mic-unmute-a.svg
@@ -1,70 +1,22 @@
-
-
-
-
\ No newline at end of file
+
+
+
diff --git a/interface/resources/icons/+android/myview-a.svg b/interface/resources/icons/+android/myview-a.svg
index 307b559c95..e2c183caf2 100755
--- a/interface/resources/icons/+android/myview-a.svg
+++ b/interface/resources/icons/+android/myview-a.svg
@@ -1,56 +1,19 @@
-
-
-
-
\ No newline at end of file
+
+
+
diff --git a/interface/resources/icons/+android/radar-a.svg b/interface/resources/icons/+android/radar-a.svg
deleted file mode 100755
index e4b157f827..0000000000
--- a/interface/resources/icons/+android/radar-a.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/interface/resources/icons/+android/radar-hover.svg b/interface/resources/icons/+android/radar-hover.svg
deleted file mode 100755
index e4b157f827..0000000000
--- a/interface/resources/icons/+android/radar-hover.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/interface/resources/icons/+android/radar-i.svg b/interface/resources/icons/+android/radar-i.svg
deleted file mode 100755
index 3994a775d3..0000000000
--- a/interface/resources/icons/+android/radar-i.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/interface/resources/images/fly.png b/interface/resources/images/fly.png
index 0edfcab21b..b8b96645f8 100644
Binary files a/interface/resources/images/fly.png and b/interface/resources/images/fly.png differ
diff --git a/interface/resources/qml/+android/AddressBarDialog.qml b/interface/resources/qml/+android/AddressBarDialog.qml
deleted file mode 100644
index 4477d512fc..0000000000
--- a/interface/resources/qml/+android/AddressBarDialog.qml
+++ /dev/null
@@ -1,232 +0,0 @@
-//
-// AddressBarDialog.qml
-//
-// Created by Austin Davis on 2015/04/14
-// Copyright 2015 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-import Hifi 1.0
-import QtQuick 2.4
-import "../controls"
-import "../styles"
-import "../hifi" as QmlHifi
-import "../hifi/toolbars"
-import "../styles-uit" as HifiStyles
-import "../controls-uit" as HifiControls
-
-Item {
- QmlHifi.HifiConstants { id: android }
-
- width: parent ? parent.width - android.dimen.windowLessWidth : 0
- height: parent ? parent.height - android.dimen.windowLessHeight : 0
- z: android.dimen.windowZ
- anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom }
-
- id: bar
- property bool isCursorVisible: false // Override default cursor visibility.
- property bool shown: true
-
- onShownChanged: {
- bar.visible = shown;
- sendToScript({method: 'shownChanged', params: { shown: shown }});
- if (shown) {
- addressLine.text="";
- updateLocationText(addressLine.text.length > 0);
- }
- }
-
- function hide() {
- shown = false;
- sendToScript ({ type: "hide" });
- }
-
- Component.onCompleted: {
- updateLocationText(addressLine.text.length > 0);
- }
-
- HifiConstants { id: hifi }
- HifiStyles.HifiConstants { id: hifiStyleConstants }
-
- signal sendToScript(var message);
-
- AddressBarDialog {
- id: addressBarDialog
- }
-
-
- Rectangle {
- id: background
- gradient: Gradient {
- GradientStop { position: 0.0; color: android.color.gradientTop }
- GradientStop { position: 1.0; color: android.color.gradientBottom }
- }
- anchors {
- fill: parent
- }
-
- MouseArea {
- anchors.fill: parent
- }
-
- QmlHifi.WindowHeader {
- id: header
- iconSource: "../../../icons/goto-i.svg"
- titleText: "GO TO"
- }
-
- HifiStyles.RalewayRegular {
- id: notice
- text: "YOUR LOCATION"
- font.pixelSize: (hifi.fonts.pixelSize * 2.15) * (android.dimen.atLeast1440p ? 1 : .75);
- color: "#2CD7FF"
- anchors {
- bottom: addressBackground.top
- bottomMargin: android.dimen.atLeast1440p ? 45 : 34
- left: addressBackground.left
- leftMargin: android.dimen.atLeast1440p ? 60 : 45
- }
-
- }
-
- property int inputAreaHeight: android.dimen.atLeast1440p ? 210 : 156
- property int inputAreaStep: (height - inputAreaHeight) / 2
-
- ToolbarButton {
- id: homeButton
- y: android.dimen.atLeast1440p ? 280 : 210
- imageURL: "../../icons/home.svg"
- onClicked: {
- addressBarDialog.loadHome();
- bar.shown = false;
- }
- anchors {
- leftMargin: android.dimen.atLeast1440p ? 75 : 56
- left: parent.left
- }
- size: android.dimen.atLeast1440p ? 150 : 150//112
- }
-
- ToolbarButton {
- id: backArrow;
- imageURL: "../../icons/backward.svg";
- onClicked: addressBarDialog.loadBack();
- anchors {
- left: homeButton.right
- leftMargin: android.dimen.atLeast1440p ? 70 : 52
- verticalCenter: homeButton.verticalCenter
- }
- size: android.dimen.atLeast1440p ? 150 : 150
- }
- ToolbarButton {
- id: forwardArrow;
- imageURL: "../../icons/forward.svg";
- onClicked: addressBarDialog.loadForward();
- anchors {
- left: backArrow.right
- leftMargin: android.dimen.atLeast1440p ? 60 : 45
- verticalCenter: homeButton.verticalCenter
- }
- size: android.dimen.atLeast1440p ? 150 : 150
- }
-
- HifiStyles.FiraSansRegular {
- id: location;
- font.pixelSize: addressLine.font.pixelSize;
- color: "lightgray";
- clip: true;
- anchors.fill: addressLine;
- visible: addressLine.text.length === 0
- z: 1
- }
-
- Rectangle {
- id: addressBackground
- x: android.dimen.atLeast1440p ? 780 : 585
- y: android.dimen.atLeast1440p ? 280 : 235 // tweaking by hand
- width: android.dimen.atLeast1440p ? 1270 : 952
- height: android.dimen.atLeast1440p ? 150 : 112
- color: "#FFFFFF"
- }
-
- TextInput {
- id: addressLine
- focus: true
- x: android.dimen.atLeast1440p ? 870 : 652
- y: android.dimen.atLeast1440p ? 300 : 245 // tweaking by hand
- width: android.dimen.atLeast1440p ? 1200 : 900
- height: android.dimen.atLeast1440p ? 120 : 90
- inputMethodHints: Qt.ImhNoPredictiveText
- //helperText: "Hint is here"
- font.pixelSize: hifi.fonts.pixelSize * 3.75
- onTextChanged: {
- //filterChoicesByText();
- updateLocationText(addressLine.text.length > 0);
- if (!isCursorVisible && text.length > 0) {
- isCursorVisible = true;
- cursorVisible = true;
- }
- }
-
- onActiveFocusChanged: {
- //cursorVisible = isCursorVisible && focus;
- }
- }
-
-
-
- function toggleOrGo() {
- if (addressLine.text !== "") {
- addressBarDialog.loadAddress(addressLine.text);
- }
- bar.shown = false;
- }
-
- Keys.onPressed: {
- switch (event.key) {
- case Qt.Key_Escape:
- case Qt.Key_Back:
- clearAddressLineTimer.start();
- event.accepted = true
- bar.shown = false;
- break
- case Qt.Key_Enter:
- case Qt.Key_Return:
- toggleOrGo();
- clearAddressLineTimer.start();
- event.accepted = true
- break
- }
- }
-
- }
-
- Timer {
- // Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
- id: clearAddressLineTimer
- running: false
- interval: 100 // ms
- repeat: false
- onTriggered: {
- addressLine.text = "";
- isCursorVisible = false;
- }
- }
-
- function updateLocationText(enteringAddress) {
- if (enteringAddress) {
- notice.text = "Go to a place, @user, path or network address";
- notice.color = "#ffffff"; // hifiStyleConstants.colors.baseGrayHighlight;
- location.visible = false;
- } else {
- notice.text = AddressManager.isConnected ? "YOUR LOCATION:" : "NOT CONNECTED";
- notice.color = AddressManager.isConnected ? hifiStyleConstants.colors.blueHighlight : hifiStyleConstants.colors.redHighlight;
- // Display hostname, which includes ip address, localhost, and other non-placenames.
- location.text = (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '');
- location.visible = true;
- }
- }
-
-}
diff --git a/interface/resources/qml/+android/LoginDialog.qml b/interface/resources/qml/+android/LoginDialog.qml
deleted file mode 100644
index 567cca9bcf..0000000000
--- a/interface/resources/qml/+android/LoginDialog.qml
+++ /dev/null
@@ -1,95 +0,0 @@
-//
-// LoginDialog.qml
-//
-// Created by David Rowe on 3 Jun 2015
-// Copyright 2015 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-import Hifi 1.0
-import QtQuick 2.4
-
-import "controls-uit"
-import "styles-uit"
-import "windows"
-
-import "LoginDialog"
-
-ModalWindow {
- id: root
- HifiConstants { id: hifi }
-
- objectName: "LoginDialog"
- implicitWidth: 1560
- implicitHeight: 450
- y:0
- destroyOnCloseButton: true
- destroyOnHidden: true
- visible: true
-
- property string iconText: ""
- property int iconSize: 105
-
- property string title: ""
- property int titleWidth: 0
-
- keyboardOverride: true // Disable ModalWindow's keyboard.
-
- function tryDestroy() {
- Controller.setVPadHidden(false);
- root.destroy();
- }
-
- LoginDialog {
- id: loginDialog
-
- Loader {
- id: bodyLoader
- source: loginDialog.isSteamRunning() ? "LoginDialog/+android/SignInBody.qml" : "LoginDialog/+android/LinkAccountBody.qml"
- }
- }
-
- Component.onCompleted: {
- this.anchors.centerIn = undefined;
- this.y = 150;
- this.x = (parent.width - this.width) / 2;
- Controller.setVPadHidden(true);
- }
-
- Keys.onPressed: {
- if (!visible) {
- return
- }
-
- if (event.modifiers === Qt.ControlModifier)
- switch (event.key) {
- case Qt.Key_A:
- event.accepted = true
- detailedText.selectAll()
- break
- case Qt.Key_C:
- event.accepted = true
- detailedText.copy()
- break
- case Qt.Key_Period:
- if (Qt.platform.os === "osx") {
- event.accepted = true
- content.reject()
- }
- break
- } else switch (event.key) {
- case Qt.Key_Escape:
- case Qt.Key_Back:
- event.accepted = true
- destroy()
- break
-
- case Qt.Key_Enter:
- case Qt.Key_Return:
- event.accepted = true
- break
- }
- }
-}
diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml
index c4b6c2aee1..9cb1add704 100644
--- a/interface/resources/qml/LoginDialog/SignInBody.qml
+++ b/interface/resources/qml/LoginDialog/SignInBody.qml
@@ -84,11 +84,9 @@ Item {
height: undefined // invalidate so that the image's size sets the height
focus: true
- style: OriginalStyles.ButtonStyle {
- background: Image {
- id: buttonImage
- source: "../../images/steam-sign-in.png"
- }
+ background: Image {
+ id: buttonImage
+ source: "../../images/steam-sign-in.png"
}
onClicked: signInBody.login()
}
diff --git a/interface/resources/qml/hifi/+android/ActionBar.qml b/interface/resources/qml/hifi/+android/ActionBar.qml
new file mode 100644
index 0000000000..d487901d6f
--- /dev/null
+++ b/interface/resources/qml/hifi/+android/ActionBar.qml
@@ -0,0 +1,71 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import QtQuick.Layouts 1.3
+import Qt.labs.settings 1.0
+import "../../styles-uit"
+import "../../controls-uit" as HifiControlsUit
+import "../../controls" as HifiControls
+import ".."
+
+Item {
+ id: actionBar
+ x:0
+ y:0
+ width: 300
+ height: 300
+ z: -1
+
+ signal sendToScript(var message);
+ signal windowClosed();
+
+ property bool shown: true
+
+ onShownChanged: {
+ actionBar.visible = shown;
+ }
+
+ Rectangle {
+ anchors.fill : parent
+ color: "transparent"
+ Flow {
+ id: flowMain
+ spacing: 10
+ flow: Flow.TopToBottom
+ layoutDirection: Flow.TopToBottom
+ anchors.fill: parent
+ anchors.margins: 4
+ }
+ }
+
+ Component.onCompleted: {
+ // put on bottom
+ x = 7;
+ y = 7;
+ width = 300;
+ height = 300;
+ }
+
+ function addButton(properties) {
+ var component = Qt.createComponent("button.qml");
+ if (component.status == Component.Ready) {
+ var button = component.createObject(flowMain);
+ // copy all properites to button
+ var keys = Object.keys(properties).forEach(function (key) {
+ button[key] = properties[key];
+ });
+ return button;
+ } else if( component.status == Component.Error) {
+ console.log("Load button errors " + component.errorString());
+ }
+ }
+
+ function urlHelper(src) {
+ if (src.match(/\bhttp/)) {
+ return src;
+ } else {
+ return "../../../" + src;
+ }
+ }
+
+}
diff --git a/interface/resources/qml/hifi/+android/AudioBar.qml b/interface/resources/qml/hifi/+android/AudioBar.qml
index f524595ef5..6cc17fccf7 100644
--- a/interface/resources/qml/hifi/+android/AudioBar.qml
+++ b/interface/resources/qml/hifi/+android/AudioBar.qml
@@ -38,13 +38,26 @@ Item {
}
}
- Component.onCompleted: {
- // put on bottom
- x = 0;
- y = 0;
+ function relocateAndResize(newWindowWidth, newWindowHeight) {
+ x = newWindowWidth-328;
+ y = 19;
width = 300;
height = 300;
}
+
+ function onWindowGeometryChanged(rect) {
+ relocateAndResize(rect.width, rect.height);
+ }
+
+ Component.onCompleted: {
+ relocateAndResize(parent.width, parent.height);
+ Window.geometryChanged.connect(onWindowGeometryChanged); // In devices with bars appearing at startup we should listen for this
+ }
+
+ Component.onDestruction: {
+ Window.geometryChanged.disconnect(onWindowGeometryChanged);
+ }
+
function addButton(properties) {
var component = Qt.createComponent("button.qml");
diff --git a/interface/resources/qml/hifi/+android/avatarSelection.qml b/interface/resources/qml/hifi/+android/avatarSelection.qml
deleted file mode 100644
index afa5634575..0000000000
--- a/interface/resources/qml/hifi/+android/avatarSelection.qml
+++ /dev/null
@@ -1,179 +0,0 @@
-//
-// avatarSelection.qml
-// interface/resources/qml/android
-//
-// Created by Gabriel Calero & Cristian Duarte on 21 Sep 2017
-// Copyright 2017 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-import QtQuick 2.5
-import QtQuick.Layouts 1.3
-import Hifi 1.0
-
-import "../../styles"
-import "."
-import ".."
-import ".." as QmlHifi
-import "../../styles-uit" as HifiStyles
-
-
-Item {
-
- id: top
-
- HifiConstants { id: android }
- width: parent ? parent.width - android.dimen.windowLessWidth : 0
- height: parent ? parent.height - android.dimen.windowLessHeight : 0
- z: android.dimen.windowZ
- anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom }
-
- signal sendToScript(var message);
-
- property bool shown: true
-
- onShownChanged: {
- top.visible = shown;
- }
-
-
- HifiConstants { id: hifi }
- HifiStyles.HifiConstants { id: hifiStyleConstants }
-
- property int cardWidth: 250 *3;
- property int cardHeight: 240 *3;
- property int gap: 14 *3;
-
- property var avatarsArray: [];
- property var extraOptionsArray: [];
-
- function hide() {
- shown = false;
- sendToScript ({ method: "hide" });
- }
-
- Rectangle {
-
- width: parent ? parent.width : 0
- height: parent ? parent.height : 0
-
- MouseArea {
- anchors.fill: parent
- }
-
- gradient: Gradient {
- GradientStop { position: 0.0; color: android.color.gradientTop }
- GradientStop { position: 1.0; color: android.color.gradientBottom }
- }
-
- QmlHifi.WindowHeader {
- id: header
- iconSource: "../../../../icons/avatar-i.svg"
- titleText: "AVATAR"
- }
-
- ListModel { id: avatars }
-
- ListView {
- id: scroll
- height: 250*3
- property int stackedCardShadowHeight: 10*3;
- spacing: gap;
- clip: true;
- anchors {
- left: parent.left
- right: parent.right
- top: header.bottom
- topMargin: gap
- leftMargin: gap
- rightMargin: gap
- }
- model: avatars;
- orientation: ListView.Horizontal;
- delegate: QmlHifi.AvatarOption {
- type: model.type;
- thumbnailUrl: model.thumbnailUrl;
- avatarUrl: model.avatarUrl;
- avatarName: model.avatarName;
- avatarSelected: model.avatarSelected;
- methodName: model.methodName;
- actionText: model.actionText;
- }
- highlightMoveDuration: -1;
- highlightMoveVelocity: -1;
- }
-
- }
-
- function escapeRegExp(str) {
- return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
- }
- function replaceAll(str, find, replace) {
- return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
- }
-
- function refreshSelected(selectedAvatarUrl) {
- // URL as ID?
- avatarsArray.forEach(function (avatarData) {
- avatarData.avatarSelected = (selectedAvatarUrl == avatarData.avatarUrl);
- console.log('[avatarSelection] avatar : ', avatarData.avatarName, ' is selected? ' , avatarData.avatarSelected);
- });
- }
-
- function addAvatar(name, thumbnailUrl, avatarUrl) {
- avatarsArray.push({
- type: "avatar",
- thumbnailUrl: thumbnailUrl,
- avatarUrl: avatarUrl,
- avatarName: name,
- avatarSelected: false,
- methodName: "",
- actionText: ""
- });
- }
-
- function showAvatars() {
- avatars.clear();
- avatarsArray.forEach(function (avatarData) {
- avatars.append(avatarData);
- console.log('[avatarSelection] adding avatar to model: ', JSON.stringify(avatarData));
- });
- extraOptionsArray.forEach(function (extraData) {
- avatars.append(extraData);
- console.log('[avatarSelection] adding extra option to model: ', JSON.stringify(extraData));
- });
- }
-
- function addExtraOption(showName, thumbnailUrl, methodNameWhenClicked, actionText) {
- extraOptionsArray.push({
- type: "extra",
- thumbnailUrl: thumbnailUrl,
- avatarUrl: "",
- avatarName: showName,
- avatarSelected: false,
- methodName: methodNameWhenClicked,
- actionText: actionText
- });
- }
-
- function fromScript(message) {
- //console.log("[CHAT] fromScript " + JSON.stringify(message));
- switch (message.type) {
- case "addAvatar":
- addAvatar(message.name, message.thumbnailUrl, message.avatarUrl);
- break;
- case "addExtraOption":
- //(showName, thumbnailUrl, methodNameWhenClicked, actionText)
- addExtraOption(message.showName, message.thumbnailUrl, message.methodNameWhenClicked, message.actionText);
- break;
- case "refreshSelected":
- refreshSelected(message.selectedAvatarUrl);
- break;
- case "showAvatars":
- showAvatars();
- break;
- default:
- }
- }
-}
\ No newline at end of file
diff --git a/interface/resources/qml/hifi/+android/bottomHudOptions.qml b/interface/resources/qml/hifi/+android/bottomHudOptions.qml
index 860298149f..22beccf531 100644
--- a/interface/resources/qml/hifi/+android/bottomHudOptions.qml
+++ b/interface/resources/qml/hifi/+android/bottomHudOptions.qml
@@ -47,17 +47,14 @@ Item {
spacing: 0
flow: Flow.LeftToRight
layoutDirection: Flow.LeftToRight
- anchors.fill: parent
+ anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: 12
Rectangle {
id: hideButton
- height: android.dimen.headerHideWidth
- width: android.dimen.headerHideHeight
+ height: android.dimen.headerHideHeight
+ width: android.dimen.headerHideWidth
color: "#00000000"
- anchors {
- horizontalCenter: parent.horizontalCenter
- }
Image {
id: hideIcon
source: "../../../icons/show-up.svg"
diff --git a/interface/resources/qml/hifi/+android/bottombar.qml b/interface/resources/qml/hifi/+android/bottombar.qml
deleted file mode 100644
index 66117d0389..0000000000
--- a/interface/resources/qml/hifi/+android/bottombar.qml
+++ /dev/null
@@ -1,151 +0,0 @@
-//
-// bottomHudOptions.qml
-// interface/resources/qml/android
-//
-// Created by Gabriel Calero & Cristian Duarte on 19 Jan 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
-//
-
-import Hifi 1.0
-import QtQuick 2.5
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import QtQuick.Layouts 1.3
-import Qt.labs.settings 1.0
-import "../../styles" as Styles
-import "../../styles-uit"
-import "../../controls-uit" as HifiControlsUit
-import "../../controls" as HifiControls
-import ".."
-import "."
-
-Item {
- id: bar
- x:0
- height: 255
-
- property bool shown: true
-
- signal sendToScript(var message);
-
- onShownChanged: {
- bar.visible = shown;
- }
-
- function hide() {
- //shown = false;
- sendToScript({ method: "hide" });
- }
-
- Styles.HifiConstants { id: hifi }
- HifiConstants { id: android }
- MouseArea {
- anchors.fill: parent
- }
-
- Rectangle {
- id: background
- anchors.fill : parent
- color: "#FF000000"
- border.color: "#FFFFFF"
- anchors.bottomMargin: -1
- anchors.leftMargin: -1
- anchors.rightMargin: -1
- Flow {
- id: flowMain
- spacing: 10
- anchors.fill: parent
- anchors.topMargin: 12
- anchors.bottomMargin: 12
- anchors.rightMargin: 12
- anchors.leftMargin: 72
- }
-
-
- Rectangle {
- id: hideButton
- height: android.dimen.headerHideWidth
- width: android.dimen.headerHideHeight
- color: "#00000000"
- anchors {
- right: parent.right
- rightMargin: android.dimen.headerHideRightMargin
- top: parent.top
- topMargin: android.dimen.headerHideTopMargin
- }
-
- Image {
- id: hideIcon
- source: "../../../icons/hide.svg"
- width: android.dimen.headerHideIconWidth
- height: android.dimen.headerHideIconHeight
- anchors {
- horizontalCenter: parent.horizontalCenter
- top: parent.top
- }
- }
- FiraSansRegular {
- anchors {
- top: hideIcon.bottom
- horizontalCenter: hideIcon.horizontalCenter
- topMargin: 12
- }
- text: "HIDE"
- color: "#FFFFFF"
- font.pixelSize: hifi.fonts.pixelSize * 2.5;
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- hide();
- }
- }
- }
- }
-
- function relocateAndResize(newWindowWidth, newWindowHeight) {
- width = newWindowWidth;
- y = newWindowHeight - height;
- }
-
- function onWindowGeometryChanged(rect) {
- relocateAndResize(rect.width, rect.height);
- }
-
- Component.onCompleted: {
- // put on bottom
- relocateAndResize(Window.innerWidth, Window.innerHeight);
- Window.geometryChanged.connect(onWindowGeometryChanged); // In devices with bars appearing at startup we should listen for this
- }
-
- Component.onDestruction: {
- Window.geometryChanged.disconnect(onWindowGeometryChanged);
- }
-
- function addButton(properties) {
- var component = Qt.createComponent("button.qml");
- if (component.status == Component.Ready) {
- var button = component.createObject(flowMain);
- // copy all properites to button
- var keys = Object.keys(properties).forEach(function (key) {
- button[key] = properties[key];
- });
- return button;
- } else if( component.status == Component.Error) {
- console.log("Load button errors " + component.errorString());
- }
- }
-
- function urlHelper(src) {
- if (src.match(/\bhttp/)) {
- return src;
- } else {
- return "../../../" + src;
- }
- }
-
-}
diff --git a/interface/resources/qml/hifi/+android/button.qml b/interface/resources/qml/hifi/+android/button.qml
index 3e9ce39351..a4c65b3c6f 100644
--- a/interface/resources/qml/hifi/+android/button.qml
+++ b/interface/resources/qml/hifi/+android/button.qml
@@ -32,6 +32,9 @@ Item {
property string hoverTextColor: "#ffffff"
property string activeTextColor: "#ffffff"
property string activeHoverTextColor: "#ffffff"
+ property string fontFamily: "FiraSans"
+ property bool fontBold: false
+
property int bottomMargin: 30
property bool isEntered: false
@@ -92,8 +95,8 @@ Item {
id: text
color: "#ffffff"
text: button.text
- font.family: "FiraSans"
- //font.bold: true
+ font.family: button.fontFamily
+ font.bold: button.fontBold
font.pixelSize: textSize
anchors.bottom: parent.bottom
anchors.bottomMargin: bottomMargin
@@ -143,15 +146,12 @@ Item {
PropertyChanges {
target: buttonBg
- //color: "#cfcfcf"
- //opacity: 1
color: button.hoverBgColor
opacity: button.hoverBgOpacity
}
PropertyChanges {
target: text
- //color: "#ffffff"
color: button.hoverTextColor
text: button.hoverText
}
@@ -166,15 +166,12 @@ Item {
PropertyChanges {
target: buttonBg
- //color: "#1fc6a6"
- //opacity: 1
color: button.activeBgColor
opacity: button.activeBgOpacity
}
PropertyChanges {
target: text
- //color: "#333333"
color: button.activeTextColor
text: button.activeText
}
@@ -189,15 +186,12 @@ Item {
PropertyChanges {
target: buttonBg
- //color: "#ff0000"
- //opacity: 1
color: button.activeHoverBgColor
opacity: button.activeHoverBgOpacity
}
PropertyChanges {
target: text
- //color: "#333333"
color: button.activeHoverTextColor
text: button.activeHoverText
}
@@ -212,15 +206,12 @@ Item {
PropertyChanges {
target: buttonBg
- //color: "#9A9A9A"
- //opacity: 0.1
color: button.bgColor
opacity: button.bgOpacity
}
PropertyChanges {
target: text
- //color: "#ffffff"
color: button.textColor
text: button.text
}
diff --git a/interface/resources/qml/hifi/+android/modesbar.qml b/interface/resources/qml/hifi/+android/modesbar.qml
index fe71314ece..994bf1efe4 100644
--- a/interface/resources/qml/hifi/+android/modesbar.qml
+++ b/interface/resources/qml/hifi/+android/modesbar.qml
@@ -10,24 +10,25 @@ import ".."
Item {
id: modesbar
- y:60
- Rectangle {
- anchors.fill : parent
- color: "transparent"
- Flow {
- id: flowMain
- spacing: 0
- flow: Flow.TopToBottom
- layoutDirection: Flow.TopToBottom
- anchors.fill: parent
- anchors.margins: 4
- }
- }
+ y:5
+
+ function relocateAndResize(newWindowWidth, newWindowHeight) {
+ width = 300;
+ height = 300;
+ x = newWindowWidth - 565;
+ }
+
+ function onWindowGeometryChanged(rect) {
+ relocateAndResize(rect.width, rect.height);
+ }
Component.onCompleted: {
- width = 300 + 30; // That 30 is extra regardless the qty of items shown
- height = 300 + 30;
- x=Window.innerWidth - width;
+ relocateAndResize(parent.width, parent.height);
+ Window.geometryChanged.connect(onWindowGeometryChanged); // In devices with bars appearing at startup we should listen for this
+ }
+
+ Component.onDestruction: {
+ Window.geometryChanged.disconnect(onWindowGeometryChanged);
}
function addButton(properties) {
@@ -35,7 +36,7 @@ Item {
console.log("load button");
if (component.status == Component.Ready) {
console.log("load button 2");
- var button = component.createObject(flowMain);
+ var button = component.createObject(modesbar);
// copy all properites to button
var keys = Object.keys(properties).forEach(function (key) {
button[key] = properties[key];
@@ -59,14 +60,12 @@ Item {
function fromScript(message) {
switch (message.type) {
- case "allButtonsShown":
- modesbar.height = flowMain.children.length * 300 + 30; // That 30 is extra regardless the qty of items shown
- break;
- case "inactiveButtonsHidden":
- modesbar.height = 300 + 30;
- break;
+ case "switch":
+ // message.params.to
+ // still not needed
+ break;
default:
- break;
+ break;
}
}
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index 1cfbcf9075..f25282c738 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -787,7 +787,7 @@ Rectangle {
}
lightboxPopup.button2text = "CONFIRM";
lightboxPopup.button2method = function() {
- Commerce.replaceContentSet(root.itemHref);
+ Commerce.replaceContentSet(root.itemHref, root.certificateId);
lightboxPopup.visible = false;
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index 6b48f8d51d..d79b8d09fa 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -729,7 +729,7 @@ Rectangle {
}
lightboxPopup.button2text = "CONFIRM";
lightboxPopup.button2method = function() {
- MyAvatar.skeletonModelURL = '';
+ MyAvatar.useFullAvatarURL('');
root.activeView = "giftAsset";
lightboxPopup.visible = false;
};
diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
index 1cada9789b..c4abd40d2a 100644
--- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
@@ -47,6 +47,13 @@ Item {
onWalletAuthenticatedStatusResult: {
submitPassphraseInputButton.enabled = true;
+
+ // It's not possible to auth with a blank passphrase,
+ // so bail early if we get this signal without anything in the passphrase field
+ if (passphraseField.text === "") {
+ return;
+ }
+
if (!isAuthenticated) {
errorText.text = "Authentication failed - please try again.";
passphraseField.error = true;
@@ -211,6 +218,10 @@ Item {
error = false;
focus = true;
forceActiveFocus();
+ } else {
+ showPassphrase.checked = false;
+ passphraseField.text = "";
+ error = false;
}
}
diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp
new file mode 100644
index 0000000000..b04a6ff2c4
--- /dev/null
+++ b/interface/src/AndroidHelper.cpp
@@ -0,0 +1,20 @@
+//
+// AndroidHelper.cpp
+// interface/src
+//
+// Created by Gabriel Calero & Cristian Duarte on 3/30/18.
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#include "AndroidHelper.h"
+#include
+
+void AndroidHelper::requestActivity(const QString &activityName) {
+ emit androidActivityRequested(activityName);
+}
+
+void AndroidHelper::goBackFromAndroidActivity() {
+ emit backFromAndroidActivity();
+}
\ No newline at end of file
diff --git a/interface/src/AndroidHelper.h b/interface/src/AndroidHelper.h
new file mode 100644
index 0000000000..34f32b461b
--- /dev/null
+++ b/interface/src/AndroidHelper.h
@@ -0,0 +1,37 @@
+//
+// AndroidHelper.h
+// interface/src
+//
+// Created by Gabriel Calero & Cristian Duarte on 3/30/18.
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_Android_Helper_h
+#define hifi_Android_Helper_h
+
+#include
+
+class AndroidHelper : public QObject {
+ Q_OBJECT
+public:
+ static AndroidHelper& instance() {
+ static AndroidHelper instance;
+ return instance;
+ }
+ void requestActivity(const QString &activityName);
+ void goBackFromAndroidActivity();
+
+ AndroidHelper(AndroidHelper const&) = delete;
+ void operator=(AndroidHelper const&) = delete;
+signals:
+ void androidActivityRequested(const QString &activityName);
+ void backFromAndroidActivity();
+
+private:
+ AndroidHelper() {}
+};
+
+#endif
\ No newline at end of file
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index cd4562da54..ec28dcb3dd 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -252,6 +252,7 @@ extern "C" {
#if defined(Q_OS_ANDROID)
#include
+#include "AndroidHelper.h"
#endif
enum ApplicationEvent {
@@ -742,6 +743,11 @@ extern DisplayPluginList getDisplayPlugins();
extern InputPluginList getInputPlugins();
extern void saveInputPluginSettings(const InputPluginList& plugins);
+// Parameters used for running tests from teh command line
+const QString TEST_SCRIPT_COMMAND { "--testScript" };
+const QString TEST_QUIT_WHEN_FINISHED_OPTION { "quitWhenFinished" };
+const QString TEST_SNAPSHOT_LOCATION_COMMAND { "--testSnapshotLocation" };
+
bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
const char** constArgv = const_cast(argv);
@@ -776,7 +782,22 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast(argv), SUPPRESS_SETTINGS_RESET);
- bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
+
+ // Ignore any previous crashes if running from command line with a test script.
+ bool inTestMode { false };
+ for (int i = 0; i < argc; ++i) {
+ QString parameter(argv[i]);
+ if (parameter == TEST_SCRIPT_COMMAND) {
+ inTestMode = true;
+ break;
+ }
+ }
+
+ bool previousSessionCrashed { false };
+ if (!inTestMode) {
+ previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
+ }
+
// get dir to use for cache
static const auto CACHE_SWITCH = "--cache";
QString cacheDir = getCmdOption(argc, const_cast(argv), CACHE_SWITCH);
@@ -966,7 +987,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_entitySimulation(new PhysicalEntitySimulation()),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entityClipboard(new EntityTree()),
- _lastQueriedTime(usecTimestampNow()),
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
_hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT),
@@ -996,13 +1016,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
{
- const QString TEST_SCRIPT = "--testScript";
const QStringList args = arguments();
+
for (int i = 0; i < args.size() - 1; ++i) {
- if (args.at(i) == TEST_SCRIPT) {
+ if (args.at(i) == TEST_SCRIPT_COMMAND && (i + 1) < args.size()) {
QString testScriptPath = args.at(i + 1);
- if (QFileInfo(testScriptPath).exists()) {
+
+ // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file
+ // This is done so as not break previous command line scripts
+ if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) {
+ setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath));
+ } else if (QFileInfo(testScriptPath).exists()) {
setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath));
+ }
+
+ // quite when finished parameter must directly follow the test script
+ if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) {
+ quitWhenFinished = true;
+ }
+ } else if (args.at(i) == TEST_SNAPSHOT_LOCATION_COMMAND) {
+ // Set test snapshot location only if it is a writeable directory
+ QString pathname(args.at(i + 1));
+ QFileInfo fileInfo(pathname);
+ if (fileInfo.isDir() && fileInfo.isWritable()) {
+ testSnapshotLocation = pathname;
}
}
}
@@ -1258,6 +1295,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {
getEntities()->reloadEntityScripts();
+ loadAvatarScripts(getMyAvatar()->getScriptUrls());
}, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptLoadError,
@@ -1323,6 +1361,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Create the main thread context, the GPU backend, and the display plugins
initializeGL();
+ DependencyManager::get()->setGPUContext(_gpuContext);
qCDebug(interfaceapp, "Initialized Display.");
// Create the rendering engine. This can be slow on some machines due to lots of
// GPU pipeline creation.
@@ -2126,14 +2165,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return entityServerNode && !isPhysicsEnabled();
});
- _snapshotSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/snap.wav"));
+ _snapshotSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav"));
QVariant testProperty = property(hifi::properties::TEST);
qDebug() << testProperty;
if (testProperty.isValid()) {
auto scriptEngines = DependencyManager::get();
const auto testScript = property(hifi::properties::TEST).toUrl();
- scriptEngines->loadScript(testScript, false);
+
+ // Set last parameter to exit interface when the test script finishes, if so requested
+ scriptEngines->loadScript(testScript, false, false, false, false, quitWhenFinished);
+
+ // This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
+ if (arguments().contains("--url")) {
+ auto reply = SandboxUtils::getStatus();
+ connect(reply, &QNetworkReply::finished, this, [=] {
+ handleSandboxStatus(reply);
+ });
+ }
} else {
PROFILE_RANGE(render, "GetSandboxStatus");
auto reply = SandboxUtils::getStatus();
@@ -2731,6 +2780,9 @@ void Application::initializeUi() {
offscreenUi->resume();
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
resizeGL();
+ if (_touchscreenVirtualPadDevice) {
+ _touchscreenVirtualPadDevice->resize();
+ }
});
// This will set up the input plugins UI
@@ -3166,11 +3218,13 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (firstRun.get()) {
-#if !defined(Q_OS_ANDROID)
- showHelp();
-#endif
+#if defined(Q_OS_ANDROID)
+ qCDebug(interfaceapp) << "First run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("default location") : addressLookupString);
+ DependencyManager::get()->loadSettings(addressLookupString);
+#else
DependencyManager::get()->goToEntry();
sentTo = SENT_TO_ENTRY;
+#endif
firstRun.set(false);
} else {
@@ -3626,7 +3680,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else {
showCursor(Cursor::Icon::DEFAULT);
}
- } else {
+ } else if (!event->isAutoRepeat()){
resetSensors(true);
}
break;
@@ -3740,6 +3794,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
void Application::keyReleaseEvent(QKeyEvent* event) {
_keysPressed.remove(event->key());
+#if defined(Q_OS_ANDROID)
+ if (event->key() == Qt::Key_Back) {
+ event->accept();
+ openAndroidActivity("Home");
+ }
+#endif
_controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
@@ -4807,6 +4867,31 @@ void Application::init() {
}, Qt::QueuedConnection);
}
+void Application::loadAvatarScripts(const QVector& urls) {
+ auto scriptEngines = DependencyManager::get();
+ auto runningScripts = scriptEngines->getRunningScripts();
+ for (auto url : urls) {
+ int index = runningScripts.indexOf(url);
+ if (index < 0) {
+ auto scriptEnginePointer = scriptEngines->loadScript(url, false);
+ if (scriptEnginePointer) {
+ scriptEnginePointer->setType(ScriptEngine::Type::AVATAR);
+ }
+ }
+ }
+}
+
+void Application::unloadAvatarScripts() {
+ auto scriptEngines = DependencyManager::get();
+ auto urls = scriptEngines->getRunningScripts();
+ for (auto url : urls) {
+ auto scriptEngine = scriptEngines->getScriptEngine(url);
+ if (scriptEngine->getType() == ScriptEngine::Type::AVATAR) {
+ scriptEngines->stopScript(url, false);
+ }
+ }
+}
+
void Application::updateLOD(float deltaTime) const {
PerformanceTimer perfTimer("LOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
@@ -5045,7 +5130,7 @@ void Application::reloadResourceCaches() {
resetPhysicsReadyInformation();
// Query the octree to refresh everything in view
- _lastQueriedTime = 0;
+ _queryExpiry = SteadyClock::now();
_octreeQuery.incrementConnectionID();
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
@@ -5179,6 +5264,78 @@ void Application::updateDialogs(float deltaTime) const {
}
}
+void Application::updateSecondaryCameraViewFrustum() {
+ // TODO: Fix this by modeling the way the secondary camera works on how the main camera works
+ // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
+ // camera should be.
+
+ // Code based on SecondaryCameraJob
+ auto renderConfig = _renderEngine->getConfiguration();
+ assert(renderConfig);
+ auto camera = dynamic_cast(renderConfig->getConfig("SecondaryCamera"));
+
+ if (!camera || !camera->isEnabled()) {
+ return;
+ }
+
+ ViewFrustum secondaryViewFrustum;
+ if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
+ auto entityScriptingInterface = DependencyManager::get();
+ auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
+ glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition();
+ glm::quat mirrorPropertiesRotation = entityProperties.getRotation();
+ glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions();
+ glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions;
+
+ // setup mirror from world as inverse of world from mirror transformation using inverted x and z for mirrored image
+ // TODO: we are assuming here that UP is world y-axis
+ glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation) * glm::scale(vec3(-1.0f, 1.0f, -1.0f));
+ glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition);
+ glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation;
+ glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror);
+
+ // get mirror camera position by reflecting main camera position's z coordinate in mirror space
+ glm::vec3 mainCameraPositionWorld = getCamera().getPosition();
+ glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f));
+ glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y,
+ -mainCameraPositionMirror.z);
+ glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f));
+
+ // set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation
+ glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation);
+ secondaryViewFrustum.setPosition(mirrorCameraPositionWorld);
+ secondaryViewFrustum.setOrientation(mirrorCameraOrientation);
+
+ // build frustum using mirror space translation of mirrored camera
+ float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f;
+ glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+ glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+ glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance);
+ secondaryViewFrustum.setProjection(frustum);
+ } else {
+ if (!camera->attachedEntityId.isNull()) {
+ auto entityScriptingInterface = DependencyManager::get();
+ auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
+ secondaryViewFrustum.setPosition(entityProperties.getPosition());
+ secondaryViewFrustum.setOrientation(entityProperties.getRotation());
+ } else {
+ secondaryViewFrustum.setPosition(camera->position);
+ secondaryViewFrustum.setOrientation(camera->orientation);
+ }
+
+ float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight;
+ secondaryViewFrustum.setProjection(camera->vFoV,
+ aspectRatio,
+ camera->nearClipPlaneDistance,
+ camera->farClipPlaneDistance);
+ }
+ // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera,
+ // which is not what we want here.
+ secondaryViewFrustum.calculate();
+
+ _conicalViews.push_back(secondaryViewFrustum);
+}
+
static bool domainLoadingInProgress = false;
void Application::update(float deltaTime) {
@@ -5532,6 +5689,13 @@ void Application::update(float deltaTime) {
{
QMutexLocker viewLocker(&_viewMutex);
_myCamera.loadViewFrustum(_viewFrustum);
+
+ _conicalViews.clear();
+ _conicalViews.push_back(_viewFrustum);
+ // TODO: Fix this by modeling the way the secondary camera works on how the main camera works
+ // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
+ // camera should be.
+ updateSecondaryCameraViewFrustum();
}
quint64 now = usecTimestampNow();
@@ -5541,18 +5705,31 @@ void Application::update(float deltaTime) {
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("queryOctree");
QMutexLocker viewLocker(&_viewMutex);
- quint64 sinceLastQuery = now - _lastQueriedTime;
- const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
- bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
- bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
+
+ bool viewIsDifferentEnough = false;
+ if (_conicalViews.size() == _lastQueriedViews.size()) {
+ for (size_t i = 0; i < _conicalViews.size(); ++i) {
+ if (!_conicalViews[i].isVerySimilar(_lastQueriedViews[i])) {
+ viewIsDifferentEnough = true;
+ break;
+ }
+ }
+ } else {
+ viewIsDifferentEnough = true;
+ }
+
+
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
- if (queryIsDue || viewIsDifferentEnough) {
- _lastQueriedTime = now;
+ static const std::chrono::seconds MIN_PERIOD_BETWEEN_QUERIES { 3 };
+ auto now = SteadyClock::now();
+ if (now > _queryExpiry || viewIsDifferentEnough) {
if (DependencyManager::get()->shouldRenderEntities()) {
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
}
- sendAvatarViewFrustum();
- _lastQueriedViewFrustum = _viewFrustum;
+ queryAvatars();
+
+ _lastQueriedViews = _conicalViews;
+ _queryExpiry = now + MIN_PERIOD_BETWEEN_QUERIES;
}
}
@@ -5724,10 +5901,20 @@ void Application::update(float deltaTime) {
}
}
-void Application::sendAvatarViewFrustum() {
- QByteArray viewFrustumByteArray = _viewFrustum.toByteArray();
- auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
- avatarPacket->write(viewFrustumByteArray);
+void Application::queryAvatars() {
+ auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
+ auto destinationBuffer = reinterpret_cast(avatarPacket->getPayload());
+ unsigned char* bufferStart = destinationBuffer;
+
+ uint8_t numFrustums = (uint8_t)_conicalViews.size();
+ memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
+ destinationBuffer += sizeof(numFrustums);
+
+ for (const auto& view : _conicalViews) {
+ destinationBuffer += view.serialize(destinationBuffer);
+ }
+
+ avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
DependencyManager::get()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
}
@@ -5789,16 +5976,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
return; // bail early if settings are not loaded
}
- ViewFrustum viewFrustum;
- copyViewFrustum(viewFrustum);
- _octreeQuery.setCameraPosition(viewFrustum.getPosition());
- _octreeQuery.setCameraOrientation(viewFrustum.getOrientation());
- _octreeQuery.setCameraFov(viewFrustum.getFieldOfView());
- _octreeQuery.setCameraAspectRatio(viewFrustum.getAspectRatio());
- _octreeQuery.setCameraNearClip(viewFrustum.getNearClip());
- _octreeQuery.setCameraFarClip(viewFrustum.getFarClip());
- _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
- _octreeQuery.setCameraCenterRadius(viewFrustum.getCenterRadius());
+ _octreeQuery.setConicalViews(_conicalViews);
+
auto lodManager = DependencyManager::get();
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
@@ -6007,7 +6186,7 @@ void Application::nodeActivated(SharedNodePointer node) {
// If we get a new EntityServer activated, reset lastQueried time
// so we will do a proper query during update
if (node->getType() == NodeType::EntityServer) {
- _lastQueriedTime = 0;
+ _queryExpiry = SteadyClock::now();
_octreeQuery.incrementConnectionID();
}
@@ -6016,6 +6195,8 @@ void Application::nodeActivated(SharedNodePointer node) {
}
if (node->getType() == NodeType::AvatarMixer) {
+ _queryExpiry = SteadyClock::now();
+
// new avatar mixer, send off our identity packet on next update loop
// Reset skeletonModelUrl if the last server modified our choice.
// Override the avatar url (but not model name) here too.
@@ -7331,7 +7512,7 @@ void Application::loadAvatarBrowser() const {
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
// Get a screenshot and save it
- QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename);
+ QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, testSnapshotLocation);
// If we're not doing an animated snapshot as well...
if (!includeAnimated) {
// Tell the dependency manager that the capture of the still snapshot has taken place.
@@ -7345,7 +7526,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
postLambdaEvent([filename, this] {
- QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename);
+ QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, testSnapshotLocation);
emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, true);
});
}
@@ -7828,6 +8009,26 @@ void Application::switchDisplayMode() {
_previousHMDWornStatus = currentHMDWornStatus;
}
+void Application::setShowBulletWireframe(bool value) {
+ _physicsEngine->setShowBulletWireframe(value);
+}
+
+void Application::setShowBulletAABBs(bool value) {
+ _physicsEngine->setShowBulletAABBs(value);
+}
+
+void Application::setShowBulletContactPoints(bool value) {
+ _physicsEngine->setShowBulletContactPoints(value);
+}
+
+void Application::setShowBulletConstraints(bool value) {
+ _physicsEngine->setShowBulletConstraints(value);
+}
+
+void Application::setShowBulletConstraintLimits(bool value) {
+ _physicsEngine->setShowBulletConstraintLimits(value);
+}
+
void Application::startHMDStandBySession() {
_autoSwitchDisplayModeSupportedHMDPlugin->startStandBySession();
}
@@ -8009,4 +8210,29 @@ void Application::saveNextPhysicsStats(QString filename) {
_physicsEngine->saveNextPhysicsStats(filename);
}
+void Application::openAndroidActivity(const QString& activityName) {
+#if defined(Q_OS_ANDROID)
+ AndroidHelper::instance().requestActivity(activityName);
+#endif
+}
+
+#if defined(Q_OS_ANDROID)
+void Application::enterBackground() {
+ QMetaObject::invokeMethod(DependencyManager::get().data(),
+ "stop", Qt::BlockingQueuedConnection);
+ //GC: commenting it out until we fix it
+ //getActiveDisplayPlugin()->deactivate();
+}
+void Application::enterForeground() {
+ QMetaObject::invokeMethod(DependencyManager::get().data(),
+ "start", Qt::BlockingQueuedConnection);
+ //GC: commenting it out until we fix it
+ /*if (!getActiveDisplayPlugin() || !getActiveDisplayPlugin()->activate()) {
+ qWarning() << "Could not re-activate display plugin";
+ }*/
+
+}
+#endif
+
+#include "Application_jni.cpp"
#include "Application.moc"
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 6d611bc8e2..aa6469c592 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -47,6 +47,7 @@
#include
#include
#include
+#include
#include
#include
@@ -110,7 +111,8 @@ class Application : public QApplication,
public AbstractViewStateInterface,
public AbstractScriptingServicesInterface,
public AbstractUriHandler,
- public PluginContainer {
+ public PluginContainer
+{
Q_OBJECT
// TODO? Get rid of those
@@ -149,6 +151,8 @@ public:
void initializeRenderEngine();
void initializeUi();
+ void updateSecondaryCameraViewFrustum();
+
void updateCamera(RenderArgs& renderArgs, float deltaTime);
void paintGL();
void resizeGL();
@@ -178,6 +182,9 @@ public:
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
+
+ const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; }
+
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
QSharedPointer getEntities() const { return DependencyManager::get(); }
QUndoStack* getUndoStack() { return &_undoStack; }
@@ -243,6 +250,7 @@ public:
bool isAboutToQuit() const { return _aboutToQuit; }
bool isPhysicsEnabled() const { return _physicsEnabled; }
+ PhysicsEnginePointer getPhysicsEngine() { return _physicsEngine; }
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
// rendering of several elements depend on that
@@ -290,6 +298,14 @@ public:
void replaceDomainContent(const QString& url);
+ void loadAvatarScripts(const QVector& urls);
+ void unloadAvatarScripts();
+
+#if defined(Q_OS_ANDROID)
+ void enterBackground();
+ void enterForeground();
+#endif
+
signals:
void svoImportRequested(const QString& url);
@@ -400,6 +416,8 @@ public slots:
Q_INVOKABLE bool askBeforeSetAvatarUrl(const QString& avatarUrl) { return askToSetAvatarUrl(avatarUrl); }
void updateVerboseLogging();
+ Q_INVOKABLE void openAndroidActivity(const QString& activityName);
+
private slots:
void onDesktopRootItemCreated(QQuickItem* qmlContext);
@@ -453,6 +471,12 @@ private slots:
void handleSandboxStatus(QNetworkReply* reply);
void switchDisplayMode();
+ void setShowBulletWireframe(bool value);
+ void setShowBulletAABBs(bool value);
+ void setShowBulletContactPoints(bool value);
+ void setShowBulletConstraints(bool value);
+ void setShowBulletConstraintLimits(bool value);
+
private:
void init();
bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event);
@@ -469,9 +493,9 @@ private:
void updateDialogs(float deltaTime) const;
void queryOctree(NodeType_t serverType, PacketType packetType);
+ void queryAvatars();
int sendNackPackets();
- void sendAvatarViewFrustum();
std::shared_ptr getMyAvatar() const;
@@ -552,9 +576,14 @@ private:
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
- ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
ViewFrustum _displayViewFrustum;
- quint64 _lastQueriedTime;
+
+ ConicalViewFrustums _conicalViews;
+ ConicalViewFrustums _lastQueriedViews; // last views used to query servers
+
+ using SteadyClock = std::chrono::steady_clock;
+ using TimePoint = SteadyClock::time_point;
+ TimePoint _queryExpiry;
OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
@@ -721,5 +750,8 @@ private:
std::atomic _pendingIdleEvent { true };
std::atomic _pendingRenderEvent { true };
+
+ QString testSnapshotLocation;
+ bool quitWhenFinished { false };
};
#endif // hifi_Application_h
diff --git a/interface/src/Application_jni.cpp b/interface/src/Application_jni.cpp
new file mode 100644
index 0000000000..5e9f1ac29e
--- /dev/null
+++ b/interface/src/Application_jni.cpp
@@ -0,0 +1,24 @@
+#if defined(Q_OS_ANDROID)
+
+#include
+#include "AndroidHelper.h"
+
+extern "C" {
+
+JNIEXPORT void
+Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterBackground(JNIEnv *env, jobject obj) {
+ if (qApp) {
+ qApp->enterBackground();
+ }
+}
+
+JNIEXPORT void
+Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterForeground(JNIEnv *env, jobject obj) {
+ if (qApp) {
+ qApp->enterForeground();
+ }
+}
+
+
+}
+#endif
\ No newline at end of file
diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp
index 7845158a80..8e15de673f 100644
--- a/interface/src/AvatarBookmarks.cpp
+++ b/interface/src/AvatarBookmarks.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "AvatarBookmarks.h"
+
#include
#include
#include
@@ -27,7 +29,6 @@
#include "MainWindow.h"
#include "Menu.h"
-#include "AvatarBookmarks.h"
#include "InterfaceLogging.h"
#include "QVariantGLM.h"
diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h
index 177e6e493e..7b47ea8af7 100644
--- a/interface/src/AvatarBookmarks.h
+++ b/interface/src/AvatarBookmarks.h
@@ -18,6 +18,10 @@
/**jsdoc
* This API helps manage adding and deleting avatar bookmarks.
* @namespace AvatarBookmarks
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
*/
class AvatarBookmarks: public Bookmarks, public Dependency {
diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp
index f48b5e1f5b..6e99b81e50 100644
--- a/interface/src/Bookmarks.cpp
+++ b/interface/src/Bookmarks.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "Bookmarks.h"
+
#include
#include
#include
@@ -22,8 +24,6 @@
#include "Menu.h"
#include "InterfaceLogging.h"
-#include "Bookmarks.h"
-
Bookmarks::Bookmarks() :
_isMenuSorted(false)
{
diff --git a/interface/src/Crashpad.cpp b/interface/src/Crashpad.cpp
index e39cd42d81..45f1d0778f 100644
--- a/interface/src/Crashpad.cpp
+++ b/interface/src/Crashpad.cpp
@@ -11,6 +11,8 @@
#include "Crashpad.h"
+#include
+
#include
#if HAS_CRASHPAD
@@ -20,7 +22,7 @@
#include
#include
-#include
+#include
#include
#include
@@ -28,28 +30,27 @@
#include
#include
+#include
+
using namespace crashpad;
static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL };
static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
-static std::wstring gIPCPipe;
-
extern QString qAppFileName();
+CrashpadClient* client { nullptr };
std::mutex annotationMutex;
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
-#include
-
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
+ if (!client) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) {
- CrashpadClient client;
- if (gIPCPipe.length()) {
- client.SetHandlerIPCPipe(gIPCPipe);
- }
- client.DumpAndCrash(pExceptionInfo);
+ client->DumpAndCrash(pExceptionInfo);
}
return EXCEPTION_CONTINUE_SEARCH;
@@ -60,7 +61,8 @@ bool startCrashHandler() {
return false;
}
- CrashpadClient client;
+ assert(!client);
+ client = new CrashpadClient();
std::vector arguments;
std::map annotations;
@@ -96,12 +98,9 @@ bool startCrashHandler() {
// Enable automated uploads.
database->GetSettings()->SetUploadsEnabled(true);
- bool result = client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
- gIPCPipe = client.GetHandlerIPCPipe();
-
AddVectoredExceptionHandler(0, vectoredExceptionHandler);
- return result;
+ return client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
}
void setCrashAnnotation(std::string name, std::string value) {
diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp
index b3c059de7f..684539145e 100644
--- a/interface/src/DiscoverabilityManager.cpp
+++ b/interface/src/DiscoverabilityManager.cpp
@@ -9,7 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "DiscoverabilityManager.h"
+
#include
+#include
#include
#include
@@ -21,11 +24,8 @@
#include
#include "Crashpad.h"
-#include "DiscoverabilityManager.h"
#include "Menu.h"
-#include
-
const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Connections;
DiscoverabilityManager::DiscoverabilityManager() :
diff --git a/interface/src/DiscoverabilityManager.h b/interface/src/DiscoverabilityManager.h
index 96190b25d9..0c62ad5663 100644
--- a/interface/src/DiscoverabilityManager.h
+++ b/interface/src/DiscoverabilityManager.h
@@ -12,9 +12,13 @@
#ifndef hifi_DiscoverabilityManager_h
#define hifi_DiscoverabilityManager_h
+#include
+
#include
#include
+class QNetworkReply;
+
namespace Discoverability {
enum Mode {
None,
diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp
index ec96f7c5d4..0d087c9b48 100644
--- a/interface/src/GLCanvas.cpp
+++ b/interface/src/GLCanvas.cpp
@@ -9,10 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-// FIXME ordering of headers
-#include "Application.h"
#include "GLCanvas.h"
+#include "Application.h"
+
bool GLCanvas::event(QEvent* event) {
if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) {
return true;
diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp
index b7861b56c8..e0d912b252 100644
--- a/interface/src/InterfaceDynamicFactory.cpp
+++ b/interface/src/InterfaceDynamicFactory.cpp
@@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-
+#include "InterfaceDynamicFactory.h"
#include
#include
@@ -22,9 +22,6 @@
#include
#include
-#include "InterfaceDynamicFactory.h"
-
-
EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case DYNAMIC_TYPE_NONE:
diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp
index 14088bc716..b9be58f04b 100644
--- a/interface/src/InterfaceParentFinder.cpp
+++ b/interface/src/InterfaceParentFinder.cpp
@@ -9,13 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "InterfaceParentFinder.h"
+
#include
+#include
+#include
#include
#include
-#include
-#include
-
-#include "InterfaceParentFinder.h"
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const {
SpatiallyNestableWeakPointer parent;
diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp
index d7d73e962a..d06ba14bcf 100644
--- a/interface/src/LODManager.cpp
+++ b/interface/src/LODManager.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "LODManager.h"
+
#include
#include
#include
@@ -17,8 +19,6 @@
#include "ui/DialogsManager.h"
#include "InterfaceLogging.h"
-#include "LODManager.h"
-
Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h
index 96d92e91e9..8f88da63a8 100644
--- a/interface/src/LODManager.h
+++ b/interface/src/LODManager.h
@@ -37,6 +37,10 @@ class AABox;
/**jsdoc
* The LOD class manages your Level of Detail functions within Interface.
* @namespace LODManager
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {number} presentTime Read-only.
* @property {number} engineRunTime Read-only.
* @property {number} gpuTime Read-only.
diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp
index 285f533a7f..8415c84282 100644
--- a/interface/src/LocationBookmarks.cpp
+++ b/interface/src/LocationBookmarks.cpp
@@ -9,21 +9,16 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "LocationBookmarks.h"
+
#include
-#include
-#include
#include
#include
-#include
#include
-#include "MainWindow.h"
#include "Menu.h"
-#include "LocationBookmarks.h"
-#include
-
const QString LocationBookmarks::HOME_BOOKMARK = "Home";
LocationBookmarks::LocationBookmarks() {
diff --git a/interface/src/LocationBookmarks.h b/interface/src/LocationBookmarks.h
index 9a800ba35e..39abea9ba4 100644
--- a/interface/src/LocationBookmarks.h
+++ b/interface/src/LocationBookmarks.h
@@ -13,6 +13,7 @@
#define hifi_LocationBookmarks_h
#include
+
#include "Bookmarks.h"
class LocationBookmarks : public Bookmarks, public Dependency {
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 50ff65ad1a..bf0fc05350 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "Menu.h"
+
#include
#include
#include
@@ -52,8 +54,6 @@
#include "SpeechRecognizer.h"
#endif
-#include "Menu.h"
-
extern bool DEV_DECIMATE_TEXTURES;
Menu* Menu::getInstance() {
@@ -735,6 +735,12 @@ Menu::Menu() {
}
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls, 0, false, qApp->getEntities().data(), SIGNAL(setRenderDebugHulls()));
+ addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletWireframe, 0, false, qApp, SLOT(setShowBulletWireframe(bool)));
+ addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletAABBs, 0, false, qApp, SLOT(setShowBulletAABBs(bool)));
+ addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletContactPoints, 0, false, qApp, SLOT(setShowBulletContactPoints(bool)));
+ addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool)));
+ addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool)));
+
// Developer > Ask to Reset Settings
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false);
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index c8c8ee42df..20375a71b2 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -143,6 +143,11 @@ namespace MenuOption {
const QString PhysicsShowHulls = "Draw Collision Shapes";
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
const QString VerboseLogging = "Verbose Logging";
+ const QString PhysicsShowBulletWireframe = "Show Bullet Collision";
+ const QString PhysicsShowBulletAABBs = "Show Bullet Bounding Boxes";
+ const QString PhysicsShowBulletContactPoints = "Show Bullet Contact Points";
+ const QString PhysicsShowBulletConstraints = "Show Bullet Constraints";
+ const QString PhysicsShowBulletConstraintLimits = "Show Bullet Constraint Limits";
const QString PipelineWarnings = "Log Render Pipeline Warnings";
const QString Preferences = "General...";
const QString Quit = "Quit";
diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp
index 5f4c7526e0..3a5d92eb8c 100644
--- a/interface/src/ModelPackager.cpp
+++ b/interface/src/ModelPackager.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "ModelPackager.h"
+
#include
#include
#include
@@ -21,8 +23,6 @@
#include "ModelPropertiesDialog.h"
#include "InterfaceLogging.h"
-#include "ModelPackager.h"
-
static const int MAX_TEXTURE_SIZE = 1024;
void copyDirectoryContent(QDir& from, QDir& to) {
@@ -156,9 +156,11 @@ bool ModelPackager::zipModel() {
QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray();
tempDir.mkpath(nameField + "/textures");
+ tempDir.mkpath(nameField + "/scripts");
QDir fbxDir(tempDir.path() + "/" + nameField);
QDir texDir(fbxDir.path() + "/textures");
-
+ QDir scriptDir(fbxDir.path() + "/scripts");
+
// Copy textures
listTextures();
if (!_textures.empty()) {
@@ -166,6 +168,23 @@ bool ModelPackager::zipModel() {
_texDir = _modelFile.path() + "/" + texdirField;
copyTextures(_texDir, texDir);
}
+
+ // Copy scripts
+ QByteArray scriptField = _mapping.value(SCRIPT_FIELD).toByteArray();
+ _mapping.remove(SCRIPT_FIELD);
+ if (scriptField.size() > 1) {
+ tempDir.mkpath(nameField + "/scripts");
+ _scriptDir = _modelFile.path() + "/" + scriptField;
+ QDir wdir = QDir(_scriptDir);
+ _mapping.remove(SCRIPT_FIELD);
+ wdir.setSorting(QDir::Name | QDir::Reversed);
+ auto list = wdir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries);
+ for (auto script : list) {
+ auto sc = tempDir.relativeFilePath(scriptDir.path()) + "/" + QUrl(script).fileName();
+ _mapping.insertMulti(SCRIPT_FIELD, sc);
+ }
+ copyDirectoryContent(wdir, scriptDir);
+ }
// Copy LODs
QVariantHash lodField = _mapping.value(LOD_FIELD).toHash();
@@ -189,7 +208,10 @@ bool ModelPackager::zipModel() {
// Correct FST
_mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath);
_mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path());
-
+
+ for (auto multi : _mapping.values(SCRIPT_FIELD)) {
+ multi.fromValue(tempDir.relativeFilePath(scriptDir.path()) + multi.toString());
+ }
// Copy FST
QFile fst(tempDir.path() + "/" + nameField + ".fst");
if (fst.open(QIODevice::WriteOnly)) {
@@ -237,7 +259,9 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
if (!mapping.contains(TEXDIR_FIELD)) {
mapping.insert(TEXDIR_FIELD, ".");
}
-
+ if (!mapping.contains(SCRIPT_FIELD)) {
+ mapping.insert(SCRIPT_FIELD, ".");
+ }
// mixamo/autodesk defaults
if (!mapping.contains(SCALE_FIELD)) {
mapping.insert(SCALE_FIELD, 1.0);
diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h
index 10942833f9..76295e5a85 100644
--- a/interface/src/ModelPackager.h
+++ b/interface/src/ModelPackager.h
@@ -12,11 +12,15 @@
#ifndef hifi_ModelPackager_h
#define hifi_ModelPackager_h
+#include
+
#include
#include
#include "ui/ModelsBrowser.h"
+class FBXGeometry;
+
class ModelPackager : public QObject {
public:
static bool package();
@@ -37,10 +41,12 @@ private:
QFileInfo _fbxInfo;
FSTReader::ModelType _modelType;
QString _texDir;
+ QString _scriptDir;
QVariantHash _mapping;
std::unique_ptr _geometry;
QStringList _textures;
+ QStringList _scripts;
};
diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp
index ae352974ae..8984f89d07 100644
--- a/interface/src/ModelPropertiesDialog.cpp
+++ b/interface/src/ModelPropertiesDialog.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "ModelPropertiesDialog.h"
+
#include
#include
#include
@@ -23,8 +25,6 @@
#include
#include
-#include "ModelPropertiesDialog.h"
-
ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping,
const QString& basePath, const FBXGeometry& geometry) :
@@ -43,6 +43,9 @@ _geometry(geometry)
form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
+ form->addRow("Script Directory:", _scriptDirectory = new QPushButton());
+ connect(_scriptDirectory, SIGNAL(clicked(bool)), SLOT(chooseScriptDirectory()));
+
form->addRow("Scale:", _scale = new QDoubleSpinBox());
_scale->setMaximum(FLT_MAX);
_scale->setSingleStep(0.01);
@@ -100,6 +103,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
mapping.insert(TYPE_FIELD, getType());
mapping.insert(NAME_FIELD, _name->text());
mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
+ mapping.insert(SCRIPT_FIELD, _scriptDirectory->text());
mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
// update the joint indices
@@ -157,6 +161,7 @@ void ModelPropertiesDialog::reset() {
_name->setText(_originalMapping.value(NAME_FIELD).toString());
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
+ _scriptDirectory->setText(_originalMapping.value(SCRIPT_FIELD).toString());
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
@@ -207,6 +212,20 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
}
+void ModelPropertiesDialog::chooseScriptDirectory() {
+ QString directory = QFileDialog::getExistingDirectory(this, "Choose Script Directory",
+ _basePath + "/" + _scriptDirectory->text());
+ if (directory.isEmpty()) {
+ return;
+ }
+ if (!directory.startsWith(_basePath)) {
+ OffscreenUi::asyncWarning(NULL, "Invalid script directory", "Script directory must be child of base path.");
+ return;
+ }
+ _scriptDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
+}
+
+
void ModelPropertiesDialog::updatePivotJoint() {
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
}
diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h
index 11abc5ab54..e3c2d8ed6a 100644
--- a/interface/src/ModelPropertiesDialog.h
+++ b/interface/src/ModelPropertiesDialog.h
@@ -37,6 +37,7 @@ public:
private slots:
void reset();
void chooseTextureDirectory();
+ void chooseScriptDirectory();
void updatePivotJoint();
void createNewFreeJoint(const QString& joint = QString());
@@ -52,6 +53,7 @@ private:
FBXGeometry _geometry;
QLineEdit* _name = nullptr;
QPushButton* _textureDirectory = nullptr;
+ QPushButton* _scriptDirectory = nullptr;
QDoubleSpinBox* _scale = nullptr;
QDoubleSpinBox* _translationX = nullptr;
QDoubleSpinBox* _translationY = nullptr;
diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp
index 2f85849fbe..7d91359a11 100644
--- a/interface/src/ModelSelector.cpp
+++ b/interface/src/ModelSelector.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "ModelSelector.h"
+
#include