diff --git a/android/app/build.gradle b/android/app/build.gradle index b2b3bcd772..70f7c622a0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -104,6 +104,11 @@ dependencies { 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' + compile 'com.android.support:cardview-v7:26.1.0' + + compile 'com.squareup.retrofit2:retrofit:2.4.0' + compile 'com.squareup.retrofit2:converter-gson:2.4.0' + implementation 'com.squareup.picasso:picasso:2.71828' compile 'com.squareup.retrofit2:retrofit:2.4.0' compile 'com.squareup.retrofit2:converter-gson:2.4.0' diff --git a/android/app/src/main/assets/hifi_domains.json b/android/app/src/main/assets/hifi_domains.json deleted file mode 100644 index a63ef7b6aa..0000000000 --- a/android/app/src/main/assets/hifi_domains.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "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 bdbbe4193f..57c6590660 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -21,6 +21,7 @@ #include #include "AndroidHelper.h" +#include QAndroidJniObject __interfaceActivity; QAndroidJniObject __loginCompletedListener; @@ -205,6 +206,10 @@ JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_getCurren return env->NewStringUTF(str.toLatin1().data()); } +JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_protocolVersionSignature(JNIEnv *env, jobject instance) { + return env->NewStringUTF(protocolVersionsSignatureBase64().toLatin1().data()); +} + JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance, jstring username_, jstring password_, diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java b/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java index 408929c20f..94378529f0 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java @@ -23,12 +23,29 @@ public class HifiUtils { return instance; } + public String sanitizeHifiUrl(String urlString) { + urlString = urlString.trim(); + if (!urlString.isEmpty()) { + URI uri; + try { + uri = new URI(urlString); + } catch (URISyntaxException e) { + return urlString; + } + if (uri.getScheme() == null || uri.getScheme().isEmpty()) { + urlString = "hifi://" + urlString; + } + } + return urlString; + } + + public String absoluteHifiAssetUrl(String urlString) { return absoluteHifiAssetUrl(urlString, METAVERSE_BASE_URL); } public String absoluteHifiAssetUrl(String urlString, String baseUrl) { - urlString = urlString.trim(); + urlString = urlString.trim(); if (!urlString.isEmpty()) { URI uri; try { @@ -45,4 +62,6 @@ public class HifiUtils { public native String getCurrentAddress(); + public native String protocolVersionSignature(); + } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index b3edd59acc..7f849ec783 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -230,5 +230,4 @@ public class InterfaceActivity extends QtActivity { public void onBackPressed() { openAndroidActivity("Home", false); } - } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 5fb93ba099..1ecd1e193c 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -29,7 +29,6 @@ import android.widget.TextView; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; -import io.highfidelity.hifiinterface.fragment.GotoFragment; import io.highfidelity.hifiinterface.fragment.HomeFragment; import io.highfidelity.hifiinterface.fragment.LoginFragment; import io.highfidelity.hifiinterface.fragment.PolicyFragment; @@ -37,8 +36,7 @@ import io.highfidelity.hifiinterface.task.DownloadProfileImageTask; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, LoginFragment.OnLoginInteractionListener, - HomeFragment.OnHomeInteractionListener, - GotoFragment.OnGotoInteractionListener { + HomeFragment.OnHomeInteractionListener { private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar; public static final String DEFAULT_FRAGMENT = "Home"; @@ -125,12 +123,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadFragment(fragment, getString(R.string.login), true); } - private void loadGotoFragment() { - Fragment fragment = GotoFragment.newInstance(); - - loadFragment(fragment, getString(R.string.go_to), true); - } - private void loadPrivacyPolicyFragment() { Fragment fragment = PolicyFragment.newInstance(); @@ -204,9 +196,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case R.id.action_home: loadHomeFragment(); return true; - case R.id.action_goto: - loadGotoFragment(); - return true; } return false; } @@ -226,10 +215,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On updateLoginMenu(); } - public void onEnteredDomain(String domainUrl) { - goToDomain(domainUrl); - } - public void onSelectedDomain(String domainUrl) { goToDomain(domainUrl); } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java index 484673bf4f..d8e2606219 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java @@ -3,6 +3,7 @@ package io.highfidelity.hifiinterface; import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; import android.view.View; public class SplashActivity extends Activity { @@ -36,7 +37,10 @@ public class SplashActivity extends Activity { } public void onAppLoadedComplete() { - startActivity(new Intent(this, MainActivity.class)); - finish(); + // Give interface more time so textures don't fail(got deleted) on Adreno (joystick) + new Handler(getMainLooper()).postDelayed(() -> { + startActivity(new Intent(this, MainActivity.class)); + new Handler(getMainLooper()).postDelayed(() -> SplashActivity.this.finish(), 1000); + }, 500); } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/GotoFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/GotoFragment.java deleted file mode 100644 index 9c47a5cc78..0000000000 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/GotoFragment.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.highfidelity.hifiinterface.fragment; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.support.v7.widget.AppCompatButton; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; - -import java.net.URI; -import java.net.URISyntaxException; - -import io.highfidelity.hifiinterface.HifiUtils; -import io.highfidelity.hifiinterface.R; - -public class GotoFragment extends Fragment { - - private EditText mUrlEditText; - private AppCompatButton mGoBtn; - - private OnGotoInteractionListener mListener; - - public GotoFragment() { - // Required empty public constructor - } - - public static GotoFragment newInstance() { - GotoFragment fragment = new GotoFragment(); - return fragment; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_goto, container, false); - - mUrlEditText = rootView.findViewById(R.id.url_text); - mUrlEditText.setOnKeyListener((view, i, keyEvent) -> { - if (i == KeyEvent.KEYCODE_ENTER) { - actionGo(); - return true; - } - return false; - }); - - mUrlEditText.setText(HifiUtils.getInstance().getCurrentAddress()); - - mGoBtn = rootView.findViewById(R.id.go_btn); - - mGoBtn.setOnClickListener(view -> actionGo()); - - return rootView; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof OnGotoInteractionListener) { - mListener = (OnGotoInteractionListener) context; - } else { - throw new RuntimeException(context.toString() - + " must implement OnGotoInteractionListener"); - } - } - - @Override - public void onDetach() { - super.onDetach(); - mListener = null; - } - - 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; - } - - mListener.onEnteredDomain(urlString); - } - } - - public interface OnGotoInteractionListener { - void onEnteredDomain(String domainUrl); - } - -} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java index dd9200138a..606dbca123 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java @@ -2,26 +2,34 @@ package io.highfidelity.hifiinterface.fragment; import android.app.Fragment; import android.content.Context; -import android.graphics.Color; import android.os.Bundle; +import android.os.Handler; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.SearchView; -import android.widget.TabHost; -import android.widget.TabWidget; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.ImageView; import android.widget.TextView; +import io.highfidelity.hifiinterface.HifiUtils; import io.highfidelity.hifiinterface.R; import io.highfidelity.hifiinterface.view.DomainAdapter; public class HomeFragment extends Fragment { - public static final int ENTER_DOMAIN_URL = 1; + private DomainAdapter mDomainAdapter; + private RecyclerView mDomainsView; + private TextView searchNoResultsView; + private ImageView mSearchIconView; + private ImageView mClearSearch; + private EditText mSearchView; - private DomainAdapter domainAdapter; private OnHomeInteractionListener mListener; @@ -46,50 +54,76 @@ public class HomeFragment extends Fragment { Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_home, container, false); - TabHost tabs = rootView.findViewById(R.id.tabhost); - tabs.setup(); + searchNoResultsView = rootView.findViewById(R.id.searchNoResultsView); - 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 mListener.onSelectedDomain(domain.url)); - domainsView.setAdapter(domainAdapter); + mDomainsView.setLayoutManager(gridLayoutMgr); + mDomainAdapter = new DomainAdapter(getContext(), HifiUtils.getInstance().protocolVersionSignature()); + mDomainAdapter.setClickListener((view, position, domain) -> { + new Handler(getActivity().getMainLooper()).postDelayed(() -> mListener.onSelectedDomain(domain.url), 400); // a delay so the ripple effect can be seen + }); + mDomainAdapter.setListener(new DomainAdapter.AdapterListener() { + @Override + public void onEmptyAdapter() { + searchNoResultsView.setText(R.string.search_no_results); + searchNoResultsView.setVisibility(View.VISIBLE); + mDomainsView.setVisibility(View.GONE); + } + + @Override + public void onNonEmptyAdapter() { + searchNoResultsView.setVisibility(View.GONE); + mDomainsView.setVisibility(View.VISIBLE); + } + + @Override + public void onError(Exception e, String message) { + + } + }); + mDomainsView.setAdapter(mDomainAdapter); + + mSearchView = rootView.findViewById(R.id.searchView); + mSearchIconView = rootView.findViewById(R.id.search_mag_icon); + mClearSearch = rootView.findViewById(R.id.search_clear); + + mSearchView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void afterTextChanged(Editable editable) { + mDomainAdapter.loadDomains(editable.toString()); + if (editable.length() > 0) { + mSearchIconView.setVisibility(View.GONE); + mClearSearch.setVisibility(View.VISIBLE); + } else { + mSearchIconView.setVisibility(View.VISIBLE); + mClearSearch.setVisibility(View.GONE); + } + } + }); + mSearchView.setOnKeyListener((view, i, keyEvent) -> { + if (i == KeyEvent.KEYCODE_ENTER) { + String urlString = mSearchView.getText().toString(); + if (!urlString.trim().isEmpty()) { + urlString = HifiUtils.getInstance().sanitizeHifiUrl(urlString); + } + mListener.onSelectedDomain(urlString); + return true; + } + return false; + }); + + mClearSearch.setOnClickListener(view -> onSearchClear(view)); + + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - SearchView searchView = rootView.findViewById(R.id.searchView); - int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null); - View searchPlate = searchView.findViewById(searchPlateId); - if (searchPlate != null) { - searchPlate.setBackgroundColor (Color.TRANSPARENT); - int searchTextId = searchPlate.getContext ().getResources ().getIdentifier ("android:id/search_src_text", null, null); - TextView searchTextView = searchView.findViewById(searchTextId); - searchTextView.setTextAppearance(R.style.SearchText); - } return rootView; } @@ -114,4 +148,9 @@ public class HomeFragment extends Fragment { void onSelectedDomain(String domainUrl); } + public void onSearchClear(View view) { + mSearchView.setText(""); + } + + } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/Callback.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/Callback.java new file mode 100644 index 0000000000..64ca6da816 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/Callback.java @@ -0,0 +1,9 @@ +package io.highfidelity.hifiinterface.provider; + +/** + * Created by cduarte on 4/18/18. + */ + +public interface Callback { + public void callback(T t); +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java new file mode 100644 index 0000000000..7a2101a229 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java @@ -0,0 +1,19 @@ +package io.highfidelity.hifiinterface.provider; + +import java.util.List; + +import io.highfidelity.hifiinterface.view.DomainAdapter; + +/** + * Created by cduarte on 4/17/18. + */ + +public interface DomainProvider { + + void retrieve(String filterText, DomainCallback domainCallback); + + interface DomainCallback { + void retrieveOk(List domain); + void retrieveError(Exception e, String message); + } +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java new file mode 100644 index 0000000000..1e29734243 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java @@ -0,0 +1,235 @@ +package io.highfidelity.hifiinterface.provider; + +import android.util.Log; +import android.util.MutableInt; + +import java.util.ArrayList; +import java.util.List; + +import io.highfidelity.hifiinterface.HifiUtils; +import io.highfidelity.hifiinterface.view.DomainAdapter; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; +import retrofit2.http.GET; +import retrofit2.http.Query; + +/** + * Created by cduarte on 4/17/18. + */ + +public class UserStoryDomainProvider implements DomainProvider { + + public static final String BASE_URL = "https://metaverse.highfidelity.com/"; + + private static final String INCLUDE_ACTIONS_FOR_PLACES = "concurrency"; + private static final String INCLUDE_ACTIONS_FOR_FULL_SEARCH = "concurrency,announcements,snapshot"; + private static final int MAX_PAGES_TO_GET = 10; + + private String mProtocol; + private Retrofit mRetrofit; + private UserStoryDomainProviderService mUserStoryDomainProviderService; + + private boolean startedToGetFromAPI = false; + private List allStories; // All retrieved stories from the API + private List suggestions; // Filtered places to show + + public UserStoryDomainProvider(String protocol) { + mRetrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + mUserStoryDomainProviderService = mRetrofit.create(UserStoryDomainProviderService.class); + mProtocol = protocol; + allStories = new ArrayList<>(); + suggestions = new ArrayList<>(); + } + + private void fillDestinations(String filterText, DomainCallback domainCallback) { + StoriesFilter filter = new StoriesFilter(filterText); + final MutableInt counter = new MutableInt(0); + allStories.clear(); + getUserStoryPage(1, + e -> { + allStories.subList(counter.value, allStories.size()).forEach(userStory -> { + filter.filterOrAdd(userStory); + }); + if (domainCallback != null) { + domainCallback.retrieveOk(suggestions); //ended + } + }, + a -> { + allStories.forEach(userStory -> { + counter.value++; + filter.filterOrAdd(userStory); + }); + } + ); + } + + private void handleError(String url, Throwable t, Callback restOfPagesCallback) { + restOfPagesCallback.callback(new Exception("Error accessing url [" + url + "]", t)); + } + + private void getUserStoryPage(int pageNumber, Callback restOfPagesCallback, Callback firstPageCallback) { + Call userStories = mUserStoryDomainProviderService.getUserStories( + INCLUDE_ACTIONS_FOR_PLACES, + "open", + true, + mProtocol, + pageNumber); + Log.d("API-USER-STORY-DOMAINS", "Protocol [" + mProtocol + "] include_actions [" + INCLUDE_ACTIONS_FOR_PLACES + "]"); + userStories.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + UserStories data = response.body(); + allStories.addAll(data.user_stories); + if (data.current_page < data.total_pages && data.current_page <= MAX_PAGES_TO_GET) { + if (pageNumber == 1 && firstPageCallback != null) { + firstPageCallback.callback(null); + } + getUserStoryPage(pageNumber + 1, restOfPagesCallback, null); + return; + } + restOfPagesCallback.callback(null); + } + + @Override + public void onFailure(Call call, Throwable t) { + handleError(call.request().url().toString(), t, restOfPagesCallback); + } + }); + } + + private class StoriesFilter { + String[] mWords = new String[]{}; + public StoriesFilter(String filterText) { + mWords = filterText.toUpperCase().split("\\s+"); + } + + private boolean matches(UserStory story) { + if (mWords.length <= 0) { + return true; + } + + for (String word : mWords) { + if (!story.searchText().contains(word)) { + return false; + } + } + + return true; + } + + private void addToSuggestions(UserStory story) { + suggestions.add(story.toDomain()); + } + + public void filterOrAdd(UserStory story) { + if (matches(story)) { + addToSuggestions(story); + } + } + } + + private void filterChoicesByText(String filterText, DomainCallback domainCallback) { + suggestions.clear(); + StoriesFilter storiesFilter = new StoriesFilter(filterText); + allStories.forEach(story -> { + storiesFilter.filterOrAdd(story); + }); + domainCallback.retrieveOk(suggestions); + } + + @Override + public synchronized void retrieve(String filterText, DomainCallback domainCallback) { + if (!startedToGetFromAPI) { + startedToGetFromAPI = true; + fillDestinations(filterText, domainCallback); + } else { + filterChoicesByText(filterText, domainCallback); + } + } + + public void retrieveNot(DomainCallback domainCallback) { + // TODO Call multiple pages + Call userStories = mUserStoryDomainProviderService.getUserStories( + INCLUDE_ACTIONS_FOR_PLACES, + "open", + true, + mProtocol, + 1); + + Log.d("API-USER-STORY-DOMAINS", "Protocol [" + mProtocol + "] include_actions [" + INCLUDE_ACTIONS_FOR_PLACES + "]"); + userStories.enqueue(new retrofit2.Callback() { + + @Override + public void onResponse(Call call, Response response) { + UserStories userStories = response.body(); + if (userStories == null) { + domainCallback.retrieveOk(new ArrayList<>(0)); + } + List domains = new ArrayList<>(userStories.total_entries); + userStories.user_stories.forEach(userStory -> { + domains.add(userStory.toDomain()); + }); + domainCallback.retrieveOk(domains); + } + + @Override + public void onFailure(Call call, Throwable t) { + domainCallback.retrieveError(new Exception(t), t.getMessage()); + } + + }); + } + + public interface UserStoryDomainProviderService { + @GET("api/v1/user_stories") + Call getUserStories(@Query("include_actions") String includeActions, + @Query("restriction") String restriction, + @Query("require_online") boolean requireOnline, + @Query("protocol") String protocol, + @Query("page") int pageNumber); + } + + class UserStory { + public UserStory() {} + String place_name; + String path; + String thumbnail_url; + String place_id; + String domain_id; + private String searchText; + + // New fields? tags, description + + String searchText() { + if (searchText == null) { + searchText = place_name == null? "" : place_name.toUpperCase(); + } + return searchText; + } + DomainAdapter.Domain toDomain() { + // TODO Proper url creation (it can or can't have hifi + // TODO Or use host value from api? + String absoluteThumbnailUrl = HifiUtils.getInstance().absoluteHifiAssetUrl(thumbnail_url); + DomainAdapter.Domain domain = new DomainAdapter.Domain( + place_name, + HifiUtils.getInstance().sanitizeHifiUrl(place_name) + "/" + path, + absoluteThumbnailUrl + ); + return domain; + } + } + + class UserStories { + String status; + int current_page; + int total_pages; + int total_entries; + List user_stories; + } + +} 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 index 1129ec586f..4f6394636f 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -1,6 +1,7 @@ package io.highfidelity.hifiinterface.view; import android.content.Context; +import android.net.Uri; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; @@ -9,6 +10,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.squareup.picasso.Picasso; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -19,69 +22,57 @@ import java.util.ArrayList; import java.util.List; import io.highfidelity.hifiinterface.R; +import io.highfidelity.hifiinterface.provider.DomainProvider; +import io.highfidelity.hifiinterface.provider.UserStoryDomainProvider; /** * 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; - } - } + private String mProtocol; + private UserStoryDomainProvider domainProvider; + private AdapterListener mAdapterListener; // references to our domains private Domain[] mDomains = {}; - public DomainAdapter(Context c) { + public DomainAdapter(Context c, String protocol) { mContext = c; this.mInflater = LayoutInflater.from(mContext); - loadDomains(); + mProtocol = protocol; + domainProvider = new UserStoryDomainProvider(mProtocol); + 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); + public void setListener(AdapterListener adapterListener) { + mAdapterListener = adapterListener; + } - String domainName = jDomain.getString("name"); - String domainUrl = jDomain.getString("url"); - String domainThumb = jDomain.getString("thumbnail"); - - domains.add(new Domain(domainName, domainUrl, domainThumb)); + public void loadDomains(String filterText) { + domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() { + @Override + public void retrieveOk(List domain) { + mDomains = new Domain[domain.size()]; + mDomains = domain.toArray(mDomains); + notifyDataSetChanged(); + if (mDomains.length == 0) { + if (mAdapterListener != null) mAdapterListener.onEmptyAdapter(); + } else { + if (mAdapterListener != null) mAdapterListener.onNonEmptyAdapter(); + } } - 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 void retrieveError(Exception e, String message) { + Log.e("DOMAINS", message, e); + if (mAdapterListener != null) mAdapterListener.onError(e, message); + } + }); } @Override @@ -94,7 +85,10 @@ public class DomainAdapter extends RecyclerView.Adapter - - - - - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_clear.xml b/android/app/src/main/res/drawable/ic_clear.xml new file mode 100644 index 0000000000..94efe2bbdb --- /dev/null +++ b/android/app/src/main/res/drawable/ic_clear.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_search.xml b/android/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000000..099c8ea953 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index 31bed89e91..c9285097b8 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -7,11 +7,13 @@ android:layout_height="match_parent" android:fitsSystemWindows="true"> + + - - + + + android:layout_height="match_parent"> - + - + - - - - - - \ No newline at end of file + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_goto.xml b/android/app/src/main/res/layout/fragment_goto.xml deleted file mode 100644 index fa19707ffe..0000000000 --- a/android/app/src/main/res/layout/fragment_goto.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - diff --git a/android/app/src/main/res/layout/fragment_home.xml b/android/app/src/main/res/layout/fragment_home.xml index 1e41d663ca..9f4322546e 100644 --- a/android/app/src/main/res/layout/fragment_home.xml +++ b/android/app/src/main/res/layout/fragment_home.xml @@ -2,75 +2,72 @@ + tools:context="io.highfidelity.hifiinterface.MainActivity"> - + + + + + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/searchView" + android:layout_marginTop="32dp" + android:text="@string/search_no_results" + android:visibility="gone" + /> - - - - - - - - - - - - - - - - - - - - - - + diff --git a/android/app/src/main/res/menu/menu_navigation.xml b/android/app/src/main/res/menu/menu_navigation.xml index 5d0ecf233b..cf80c84177 100644 --- a/android/app/src/main/res/menu/menu_navigation.xml +++ b/android/app/src/main/res/menu/menu_navigation.xml @@ -5,8 +5,4 @@ android:id="@+id/action_home" android:title="@string/home" /> - diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 28428d1b60..15895e4355 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -15,5 +15,6 @@ #9e9e9e #F2F2F2 #FF7171 + #99000000 #292929 diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index d10b294587..bb5be1c070 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -11,7 +11,30 @@ 12dp 12dp 8dp + 4dp + + + 47.5dp + 11dp + 24dp + 51dp + 19sp + 16dp + 16dp + 22dp + 16dp + 16dp + 22dp + + 163dp + 14dp + 14dp + 2dp + 6dp + 64dp + 56dp 101dp 425dp - \ No newline at end of file + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b84e360e42..a34b480c3c 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,7 +1,6 @@ Interface Home - Go To Open in browser Share link Shared a link @@ -9,9 +8,7 @@ FEATURED POPULAR BOOKMARKS - Go To Type a domain url - Go Username or email Password Login @@ -19,6 +16,9 @@ Forgot password? Username or password incorrect. Logging into High Fidelity + Search for a place by name\u00A0 + Loading places… + No places exist with that name Privacy Policy diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 5b32de2465..308c438fa6 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -42,7 +42,7 @@ diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0b373e511b..42924a8487 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -548,18 +548,18 @@ void Agent::setIsAvatar(bool isAvatar) { if (_isAvatar && !_avatarIdentityTimer) { // set up the avatar timers _avatarIdentityTimer = new QTimer(this); - _avatarViewTimer = new QTimer(this); + _avatarQueryTimer = new QTimer(this); // connect our slot connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket); - connect(_avatarViewTimer, &QTimer::timeout, this, &Agent::sendAvatarViewFrustum); + 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 - _avatarViewTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS); + _avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS); // tell the avatarAudioTimer to start ticking QMetaObject::invokeMethod(&_avatarAudioTimer, "start"); @@ -572,9 +572,9 @@ void Agent::setIsAvatar(bool isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = nullptr; - _avatarViewTimer->stop(); - delete _avatarViewTimer; - _avatarViewTimer = 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 @@ -607,20 +607,26 @@ void Agent::sendAvatarIdentityPacket() { } } -void Agent::sendAvatarViewFrustum() { +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; - auto viewFrustumByteArray = view.toByteArray(); + memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums)); + destinationBuffer += sizeof(numFrustums); - auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size() + sizeof(numFrustums)); - avatarPacket->writePrimitive(numFrustums); - avatarPacket->write(viewFrustumByteArray); + destinationBuffer += conicalView.serialize(destinationBuffer); + + avatarPacket->setPayloadSize(destinationBuffer - bufferStart); DependencyManager::get()->broadcastToNodes(std::move(avatarPacket), { NodeType::AvatarMixer }); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index d144f0bc01..0cdc9e0029 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -97,7 +97,7 @@ private: void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; } void sendAvatarIdentityPacket(); - void sendAvatarViewFrustum(); + void queryAvatars(); QString _scriptContents; QTimer* _scriptRequestTimeout { nullptr }; @@ -107,7 +107,7 @@ private: int _numAvatarSoundSentBytes = 0; bool _isAvatar = false; QTimer* _avatarIdentityTimer = nullptr; - QTimer* _avatarViewTimer = nullptr; + QTimer* _avatarQueryTimer = nullptr; QHash _outgoingScriptAudioSequenceNumbers; AudioGate _audioGate; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 6353a1664f..d74c76032d 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -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,7 +517,7 @@ void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer mess } -void AvatarMixer::handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode) { +void AvatarMixer::handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode) { auto start = usecTimestampNow(); getOrCreateClientData(senderNode); @@ -683,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 a8e5a7c541..d5a8b37742 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -126,17 +126,18 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, } } -void AvatarMixerClientData::readViewFrustumPacket(QByteArray message) { +void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { _currentViewFrustums.clear(); + + auto sourceBuffer = reinterpret_cast(message.constData()); uint8_t numFrustums = 0; - memcpy(&numFrustums, message.constData(), sizeof(numFrustums)); - message.remove(0, sizeof(numFrustums)); + memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums)); + sourceBuffer += sizeof(numFrustums); for (uint8_t i = 0; i < numFrustums; ++i) { - ViewFrustum frustum; - auto bytesRead = frustum.fromByteArray(message); - message.remove(0, bytesRead); + ConicalViewFrustum frustum; + sourceBuffer += frustum.deserialize(sourceBuffer); _currentViewFrustums.push_back(frustum); } @@ -144,8 +145,8 @@ void AvatarMixerClientData::readViewFrustumPacket(QByteArray message) { bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums), - [&](const ViewFrustum& viewFrustum) { - return viewFrustum.boxIntersectsKeyhole(otherAvatarBox); + [&](const ConicalViewFrustum& viewFrustum) { + return viewFrustum.intersects(otherAvatarBox); }); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index f17404b79f..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"; @@ -98,7 +98,7 @@ public: void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other); void ignoreOther(SharedNodePointer self, SharedNodePointer other); - void readViewFrustumPacket(QByteArray message); + void readViewFrustumPacket(const QByteArray& message); bool otherAvatarInView(const AABox& otherAvatarBox); @@ -110,7 +110,7 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } - const ViewFrustums& getViewFrustums() const { return _currentViewFrustums; } + 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; - ViewFrustums _currentViewFrustums; + ConicalViewFrustums _currentViewFrustums; int _recentOtherAvatarsInView { 0 }; int _recentOtherAvatarsOutOfView { 0 }; diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp deleted file mode 100644 index 88dee58f9d..0000000000 --- a/assignment-client/src/entities/EntityPriorityQueue.cpp +++ /dev/null @@ -1,76 +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 ConicalViewFrustum::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 ConicalViewFrustum::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; -} - - -void ConicalView::set(const DiffTraversal::View& view) { - auto size = view.viewFrustums.size(); - _conicalViewFrustums.resize(size); - for (size_t i = 0; i < size; ++i) { - _conicalViewFrustums[i].set(view.viewFrustums[i]); - } -} - -float ConicalView::computePriority(const AACube& cube) const { - if (_conicalViewFrustums.empty()) { - return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; - } - - float priority = PrioritizedEntity::DO_NOT_SEND; - - for (const auto& view : _conicalViewFrustums) { - priority = std::max(priority, view.computePriority(cube)); - } - - return priority; -} diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 2e57f2e00f..f008ef9925 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -107,16 +107,7 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O DiffTraversal::View newView; - - ViewFrustum viewFrustum; - if (nodeData->hasMainViewFrustum()) { - nodeData->copyCurrentMainViewFrustum(viewFrustum); - newView.viewFrustums.push_back(viewFrustum); - } - if (nodeData->hasSecondaryViewFrustum()) { - nodeData->copyCurrentSecondaryViewFrustum(viewFrustum); - newView.viewFrustums.push_back(viewFrustum); - } + newView.viewFrustums = nodeData->getCurrentViews(); int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); newView.lodScaleFactor = powf(2.0f, lodLevelOffset); @@ -141,16 +132,8 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O if (forceRemove) { priority = PrioritizedEntity::FORCE_REMOVE; } else { - bool success = false; - AACube cube = entity->getQueryAACube(success); - if (success) { - const auto& view = _traversal.getCurrentView(); - if (view.intersects(cube) && view.isBigEnough(cube)) { - priority = _conicalView.computePriority(cube); - } - } else { - priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; - } + const auto& view = _traversal.getCurrentView(); + priority = view.computePriority(entity); } if (priority != PrioritizedEntity::DO_NOT_SEND) { @@ -235,11 +218,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En // (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: @@ -251,25 +229,8 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En if (_sendQueue.contains(entity.get())) { return; } - float priority = PrioritizedEntity::DO_NOT_SEND; - - - bool success = false; - AACube cube = entity->getQueryAACube(success); - if (success) { - const auto& view = _traversal.getCurrentView(); - // 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. - if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) && - view.isBigEnough(cube)) { - priority = _conicalView.computePriority(cube); - } - } else { - priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; - } - + const auto& view = _traversal.getCurrentView(); + float priority = view.computePriority(entity); if (priority != PrioritizedEntity::DO_NOT_SEND) { _sendQueue.emplace(entity, priority); @@ -288,21 +249,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En } 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) { - const auto& view = _traversal.getCurrentView(); - // See the DiffTraversal::First case for an explanation of the "entity is too small" check - if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) && - view.isBigEnough(cube)) { - priority = _conicalView.computePriority(cube); - } - } else { - priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; - } + 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 @@ -310,7 +261,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; } - if (priority != PrioritizedEntity::DO_NOT_SEND) { _sendQueue.emplace(entity, priority); } @@ -328,21 +278,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En } 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) { - const auto& view = _traversal.getCurrentView(); - // See the DiffTraversal::First case for an explanation of the "entity is too small" check - if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) && - view.isBigEnough(cube)) { - priority = _conicalView.computePriority(cube); - } - } else { - priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; - } + 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 @@ -350,7 +290,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; } - if (priority != PrioritizedEntity::DO_NOT_SEND) { _sendQueue.emplace(entity, priority); } @@ -463,14 +402,13 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) { if (entity) { if (!_sendQueue.contains(entity.get()) && _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().intersects(cube)) { - _sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true); - } - } else { + 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 9eea98b7fd..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; @@ -51,7 +52,6 @@ private: DiffTraversal _traversal; EntityPriorityQueue _sendQueue; 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/octree/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp index 6d91a134c2..039dbdab78 100644 --- a/assignment-client/src/octree/OctreeHeadlessViewer.cpp +++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp @@ -19,7 +19,10 @@ void OctreeHeadlessViewer::queryOctree() { PacketType packetType = getMyQueryMessageType(); if (_hasViewFrustum) { - _octreeQuery.setMainViewFrustum(_viewFrustum); + ConicalViewFrustums views { _viewFrustum }; + _octreeQuery.setConicalViews(views); + } else { + _octreeQuery.clearConicalViews(); } auto nodeList = DependencyManager::get(); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 40c052659d..482b20272a 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -330,7 +330,7 @@ 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->hasMainViewFrustum() && + (nodeData->hasConicalViews() && (nodeData->getViewFrustumJustStoppedChanging() || nodeData->hasLodChanged())); } 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/Application.cpp b/interface/src/Application.cpp index 21257dffe6..f411bbf7fb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -971,7 +971,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), @@ -5106,7 +5105,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); @@ -5251,10 +5250,10 @@ void Application::updateSecondaryCameraViewFrustum() { auto camera = dynamic_cast(renderConfig->getConfig("SecondaryCamera")); if (!camera || !camera->isEnabled()) { - _hasSecondaryViewFrustum = false; return; } + ViewFrustum secondaryViewFrustum; if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) { auto entityScriptingInterface = DependencyManager::get(); auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId); @@ -5279,36 +5278,37 @@ void Application::updateSecondaryCameraViewFrustum() { // 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); + 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); + 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()); + secondaryViewFrustum.setPosition(entityProperties.getPosition()); + secondaryViewFrustum.setOrientation(entityProperties.getRotation()); } else { - _secondaryViewFrustum.setPosition(camera->position); - _secondaryViewFrustum.setOrientation(camera->orientation); + secondaryViewFrustum.setPosition(camera->position); + secondaryViewFrustum.setOrientation(camera->orientation); } float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight; - _secondaryViewFrustum.setProjection(camera->vFoV, + 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(); - _hasSecondaryViewFrustum = true; + secondaryViewFrustum.calculate(); + + _conicalViews.push_back(secondaryViewFrustum); } static bool domainLoadingInProgress = false; @@ -5665,6 +5665,8 @@ 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. @@ -5678,21 +5680,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); - viewIsDifferentEnough |= _hasSecondaryViewFrustum && !_lastQueriedSecondaryViewFrustum.isVerySimilar(_secondaryViewFrustum); + + 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; - _lastQueriedSecondaryViewFrustum = _secondaryViewFrustum; + queryAvatars(); + + _lastQueriedViews = _conicalViews; + _queryExpiry = now + MIN_PERIOD_BETWEEN_QUERIES; } } @@ -5864,18 +5876,20 @@ void Application::update(float deltaTime) { } } -void Application::sendAvatarViewFrustum() { - uint8_t numFrustums = 1; - QByteArray viewFrustumByteArray = _viewFrustum.toByteArray(); +void Application::queryAvatars() { + auto avatarPacket = NLPacket::create(PacketType::AvatarQuery); + auto destinationBuffer = reinterpret_cast(avatarPacket->getPayload()); + unsigned char* bufferStart = destinationBuffer; - if (_hasSecondaryViewFrustum) { - ++numFrustums; - viewFrustumByteArray += _secondaryViewFrustum.toByteArray(); + uint8_t numFrustums = (uint8_t)_conicalViews.size(); + memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums)); + destinationBuffer += sizeof(numFrustums); + + for (const auto& view : _conicalViews) { + destinationBuffer += view.serialize(destinationBuffer); } - auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size() + sizeof(numFrustums)); - avatarPacket->writePrimitive(numFrustums); - avatarPacket->write(viewFrustumByteArray); + avatarPacket->setPayloadSize(destinationBuffer - bufferStart); DependencyManager::get()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer); } @@ -5937,16 +5951,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { return; // bail early if settings are not loaded } - ViewFrustum viewFrustum; - copyViewFrustum(viewFrustum); - _octreeQuery.setMainViewFrustum(viewFrustum); - - if (hasSecondaryViewFrustum()) { - copySecondaryViewFrustum(viewFrustum); - _octreeQuery.setSecondaryViewFrustum(viewFrustum); - } else { - _octreeQuery.clearSecondaryViewFrustum(); - } + _octreeQuery.setConicalViews(_conicalViews); auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); @@ -6031,11 +6036,6 @@ void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const { viewOut = _displayViewFrustum; } -void Application::copySecondaryViewFrustum(ViewFrustum& viewOut) const { - QMutexLocker viewLocker(&_viewMutex); - viewOut = _secondaryViewFrustum; -} - void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); @@ -6161,7 +6161,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(); } @@ -6170,6 +6170,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. diff --git a/interface/src/Application.h b/interface/src/Application.h index 03847a767a..537533c5b1 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 @@ -175,14 +177,14 @@ public: Camera& getCamera() { return _myCamera; } const Camera& getCamera() const { return _myCamera; } // Represents the current view frustum of the avatar. - void copyViewFrustum(ViewFrustum& viewOut) const override; - void copySecondaryViewFrustum(ViewFrustum& viewOut) const override; - bool hasSecondaryViewFrustum() const override { return _hasSecondaryViewFrustum; } + void copyViewFrustum(ViewFrustum& viewOut) const; // Represents the view frustum of the current rendering pass, // 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; } @@ -487,9 +489,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; @@ -570,12 +572,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 ViewFrustum _displayViewFrustum; - ViewFrustum _secondaryViewFrustum; - ViewFrustum _lastQueriedSecondaryViewFrustum; // last secondary view frustum used to query octree servers - bool _hasSecondaryViewFrustum; - 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 diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 087e23a933..d24618fada 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "Application.h" #include "AvatarManager.h" @@ -156,17 +157,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { }; - ViewFrustums views; - - ViewFrustum view; - qApp->copyCurrentViewFrustum(view); - views.push_back(view); - - if (qApp->hasSecondaryViewFrustum()) { - qApp->copySecondaryViewFrustum(view); - views.push_back(view); - } - + const auto& views = qApp->getConicalViews(); PrioritySortUtil::PriorityQueue sortedAvatars(views, AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 6dd13c7332..600d1c32a8 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -296,8 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r } } -void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustums& views, - render::Transaction& transaction) { +void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction) { PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size()); PerformanceTimer pt("change"); std::unordered_set changedEntities; @@ -358,6 +357,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene // prioritize and sort the renderables uint64_t sortStart = usecTimestampNow(); + + const auto& views = _viewState->getConicalViews(); PrioritySortUtil::PriorityQueue sortedRenderables(views); { PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); @@ -417,19 +418,7 @@ void EntityTreeRenderer::update(bool simulate) { render::Transaction transaction; addPendingEntities(scene, transaction); - ViewFrustums views; - - ViewFrustum view; - _viewState->copyViewFrustum(view); - views.push_back(view); - - if (_viewState->hasSecondaryViewFrustum()) { - _viewState->copySecondaryViewFrustum(view); - views.push_back(view); - } - - - updateChangedEntities(scene, views, transaction); + updateChangedEntities(scene, transaction); scene->enqueueTransaction(transaction); } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 9ed4f9d21d..882ec2fd5b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -148,8 +148,7 @@ protected: private: void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction); - void updateChangedEntities(const render::ScenePointer& scene, const ViewFrustums& views, - render::Transaction& transaction); + void updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction); EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); } render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); } diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp index 39328e11ad..36d1f41267 100644 --- a/libraries/entities/src/DiffTraversal.cpp +++ b/libraries/entities/src/DiffTraversal.cpp @@ -13,6 +13,8 @@ #include +#include "EntityPriorityQueue.h" + DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) { assert(element); _weakElement = element; @@ -35,19 +37,9 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; - if (nextElement) { - const auto& cube = nextElement->getAACube(); - if (!view.usesViewFrustums()) { - // No LOD truncation if we aren't using the view frustum - next.element = nextElement; - return; - } else if (view.intersects(cube)) { - // check for LOD truncation - if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) { - next.element = nextElement; - return; - } - } + if (nextElement && view.shouldTraverseElement(*nextElement)) { + next.element = nextElement; + return; } } } @@ -63,7 +55,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat( EntityTreeElementPointer element = _weakElement.lock(); if (element->getLastChangedContent() > lastTime) { next.element = element; - next.intersection = ViewFrustum::INTERSECT; return; } } @@ -73,30 +64,17 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat( while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; - if (nextElement && nextElement->getLastChanged() > lastTime) { - if (!view.usesViewFrustums()) { - // No LOD truncation if we aren't using the view frustum - next.element = nextElement; - next.intersection = ViewFrustum::INSIDE; - return; - } else { - // check for LOD truncation - const auto& cube = nextElement->getAACube(); - if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) { - ViewFrustum::intersection intersection = view.calculateIntersection(cube); - if (intersection != ViewFrustum::OUTSIDE) { - next.element = nextElement; - next.intersection = intersection; - return; - } - } - } + if (nextElement && + nextElement->getLastChanged() > lastTime && + view.shouldTraverseElement(*nextElement)) { + + next.element = nextElement; + return; } } } } next.element.reset(); - next.intersection = ViewFrustum::OUTSIDE; } void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next, @@ -106,7 +84,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V ++_nextIndex; EntityTreeElementPointer element = _weakElement.lock(); next.element = element; - next.intersection = ViewFrustum::INTERSECT; return; } else if (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer element = _weakElement.lock(); @@ -114,74 +91,14 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; - if (nextElement) { - // check for LOD truncation - const auto& cube = nextElement->getAACube(); - if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) { - ViewFrustum::intersection intersection = view.calculateIntersection(cube); - if (intersection != ViewFrustum::OUTSIDE) { - next.element = nextElement; - next.intersection = intersection; - return; - } - } + if (nextElement && view.shouldTraverseElement(*nextElement)) { + next.element = nextElement; + return; } } } } next.element.reset(); - next.intersection = ViewFrustum::OUTSIDE; -} - -bool DiffTraversal::View::isBigEnough(const AACube& cube, float minDiameter) const { - if (viewFrustums.empty()) { - // Everything is big enough when not using view frustums - return true; - } - - bool isBigEnough = std::any_of(std::begin(viewFrustums), std::end(viewFrustums), - [&](const ViewFrustum& viewFrustum) { - return isAngularSizeBigEnough(viewFrustum.getPosition(), cube, lodScaleFactor, minDiameter); - }); - - return isBigEnough; -} - -bool DiffTraversal::View::intersects(const AACube& cube) const { - if (viewFrustums.empty()) { - // Everything intersects when not using view frustums - return true; - } - - bool intersects = std::any_of(std::begin(viewFrustums), std::end(viewFrustums), - [&](const ViewFrustum& viewFrustum) { - return viewFrustum.cubeIntersectsKeyhole(cube); - }); - - return intersects; -} - -ViewFrustum::intersection DiffTraversal::View::calculateIntersection(const AACube& cube) const { - if (viewFrustums.empty()) { - // Everything is inside when not using view frustums - return ViewFrustum::INSIDE; - } - - ViewFrustum::intersection intersection = ViewFrustum::OUTSIDE; - - for (const auto& viewFrustum : viewFrustums) { - switch (viewFrustum.calculateCubeKeyholeIntersection(cube)) { - case ViewFrustum::INSIDE: - return ViewFrustum::INSIDE; - case ViewFrustum::INTERSECT: - intersection = ViewFrustum::INTERSECT; - break; - default: - // DO NOTHING - break; - } - } - return intersection; } bool DiffTraversal::View::usesViewFrustums() const { @@ -204,6 +121,73 @@ bool DiffTraversal::View::isVerySimilar(const View& view) const { return true; } +float DiffTraversal::View::computePriority(const EntityItemPointer& entity) const { + if (!entity) { + return PrioritizedEntity::DO_NOT_SEND; + } + + if (!usesViewFrustums()) { + return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; + } + + bool success = false; + auto cube = entity->getQueryAACube(success); + if (!success) { + return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; + } + + auto center = cube.calcCenter(); // center of bounding sphere + auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere + + auto priority = PrioritizedEntity::DO_NOT_SEND; + + for (const auto& frustum : viewFrustums) { + auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame + float distance = glm::length(position); // distance to center of bounding sphere + + // 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 angularSize = frustum.getAngularSize(distance, radius); + if (angularSize > lodScaleFactor * MIN_ENTITY_ANGULAR_DIAMETER && + frustum.intersects(position, distance, radius)) { + + // use the angular size as priority + // we compute the max priority for all frustums + priority = std::max(priority, angularSize); + } + } + + return priority; +} + +bool DiffTraversal::View::shouldTraverseElement(const EntityTreeElement& element) const { + if (!usesViewFrustums()) { + return true; + } + + const auto& cube = element.getAACube(); + + auto center = cube.calcCenter(); // center of bounding sphere + auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere + + + return any_of(begin(viewFrustums), end(viewFrustums), [&](const ConicalViewFrustum& frustum) { + auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame + float distance = glm::length(position); // distance to center of bounding sphere + + // 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 angularSize = frustum.getAngularSize(distance, radius); + + return angularSize > lodScaleFactor * MIN_ELEMENT_ANGULAR_DIAMETER && + frustum.intersects(position, distance, radius); + }); +} + DiffTraversal::DiffTraversal() { const int32_t MIN_PATH_DEPTH = 16; _path.reserve(MIN_PATH_DEPTH); @@ -262,7 +246,6 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) { if (_path.empty()) { next.element.reset(); - next.intersection = ViewFrustum::OUTSIDE; return; } _getNextVisibleElementCallback(next); diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h index 0fd014ac23..d62c7b8ee1 100644 --- a/libraries/entities/src/DiffTraversal.h +++ b/libraries/entities/src/DiffTraversal.h @@ -12,7 +12,7 @@ #ifndef hifi_DiffTraversal_h #define hifi_DiffTraversal_h -#include +#include #include "EntityTreeElement.h" @@ -24,19 +24,18 @@ public: class VisibleElement { public: EntityTreeElementPointer element; - ViewFrustum::intersection intersection { ViewFrustum::OUTSIDE }; }; // View is a struct with a ViewFrustum and LOD parameters class View { public: - bool isBigEnough(const AACube& cube, float minDiameter = MIN_ENTITY_ANGULAR_DIAMETER) const; - bool intersects(const AACube& cube) const; bool usesViewFrustums() const; bool isVerySimilar(const View& view) const; - ViewFrustum::intersection calculateIntersection(const AACube& cube) const; - ViewFrustums viewFrustums; + bool shouldTraverseElement(const EntityTreeElement& element) const; + float computePriority(const EntityItemPointer& entity) const; + + ConicalViewFrustums viewFrustums; uint64_t startTime { 0 }; float lodScaleFactor { 1.0f }; }; @@ -65,9 +64,6 @@ public: Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root); const View& getCurrentView() const { return _currentView; } - const View& getCompletedView() const { return _completedView; } - - bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustums(); } uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; } bool finished() const { return _path.empty(); } diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/libraries/entities/src/EntityPriorityQueue.h similarity index 70% rename from assignment-client/src/entities/EntityPriorityQueue.h rename to libraries/entities/src/EntityPriorityQueue.h index 9210ac549f..339676d237 100644 --- a/assignment-client/src/entities/EntityPriorityQueue.h +++ b/libraries/entities/src/EntityPriorityQueue.h @@ -15,44 +15,14 @@ #include #include -#include -#include -#include - -const float SQRT_TWO_OVER_TWO = 0.7071067811865f; -const float DEFAULT_VIEW_RADIUS = 10.0f; - -// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority. -class ConicalViewFrustum { -public: - ConicalViewFrustum() {} - ConicalViewFrustum(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 }; -}; - -// Simple wrapper around a set of conical view frustums -class ConicalView { -public: - ConicalView() {} - void set(const DiffTraversal::View& view); - float computePriority(const AACube& cube) const; -private: - std::vector _conicalViewFrustums; -}; +#include "EntityItem.h" // 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; + static constexpr float DO_NOT_SEND { -1.0e6f }; + static constexpr float FORCE_REMOVE { -1.0e5f }; + static constexpr float WHEN_IN_DOUBT_PRIORITY { 1.0f }; PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {} EntityItemPointer getEntity() const { return _weakEntity.lock(); } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 17b0d90cfe..d6ab7d0f9c 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -34,7 +34,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityPhysics: return static_cast(EntityVersion::MaterialData); case PacketType::EntityQuery: - return static_cast(EntityQueryPacketVersion::MultiFrustumQuery); + return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: @@ -90,8 +90,8 @@ PacketVersion versionForPacketType(PacketType packetType) { return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height case PacketType::Ping: return static_cast(PingVersion::IncludeConnectionID); - case PacketType::ViewFrustum: - return static_cast(ViewFrustumVersion::SendMultipleFrustums); + case PacketType::AvatarQuery: + return static_cast(AvatarQueryVersion::ConicalFrustums); default: return 20; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index c72bbb0129..2252b09ae5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -103,7 +103,7 @@ public: RadiusIgnoreRequest, UsernameFromIDRequest, UsernameFromIDReply, - ViewFrustum, + AvatarQuery, RequestsDomainListData, PerAvatarGainSet, EntityScriptGetStatus, @@ -245,7 +245,8 @@ enum class EntityQueryPacketVersion: PacketVersion { JSONFilterWithFamilyTree = 19, ConnectionIdentifier = 20, RemovedJurisdictions = 21, - MultiFrustumQuery = 22 + MultiFrustumQuery = 22, + ConicalFrustums = 23 }; enum class AssetServerPacketVersion: PacketVersion { @@ -328,8 +329,9 @@ enum class PingVersion : PacketVersion { IncludeConnectionID = 18 }; -enum class ViewFrustumVersion : PacketVersion { - SendMultipleFrustums = 21 +enum class AvatarQueryVersion : PacketVersion { + SendMultipleFrustums = 21, + ConicalFrustums = 22 }; #endif // hifi_PacketHeaders_h diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 18e907cb8c..3f730d4458 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -18,10 +18,6 @@ #include #include -using QueryFlags = uint8_t; -const QueryFlags QUERY_HAS_MAIN_FRUSTUM = 1U << 0; -const QueryFlags QUERY_HAS_SECONDARY_FRUSTUM = 1U << 1; - OctreeQuery::OctreeQuery(bool randomizeConnectionID) { if (randomizeConnectionID) { // randomize our initial octree query connection ID using random_device @@ -38,27 +34,13 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID)); destinationBuffer += sizeof(_connectionID); - // flags for wether the frustums are present - QueryFlags frustumFlags = 0; - if (_hasMainFrustum) { - frustumFlags |= QUERY_HAS_MAIN_FRUSTUM; - } - if (_hasSecondaryFrustum) { - frustumFlags |= QUERY_HAS_SECONDARY_FRUSTUM; - } - memcpy(destinationBuffer, &frustumFlags, sizeof(frustumFlags)); - destinationBuffer += sizeof(frustumFlags); + // Number of frustums + uint8_t numFrustums = (uint8_t)_conicalViews.size(); + memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums)); + destinationBuffer += sizeof(numFrustums); - if (_hasMainFrustum) { - auto byteArray = _mainViewFrustum.toByteArray(); - memcpy(destinationBuffer, byteArray.constData(), byteArray.size()); - destinationBuffer += byteArray.size(); - } - - if (_hasSecondaryFrustum) { - auto byteArray = _secondaryViewFrustum.toByteArray(); - memcpy(destinationBuffer, byteArray.constData(), byteArray.size()); - destinationBuffer += byteArray.size(); + for (const auto& view : _conicalViews) { + destinationBuffer += view.serialize(destinationBuffer); } // desired Max Octree PPS @@ -99,7 +81,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { int OctreeQuery::parseData(ReceivedMessage& message) { const unsigned char* startPosition = reinterpret_cast(message.getRawMessage()); - const unsigned char* endPosition = startPosition + message.getSize(); const unsigned char* sourceBuffer = startPosition; // unpack the connection ID @@ -123,23 +104,15 @@ int OctreeQuery::parseData(ReceivedMessage& message) { } // check if this query uses a view frustum - QueryFlags frustumFlags { 0 }; - memcpy(&frustumFlags, sourceBuffer, sizeof(frustumFlags)); - sourceBuffer += sizeof(frustumFlags); + uint8_t numFrustums = 0; + memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums)); + sourceBuffer += sizeof(numFrustums); - _hasMainFrustum = frustumFlags & QUERY_HAS_MAIN_FRUSTUM; - _hasSecondaryFrustum = frustumFlags & QUERY_HAS_SECONDARY_FRUSTUM; - - if (_hasMainFrustum) { - auto bytesLeft = endPosition - sourceBuffer; - auto byteArray = QByteArray::fromRawData(reinterpret_cast(sourceBuffer), bytesLeft); - sourceBuffer += _mainViewFrustum.fromByteArray(byteArray); - } - - if (_hasSecondaryFrustum) { - auto bytesLeft = endPosition - sourceBuffer; - auto byteArray = QByteArray::fromRawData(reinterpret_cast(sourceBuffer), bytesLeft); - sourceBuffer += _secondaryViewFrustum.fromByteArray(byteArray); + _conicalViews.clear(); + for (int i = 0; i < numFrustums; ++i) { + ConicalViewFrustum view; + sourceBuffer += view.deserialize(sourceBuffer); + _conicalViews.push_back(view); } // desired Max Octree PPS diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index ef52e29f51..7dfc1cfaaf 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -16,8 +16,7 @@ #include #include - -#include +#include #include "OctreeConstants.h" @@ -34,15 +33,9 @@ public: int getBroadcastData(unsigned char* destinationBuffer); int parseData(ReceivedMessage& message) override; - bool hasMainViewFrustum() const { return _hasMainFrustum; } - void setMainViewFrustum(const ViewFrustum& viewFrustum) { _hasMainFrustum = true; _mainViewFrustum = viewFrustum; } - void clearMainViewFrustum() { _hasMainFrustum = false; } - const ViewFrustum& getMainViewFrustum() const { return _mainViewFrustum; } - - bool hasSecondaryViewFrustum() const { return _hasSecondaryFrustum; } - void setSecondaryViewFrustum(const ViewFrustum& viewFrustum) { _hasSecondaryFrustum = true; _secondaryViewFrustum = viewFrustum; } - void clearSecondaryViewFrustum() { _hasSecondaryFrustum = false; } - const ViewFrustum& getSecondaryViewFrustum() const { return _secondaryViewFrustum; } + bool hasConicalViews() const { return !_conicalViews.empty(); } + void setConicalViews(ConicalViewFrustums views) { _conicalViews = views; } + void clearConicalViews() { _conicalViews.clear(); } // getters/setters for JSON filter QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; } @@ -67,10 +60,7 @@ public slots: void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; } protected: - bool _hasMainFrustum { false }; - ViewFrustum _mainViewFrustum; - bool _hasSecondaryFrustum { false }; - ViewFrustum _secondaryViewFrustum; + ConicalViewFrustums _conicalViews; // octree server sending items int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS; diff --git a/libraries/octree/src/OctreeQueryNode.cpp b/libraries/octree/src/OctreeQueryNode.cpp index d6e9343896..2d1d89a11c 100644 --- a/libraries/octree/src/OctreeQueryNode.cpp +++ b/libraries/octree/src/OctreeQueryNode.cpp @@ -139,23 +139,13 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by } } -void OctreeQueryNode::copyCurrentMainViewFrustum(ViewFrustum& viewOut) const { - QMutexLocker viewLocker(&_viewMutex); - viewOut = _currentMainViewFrustum; -} - -void OctreeQueryNode::copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const { - QMutexLocker viewLocker(&_viewMutex); - viewOut = _currentSecondaryViewFrustum; -} - bool OctreeQueryNode::updateCurrentViewFrustum() { // if shutting down, return immediately if (_isShuttingDown) { return false; } - if (!_hasMainFrustum && !_hasSecondaryFrustum) { + if (!hasConicalViews()) { // this client does not use a view frustum so the view frustum for this query has not changed return false; } @@ -164,12 +154,17 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { { // if there has been a change, then recalculate QMutexLocker viewLocker(&_viewMutex); - if (_hasMainFrustum && !_mainViewFrustum.isVerySimilar(_currentMainViewFrustum)) { - _currentMainViewFrustum = _mainViewFrustum; - currentViewFrustumChanged = true; - } - if (_hasSecondaryFrustum && !_secondaryViewFrustum.isVerySimilar(_currentSecondaryViewFrustum)) { - _currentSecondaryViewFrustum = _secondaryViewFrustum; + + if (_conicalViews.size() == _currentConicalViews.size()) { + for (size_t i = 0; i < _conicalViews.size(); ++i) { + if (!_conicalViews[i].isVerySimilar(_currentConicalViews[i])) { + _currentConicalViews = _conicalViews; + currentViewFrustumChanged = true; + break; + } + } + } else { + _currentConicalViews = _conicalViews; currentViewFrustumChanged = true; } } diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h index d8f05c4043..d984e048c1 100644 --- a/libraries/octree/src/OctreeQueryNode.h +++ b/libraries/octree/src/OctreeQueryNode.h @@ -14,14 +14,14 @@ #include -#include +#include + #include "OctreeConstants.h" #include "OctreeElementBag.h" #include "OctreePacketData.h" #include "OctreeQuery.h" #include "OctreeSceneStats.h" #include "SentPacketHistory.h" -#include class OctreeSendThread; class OctreeServer; @@ -49,8 +49,7 @@ public: OctreeElementExtraEncodeData extraEncodeData; - void copyCurrentMainViewFrustum(ViewFrustum& viewOut) const; - void copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const; + const ConicalViewFrustums& getCurrentViews() const { return _currentConicalViews; } // These are not classic setters because they are calculating and maintaining state // which is set asynchronously through the network receive @@ -97,8 +96,7 @@ private: quint64 _firstSuppressedPacket { usecTimestampNow() }; mutable QMutex _viewMutex { QMutex::Recursive }; - ViewFrustum _currentMainViewFrustum; - ViewFrustum _currentSecondaryViewFrustum; + ConicalViewFrustums _currentConicalViews; bool _viewFrustumChanging { false }; bool _viewFrustumJustStoppedChanging { true }; diff --git a/libraries/octree/src/OctreeUtils.h b/libraries/octree/src/OctreeUtils.h index 58ab366d8d..dff56cad64 100644 --- a/libraries/octree/src/OctreeUtils.h +++ b/libraries/octree/src/OctreeUtils.h @@ -12,6 +12,8 @@ #ifndef hifi_OctreeUtils_h #define hifi_OctreeUtils_h +#include + #include "OctreeConstants.h" class AABox; @@ -33,7 +35,6 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust // MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians // NOTE: the entity bounding cube is larger than the smallest possible containing octree element by sqrt(3) -const float SQRT_THREE = 1.73205080f; const float MIN_ENTITY_ANGULAR_DIAMETER = MIN_ELEMENT_ANGULAR_DIAMETER * SQRT_THREE; const float MIN_VISIBLE_DISTANCE = 0.0001f; // helps avoid divide-by-zero check diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index 9d781b7d18..b90e291da5 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -24,6 +24,8 @@ class Transform; class QThread; class ViewFrustum; class PickRay; +class ConicalViewFrustum; +using ConicalViewFrustums = std::vector; /// Interface provided by Application to other objects that need access to the current view state details class AbstractViewStateInterface { @@ -31,9 +33,7 @@ public: /// copies the current view frustum for rendering the view state virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0; - virtual void copyViewFrustum(ViewFrustum& viewOut) const = 0; - virtual void copySecondaryViewFrustum(ViewFrustum& viewOut) const = 0; - virtual bool hasSecondaryViewFrustum() const = 0; + virtual const ConicalViewFrustums& getConicalViews() const = 0; virtual QThread* getMainThread() = 0; diff --git a/libraries/render-utils/src/forward_simple.slf b/libraries/render-utils/src/forward_simple.slf index 587fcbde73..1ac44750a7 100644 --- a/libraries/render-utils/src/forward_simple.slf +++ b/libraries/render-utils/src/forward_simple.slf @@ -16,11 +16,21 @@ <@include ForwardGlobalLight.slh@> <$declareEvalSkyboxGlobalColor()$> + // the interpolated normal in vec3 _normalWS; +in vec3 _normalMS; in vec4 _color; +in vec2 _texCoord0; +in vec4 _positionMS; in vec4 _positionES; +// For retro-compatibility +#define _normal _normalWS +#define _modelNormal _normalMS +#define _position _positionMS +#define _eyePosition _positionES + layout(location = 0) out vec4 _fragColor0; //PROCEDURAL_COMMON_BLOCK diff --git a/libraries/render-utils/src/forward_simple_transparent.slf b/libraries/render-utils/src/forward_simple_transparent.slf index f40ba2ed4f..8be2759571 100644 --- a/libraries/render-utils/src/forward_simple_transparent.slf +++ b/libraries/render-utils/src/forward_simple_transparent.slf @@ -18,9 +18,18 @@ // the interpolated normal in vec3 _normalWS; +in vec3 _normalMS; in vec4 _color; +in vec2 _texCoord0; +in vec4 _positionMS; in vec4 _positionES; +// For retro-compatibility +#define _normal _normalWS +#define _modelNormal _normalMS +#define _position _positionMS +#define _eyePosition _positionES + layout(location = 0) out vec4 _fragColor0; //PROCEDURAL_COMMON_BLOCK diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index ed77777184..7591dc1882 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -16,7 +16,17 @@ // the interpolated normal in vec3 _normalWS; +in vec3 _normalMS; in vec4 _color; +in vec2 _texCoord0; +in vec4 _positionMS; +in vec4 _positionES; + +// For retro-compatibility +#define _normal _normalWS +#define _modelNormal _normalMS +#define _position _positionMS +#define _eyePosition _positionES //PROCEDURAL_COMMON_BLOCK diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf index 6e7aee2894..0710c3e10b 100644 --- a/libraries/render-utils/src/simple_fade.slf +++ b/libraries/render-utils/src/simple_fade.slf @@ -19,9 +19,19 @@ // the interpolated normal in vec3 _normalWS; +in vec3 _normalMS; in vec4 _color; +in vec2 _texCoord0; +in vec4 _positionMS; +in vec4 _positionES; in vec4 _positionWS; +// For retro-compatibility +#define _normal _normalWS +#define _modelNormal _normalMS +#define _position _positionMS +#define _eyePosition _positionES + //PROCEDURAL_COMMON_BLOCK #line 1001 diff --git a/libraries/render-utils/src/simple_transparent.slf b/libraries/render-utils/src/simple_transparent.slf index c9815e8a80..ee79d2c0c4 100644 --- a/libraries/render-utils/src/simple_transparent.slf +++ b/libraries/render-utils/src/simple_transparent.slf @@ -16,7 +16,17 @@ // the interpolated normal in vec3 _normalWS; +in vec3 _normalMS; in vec4 _color; +in vec2 _texCoord0; +in vec4 _positionMS; +in vec4 _positionES; + +// For retro-compatibility +#define _normal _normalWS +#define _modelNormal _normalMS +#define _position _positionMS +#define _eyePosition _positionES //PROCEDURAL_COMMON_BLOCK diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index e963345ecd..75446754d5 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -225,6 +225,12 @@ int unpackOrientationQuatFromSixBytes(const unsigned char* buffer, glm::quat& qu return 6; } +bool closeEnough(float a, float b, float relativeError) { + assert(relativeError >= 0.0f); + // NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero. + // This method works fine when: fabsf(a + b) >> EPSILON + return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError; +} // Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's // http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde, diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 5c9a8b5ca1..0e1af27cd2 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -137,6 +137,8 @@ int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, flo int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix); int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix); +bool closeEnough(float a, float b, float relativeError); + /// \return vec3 with euler angles in radians glm::vec3 safeEulerAngles(const glm::quat& q); diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index e2d2409d56..4c24a73226 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -48,6 +48,8 @@ const int BYTES_PER_KILOBYTE = 1000; const int BYTES_PER_KILOBIT = BYTES_PER_KILOBYTE / BITS_IN_BYTE; const int KILO_PER_MEGA = 1000; +const float SQRT_THREE = 1.73205080f; + #define KB_TO_BYTES_SHIFT 10 #define MB_TO_BYTES_SHIFT 20 #define GB_TO_BYTES_SHIFT 30 diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index ba15222611..34ec074d45 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -15,7 +15,7 @@ #include #include "NumericalConstants.h" -#include "ViewFrustum.h" +#include "shared/ConicalViewFrustum.h" /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: @@ -84,12 +84,12 @@ namespace PrioritySortUtil { class PriorityQueue { public: PriorityQueue() = delete; - PriorityQueue(const ViewFrustums& views) : _views(views) { } - PriorityQueue(const ViewFrustums& views, float angularWeight, float centerWeight, float ageWeight) + PriorityQueue(const ConicalViewFrustums& views) : _views(views) { } + PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight) : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) { } - void setViews(const ViewFrustums& views) { _views = views; } + void setViews(const ConicalViewFrustums& views) { _views = views; } void setWeights(float angularWeight, float centerWeight, float ageWeight) { _angularWeight = angularWeight; @@ -118,7 +118,7 @@ namespace PrioritySortUtil { return priority; } - float computePriority(const ViewFrustum& view, const T& thing) const { + float computePriority(const ConicalViewFrustum& view, const T& thing) const { // priority = weighted linear combination of multiple values: // (a) angular size // (b) proximity to center of view @@ -143,8 +143,8 @@ namespace PrioritySortUtil { + _ageWeight * cosineAngleFactor * age; // decrement priority of things outside keyhole - if (distance - radius > view.getCenterRadius()) { - if (!view.sphereIntersectsFrustum(position, radius)) { + if (distance - radius > view.getRadius()) { + if (!view.intersects(offset, distance, radius)) { constexpr float OUT_OF_VIEW_PENALTY = -10.0f; priority += OUT_OF_VIEW_PENALTY; } @@ -152,7 +152,7 @@ namespace PrioritySortUtil { return priority; } - ViewFrustums _views; + ConicalViewFrustums _views; std::priority_queue _queue; float _angularWeight { DEFAULT_ANGULAR_COEF }; float _centerWeight { DEFAULT_CENTER_COEF }; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index c2b92aac5f..3aa70b0897 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -138,69 +138,6 @@ const char* ViewFrustum::debugPlaneName (int plane) const { return "Unknown"; } -int ViewFrustum::fromByteArray(const QByteArray& input) { - - // From the wire! - glm::vec3 cameraPosition; - glm::quat cameraOrientation; - float cameraCenterRadius; - float cameraFov; - float cameraAspectRatio; - float cameraNearClip; - float cameraFarClip; - - const unsigned char* startPosition = reinterpret_cast(input.constData()); - const unsigned char* sourceBuffer = startPosition; - - // camera details - memcpy(&cameraPosition, sourceBuffer, sizeof(cameraPosition)); - sourceBuffer += sizeof(cameraPosition); - sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, cameraOrientation); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &cameraFov); - sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, cameraAspectRatio); - sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraNearClip); - sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraFarClip); - memcpy(&cameraCenterRadius, sourceBuffer, sizeof(cameraCenterRadius)); - sourceBuffer += sizeof(cameraCenterRadius); - - setPosition(cameraPosition); - setOrientation(cameraOrientation); - setCenterRadius(cameraCenterRadius); - - // Also make sure it's got the correct lens details from the camera - if (0.0f != cameraAspectRatio && - 0.0f != cameraNearClip && - 0.0f != cameraFarClip && - cameraNearClip != cameraFarClip) { - - setProjection(cameraFov, cameraAspectRatio, cameraNearClip, cameraFarClip); - calculate(); - } - - return sourceBuffer - startPosition; -} - - -QByteArray ViewFrustum::toByteArray() { - static const int LARGE_ENOUGH = 1024; - QByteArray viewFrustumDataByteArray(LARGE_ENOUGH, 0); - unsigned char* destinationBuffer = reinterpret_cast(viewFrustumDataByteArray.data()); - unsigned char* startPosition = destinationBuffer; - - // camera details - memcpy(destinationBuffer, &_position, sizeof(_position)); - destinationBuffer += sizeof(_position); - destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _orientation); - destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _fieldOfView); - destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _aspectRatio); - destinationBuffer += packClipValueToTwoByte(destinationBuffer, _nearClip); - destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip); - memcpy(destinationBuffer, &_centerSphereRadius, sizeof(_centerSphereRadius)); - destinationBuffer += sizeof(_centerSphereRadius); - - return viewFrustumDataByteArray.left(destinationBuffer - startPosition); -} - ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const { // only check against frustum ViewFrustum::intersection result = INSIDE; @@ -338,13 +275,6 @@ bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const { return true; } -bool closeEnough(float a, float b, float relativeError) { - assert(relativeError >= 0.0f); - // NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero. - // This method works fine when: fabsf(a + b) >> EPSILON - return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError; -} - // TODO: the slop and relative error should be passed in by argument rather than hard-coded. bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const { const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 128a717df8..eada65468d 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -147,9 +147,6 @@ public: void invalidate(); // causes all reasonable intersection tests to fail - QByteArray toByteArray(); - int fromByteArray(const QByteArray& input); - private: glm::mat4 _view; glm::mat4 _projection; diff --git a/libraries/shared/src/shared/ConicalViewFrustum.cpp b/libraries/shared/src/shared/ConicalViewFrustum.cpp new file mode 100644 index 0000000000..2ef096e3a8 --- /dev/null +++ b/libraries/shared/src/shared/ConicalViewFrustum.cpp @@ -0,0 +1,146 @@ +// +// ConicalViewFrustum.cpp +// libraries/shared/src/shared +// +// Created by Clement Brisset 4/26/18 +// 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 "ConicalViewFrustum.h" + + +#include "../NumericalConstants.h" +#include "../ViewFrustum.h" + +void ConicalViewFrustum::set(const ViewFrustum& viewFrustum) { + // The ConicalViewFrustum 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(); + _radius = viewFrustum.getCenterRadius(); + _farClip = viewFrustum.getFarClip(); + + auto topLeft = viewFrustum.getNearTopLeft() - _position; + auto topRight = viewFrustum.getNearTopRight() - _position; + auto bottomLeft = viewFrustum.getNearBottomLeft() - _position; + auto bottomRight = viewFrustum.getNearBottomRight() - _position; + auto centerAxis = 0.25f * (topLeft + topRight + bottomLeft + bottomRight); // Take the average + + _direction = glm::normalize(centerAxis); + _angle = std::max(std::max(angleBetween(_direction, topLeft), + angleBetween(_direction, topRight)), + std::max(angleBetween(_direction, bottomLeft), + angleBetween(_direction, bottomRight))); +} + +void ConicalViewFrustum::calculate() { + // Pre-compute cos and sin for faster checks + _cosAngle = cosf(_angle); + _sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle); +} + +bool ConicalViewFrustum::isVerySimilar(const ConicalViewFrustum& other) const { + const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared + const float MIN_ANGLE_BETWEEN = 0.174533f; // radian angle between 2 vectors 10 degrees apart + const float MIN_RELATIVE_ERROR = 0.01f; // 1% + + return glm::distance2(_position, other._position) < MIN_POSITION_SLOP_SQUARED && + angleBetween(_direction, other._direction) < MIN_ANGLE_BETWEEN && + closeEnough(_angle, other._angle, MIN_RELATIVE_ERROR) && + closeEnough(_farClip, other._farClip, MIN_RELATIVE_ERROR) && + closeEnough(_radius, other._radius, MIN_RELATIVE_ERROR); +} + +bool ConicalViewFrustum::intersects(const AACube& cube) const { + auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere + auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame + float distance = glm::length(position); + + return intersects(position, distance, radius); +} + +bool ConicalViewFrustum::intersects(const AABox& box) const { + auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere + auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame + float distance = glm::length(position); + + return intersects(position, distance, radius); +} + +bool ConicalViewFrustum::getAngularSize(const AACube& cube) const { + auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere + auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame + float distance = glm::length(position); + + return getAngularSize(distance, radius); +} + +bool ConicalViewFrustum::getAngularSize(const AABox& box) const { + auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere + auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame + float distance = glm::length(position); + + return getAngularSize(distance, radius); +} + + +bool ConicalViewFrustum::intersects(const glm::vec3& relativePosition, float distance, float radius) const { + if (distance < _radius + radius) { + // Inside keyhole radius + return true; + } + if (distance > _farClip + radius) { + // Past far clip + return false; + } + + // 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) + return glm::dot(relativePosition, _direction) > + sqrtf(distance * distance - radius * radius) * _cosAngle - radius * _sinAngle; +} + +bool ConicalViewFrustum::getAngularSize(float distance, float radius) const { + const float AVOID_DIVIDE_BY_ZERO = 0.001f; + float angularSize = radius / (distance + AVOID_DIVIDE_BY_ZERO); + return angularSize; +} + +int ConicalViewFrustum::serialize(unsigned char* destinationBuffer) const { + const unsigned char* startPosition = destinationBuffer; + + memcpy(destinationBuffer, &_position, sizeof(_position)); + destinationBuffer += sizeof(_position); + memcpy(destinationBuffer, &_direction, sizeof(_direction)); + destinationBuffer += sizeof(_direction); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _angle); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip); + memcpy(destinationBuffer, &_radius, sizeof(_radius)); + destinationBuffer += sizeof(_radius); + + return destinationBuffer - startPosition; +} + +int ConicalViewFrustum::deserialize(const unsigned char* sourceBuffer) { + const unsigned char* startPosition = sourceBuffer; + + memcpy(&_position, sourceBuffer, sizeof(_position)); + sourceBuffer += sizeof(_position); + memcpy(&_direction, sourceBuffer, sizeof(_direction)); + sourceBuffer += sizeof(_direction); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &_angle); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, _farClip); + memcpy(&_radius, sourceBuffer, sizeof(_radius)); + sourceBuffer += sizeof(_radius); + + calculate(); + + return sourceBuffer - startPosition; +} diff --git a/libraries/shared/src/shared/ConicalViewFrustum.h b/libraries/shared/src/shared/ConicalViewFrustum.h new file mode 100644 index 0000000000..dc372d560e --- /dev/null +++ b/libraries/shared/src/shared/ConicalViewFrustum.h @@ -0,0 +1,70 @@ +// +// ConicalViewFrustum.h +// libraries/shared/src/shared +// +// Created by Clement Brisset 4/26/18 +// 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_ConicalViewFrustum_h +#define hifi_ConicalViewFrustum_h + +#include + +#include + +class AACube; +class AABox; +class ViewFrustum; +using ViewFrustums = std::vector; + +const float SQRT_TWO_OVER_TWO = 0.7071067811865f; +const float DEFAULT_VIEW_ANGLE = 1.0f; +const float DEFAULT_VIEW_RADIUS = 10.0f; +const float DEFAULT_VIEW_FAR_CLIP = 100.0f; + +// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority. +class ConicalViewFrustum { +public: + ConicalViewFrustum() = default; + ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); } + + void set(const ViewFrustum& viewFrustum); + void calculate(); + + const glm::vec3& getPosition() const { return _position; } + const glm::vec3& getDirection() const { return _direction; } + float getAngle() const { return _angle; } + float getRadius() const { return _radius; } + float getFarClip() const { return _farClip; } + + bool isVerySimilar(const ConicalViewFrustum& other) const; + + bool intersects(const AACube& cube) const; + bool intersects(const AABox& box) const; + bool getAngularSize(const AACube& cube) const; + bool getAngularSize(const AABox& box) const; + + bool intersects(const glm::vec3& relativePosition, float distance, float radius) const; + bool getAngularSize(float distance, float radius) const; + + int serialize(unsigned char* destinationBuffer) const; + int deserialize(const unsigned char* sourceBuffer); + +private: + glm::vec3 _position { 0.0f, 0.0f, 0.0f }; + glm::vec3 _direction { 0.0f, 0.0f, 1.0f }; + float _angle { DEFAULT_VIEW_ANGLE }; + float _radius { DEFAULT_VIEW_RADIUS }; + float _farClip { DEFAULT_VIEW_FAR_CLIP }; + + float _sinAngle { SQRT_TWO_OVER_TWO }; + float _cosAngle { SQRT_TWO_OVER_TWO }; +}; +using ConicalViewFrustums = std::vector; + + +#endif /* hifi_ConicalViewFrustum_h */ diff --git a/server-console/package-lock.json b/server-console/package-lock.json index 4311fde51a..4f12f2fa00 100644 --- a/server-console/package-lock.json +++ b/server-console/package-lock.json @@ -20,7 +20,6 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, "requires": { "co": "4.6.0", "fast-deep-equal": "1.0.0", @@ -41,11 +40,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=" }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, "array-find-index": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.1.tgz", @@ -118,16 +112,10 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "author-regex": { "version": "1.0.0", @@ -136,17 +124,14 @@ "dev": true }, "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.3.2.tgz", - "integrity": "sha1-054L7kEs7Q6O2Uoj4xTzE6lbn9E=", - "requires": { - "lru-cache": "4.0.1" - } + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, "base64-js": { "version": "1.2.0", @@ -204,11 +189,11 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "requires": { - "hoek": "2.16.3" + "hoek": "4.2.1" } }, "buffers": { @@ -239,9 +224,9 @@ } }, "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chainsaw": { "version": "0.1.0", @@ -252,18 +237,6 @@ "traverse": "0.3.9" } }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, "cheerio": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.19.0.tgz", @@ -295,8 +268,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "code-point-at": { "version": "1.0.0", @@ -318,6 +290,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, "requires": { "graceful-readlink": "1.0.1" } @@ -373,11 +346,21 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "requires": { - "boom": "2.10.1" + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.1" + } + } } }, "css-select": { @@ -728,7 +711,7 @@ "minimist": "1.2.0", "pretty-bytes": "1.0.4", "progress-stream": "1.2.0", - "request": "2.71.0", + "request": "2.85.0", "single-line-log": "1.1.2", "throttleit": "0.0.2" }, @@ -828,11 +811,6 @@ "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, "extend": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", @@ -875,14 +853,12 @@ "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fd-slicer": { "version": "1.0.1", @@ -976,13 +952,23 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "1.0.0-rc4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz", - "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "async": "1.5.2", - "combined-stream": "1.0.5", - "mime-types": "2.1.10" + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + }, + "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + } } }, "fs-extra": { @@ -1058,19 +1044,6 @@ } } }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "requires": { - "is-property": "1.0.2" - } - }, "get-package-info": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", @@ -1212,7 +1185,8 @@ "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true }, "growly": { "version": "1.3.0", @@ -1222,43 +1196,32 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "chalk": "1.1.3", - "commander": "2.9.0", - "is-my-json-valid": "2.13.1", - "pinkie-promise": "2.0.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" } }, "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" }, "home-path": { "version": "1.0.5", @@ -1301,13 +1264,20 @@ } }, "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "0.2.0", + "assert-plus": "1.0.0", "jsprim": "1.2.2", "sshpk": "1.7.4" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } } }, "indent-string": { @@ -1388,28 +1358,12 @@ "number-is-nan": "1.0.0" } }, - "is-my-json-valid": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", - "integrity": "sha1-1Vd4qC/rawlj/0vhEdXRaE6JBwc=", - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "2.0.0", - "xtend": "4.0.1" - } - }, "is-promise": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=", "dev": true }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -1465,8 +1419,7 @@ "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stringify-safe": { "version": "5.0.1", @@ -1478,11 +1431,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz", "integrity": "sha1-4lK5mmr5AdPsQfMyWJyQUJp7xgU=" }, - "jsonpointer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz", - "integrity": "sha1-OvHdIP6FRjkQ1GmjheMwF9KgMNk=" - }, "jsprim": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz", @@ -1569,15 +1517,6 @@ "signal-exit": "2.1.2" } }, - "lru-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz", - "integrity": "sha1-E0OVXtry432bnn7nJB4nxLn7cr4=", - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.0.0" - } - }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -1603,16 +1542,16 @@ } }, "mime-db": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.22.0.tgz", - "integrity": "sha1-qyOmNy3J2G09yRIb0OvTgQWhkEo=" + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" }, "mime-types": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz", - "integrity": "sha1-uTx8tDYuFtQQcqflRTj7TUMHCDc=", + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.22.0" + "mime-db": "1.33.0" } }, "minimist": { @@ -1891,11 +1830,6 @@ } } }, - "node-uuid": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz", - "integrity": "sha1-baWhdmjEs91ZYjvaEc9/pMH2Cm8=" - }, "nodeify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz", @@ -1945,7 +1879,7 @@ "minimist": "1.2.0", "pretty-bytes": "1.0.4", "progress-stream": "1.2.0", - "request": "2.71.0", + "request": "2.85.0", "single-line-log": "1.1.2", "throttleit": "0.0.2" }, @@ -1973,9 +1907,9 @@ "integrity": "sha1-wCD1KcUoKt/dIz2R1LGBw9aG3Es=" }, "oauth-sign": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.1.tgz", - "integrity": "sha1-GCQ5vbkTeL90YOdcZOpD5kSN7wY=" + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { "version": "4.0.1", @@ -2099,8 +2033,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "2.3.0", @@ -2111,12 +2044,14 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "2.0.4" } @@ -2166,11 +2101,6 @@ "is-promise": "1.0.1" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "pump": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", @@ -2183,8 +2113,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "q": { "version": "1.5.1", @@ -2193,9 +2122,9 @@ "dev": true }, "qs": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz", - "integrity": "sha1-7B0WJrJCeNmfD99FSeUk4k7O6yY=" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "rc": { "version": "1.1.6", @@ -2252,31 +2181,39 @@ } }, "request": { - "version": "2.71.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.71.0.tgz", - "integrity": "sha1-bxRkPJxaZ8ruapXPjvBHfVYDvZE=", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", + "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.3.2", - "bl": "1.1.2", - "caseless": "0.11.0", + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", "combined-stream": "1.0.5", - "extend": "3.0.0", + "extend": "3.0.1", "forever-agent": "0.6.1", - "form-data": "1.0.0-rc4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.10", - "node-uuid": "1.4.7", - "oauth-sign": "0.8.1", - "qs": "6.1.0", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", "stringstream": "0.0.5", - "tough-cookie": "2.2.2", - "tunnel-agent": "0.4.2" + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + }, + "dependencies": { + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + } } }, "request-progress": { @@ -2308,8 +2245,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "sanitize-filename": { "version": "1.6.1", @@ -2347,11 +2283,11 @@ } }, "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { - "hoek": "2.16.3" + "hoek": "4.2.1" } }, "spdx-correct": { @@ -2483,11 +2419,6 @@ } } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, "tar-fs": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz", @@ -2585,9 +2516,12 @@ } }, "tough-cookie": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", - "integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc=" + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "1.4.1" + } }, "traverse": { "version": "0.3.9", @@ -2611,9 +2545,12 @@ } }, "tunnel-agent": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.2.tgz", - "integrity": "sha1-EQTj82rIcSXChycAZ9WC0YEzv+4=" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } }, "tweetnacl": { "version": "0.14.3", @@ -2644,6 +2581,11 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", @@ -2710,11 +2652,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, - "yallist": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz", - "integrity": "sha1-MGxUODXwnuGkyyO3vOmrNByRzdQ=" - }, "yargs": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", diff --git a/server-console/package.json b/server-console/package.json index 2428d2574e..6dd39ea6f8 100644 --- a/server-console/package.json +++ b/server-console/package.json @@ -30,7 +30,7 @@ "fs-extra": "^1.0.0", "node-notifier": "^5.2.1", "os-homedir": "^1.0.1", - "request": "^2.67.0", + "request": "^2.85.0", "request-progress": "1.0.2", "tar-fs": "^1.12.0", "yargs": "^3.30.0" diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 8c17d7c5c2..33aea6dcc9 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -441,14 +442,8 @@ protected: viewOut = _viewFrustum; } - void copyViewFrustum(ViewFrustum& viewOut) const override { - viewOut = _viewFrustum; - } - - void copySecondaryViewFrustum(ViewFrustum& viewOut) const override {} - - bool hasSecondaryViewFrustum() const override { - return false; + const ConicalViewFrustums& getConicalViews() const override { + return _view; } QThread* getMainThread() override { @@ -1126,6 +1121,7 @@ private: graphics::LightPointer _globalLight { std::make_shared() }; bool _ready { false }; EntitySimulationPointer _entitySimulation; + ConicalViewFrustums _view; QStringList _commands; QString _commandPath;