Merge remote-tracking branch 'cristo86/android_places_goto' into android_new_login

This commit is contained in:
Gabriel Calero 2018-05-07 15:56:43 -03:00
commit 66b975192d
69 changed files with 1299 additions and 1222 deletions

View file

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

View file

@ -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": ""
}
]
}

View file

@ -21,6 +21,7 @@
#include <AddressManager.h>
#include "AndroidHelper.h"
#include <udt/PacketHeaders.h>
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_,

View file

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

View file

@ -230,5 +230,4 @@ public class InterfaceActivity extends QtActivity {
public void onBackPressed() {
openAndroidActivity("Home", false);
}
}

View file

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

View file

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

View file

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

View file

@ -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<tabwidget.getChildCount(); i++){
TextView tv= tabwidget.getChildAt(i).findViewById(android.R.id.title);
tv.setTextAppearance(R.style.TabText);
}
RecyclerView domainsView = rootView.findViewById(R.id.rvDomains);
mDomainsView = rootView.findViewById(R.id.rvDomains);
int numberOfColumns = 1;
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
domainsView.setLayoutManager(gridLayoutMgr);
domainAdapter = new DomainAdapter(getContext());
domainAdapter.setClickListener((view, position, domain) -> 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("");
}
}

View file

@ -0,0 +1,9 @@
package io.highfidelity.hifiinterface.provider;
/**
* Created by cduarte on 4/18/18.
*/
public interface Callback<T> {
public void callback(T t);
}

View file

@ -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<DomainAdapter.Domain> domain);
void retrieveError(Exception e, String message);
}
}

View file

@ -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<UserStory> allStories; // All retrieved stories from the API
private List<DomainAdapter.Domain> 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<Exception> restOfPagesCallback) {
restOfPagesCallback.callback(new Exception("Error accessing url [" + url + "]", t));
}
private void getUserStoryPage(int pageNumber, Callback<Exception> restOfPagesCallback, Callback<Void> firstPageCallback) {
Call<UserStories> 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<UserStories>() {
@Override
public void onResponse(Call<UserStories> call, Response<UserStories> 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<UserStories> 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> 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<UserStories>() {
@Override
public void onResponse(Call<UserStories> call, Response<UserStories> response) {
UserStories userStories = response.body();
if (userStories == null) {
domainCallback.retrieveOk(new ArrayList<>(0));
}
List<DomainAdapter.Domain> domains = new ArrayList<>(userStories.total_entries);
userStories.user_stories.forEach(userStory -> {
domains.add(userStory.toDomain());
});
domainCallback.retrieveOk(domains);
}
@Override
public void onFailure(Call<UserStories> call, Throwable t) {
domainCallback.retrieveError(new Exception(t), t.getMessage());
}
});
}
public interface UserStoryDomainProviderService {
@GET("api/v1/user_stories")
Call<UserStories> 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<UserStory> user_stories;
}
}

View file

@ -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<DomainAdapter.ViewHolder> {
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<Domain> 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> 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<DomainAdapter.ViewHolder
public void onBindViewHolder(ViewHolder holder, int position) {
// TODO
//holder.thumbnail.setImageResource(mDomains[position].thumbnail);
holder.mDomainName.setText(mDomains[position].name);
Domain domain = mDomains[position];
holder.mDomainName.setText(domain.name);
Uri uri = Uri.parse(domain.thumbnail);
Picasso.get().load(uri).into(holder.mThumbnail);
}
@Override
@ -128,4 +122,21 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
public interface ItemClickListener {
void onItemClick(View view, int position, Domain domain);
}
public static class Domain {
public String name;
public String url;
public String thumbnail;
public Domain(String name, String url, String thumbnail) {
this.name = name;
this.thumbnail = thumbnail;
this.url = url;
}
}
public interface AdapterListener {
void onEmptyAdapter();
void onNonEmptyAdapter();
void onError(Exception e, String message);
}
}

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="1dp" android:left="12dp" android:bottom="6dp" android:right="12dp">
<shape android:shape="rectangle">
<solid android:color="@color/backgroundDark" />
<corners android:radius="4dp"/>
</shape>
</item>
</layer-list>

View file

@ -0,0 +1,12 @@
<vector android:height="24dp" android:viewportHeight="15.0"
android:viewportWidth="15.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#404040" android:pathData="M7.5,7.5m-7.5,0a7.5,7.5 0,1 1,15 0a7.5,7.5 0,1 1,-15 0"/>
<path android:fillColor="#00000000"
android:pathData="M4.683,4.583L10,10.27"
android:strokeColor="#E3E3E3" android:strokeLineCap="round"
android:strokeLineJoin="bevel" android:strokeWidth="1"/>
<path android:fillColor="#00000000"
android:pathData="M10,4.73L4.683,10.417"
android:strokeColor="#E3E3E3" android:strokeLineCap="round"
android:strokeLineJoin="bevel" android:strokeWidth="1"/>
</vector>

View file

@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="18.0"
android:viewportWidth="18.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#F2F2F2" android:fillType="evenOdd" android:pathData="M12.5,11L11.7,11L11.4,10.7C12.4,9.6 13,8.1 13,6.5C13,2.9 10.1,0 6.5,0C2.9,0 0,2.9 0,6.5C0,10.1 2.9,13 6.5,13C8.1,13 9.6,12.4 10.7,11.4L11,11.7L11,12.5L16,17.5L17.5,16L12.5,11ZM6.5,11C4,11 2,9 2,6.5C2,4 4,2 6.5,2C9,2 11,4 11,6.5C11,9 9,11 6.5,11Z"/>
</vector>

View file

@ -7,11 +7,13 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"

View file

@ -1,74 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="113dp"
android:paddingLeft="14dp"
android:paddingRight="14dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:background="@drawable/domain_bg">
<ImageView
android:id="@+id/domainThumbnail"
android:layout_height="@dimen/domainItemHeight"
android:layout_marginStart="@dimen/domainMarginStart"
android:layout_marginEnd="@dimen/domainMarginEnd"
android:layout_marginTop="@dimen/domainMarginTop"
android:layout_marginBottom="@dimen/domainMarginBottom"
android:foreground="@drawable/rippleable"
android:clickable="true"
android:focusable="true"
android:elevation="0dp"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/item_corner_radius">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srcCompat="@android:drawable/ic_menu_gallery" />
android:layout_height="match_parent">
<TextView
android:id="@+id/domainName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="14dp"
android:layout_marginLeft="10dp"
android:fontFamily="@font/raleway_semibold"
android:text=""
android:textSize="21sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<ImageView
android:id="@+id/domainThumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:srcCompat="@android:drawable/ic_menu_gallery" />
<TextView
android:id="@+id/amountOfPeople"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="7dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/raleway"
android:text="10"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/domainName"
android:layout_width="match_parent"
android:layout_height="@dimen/domainNameHeight"
android:fontFamily="@font/raleway_semibold"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:gravity="center_vertical"
android:text=""
android:textSize="21sp"
android:textColor="@color/white_opaque"
android:background="@color/black_060"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="12dp"
android:layout_height="13dp"
android:layout_marginLeft="4dp"
android:layout_marginBottom="3dp"
app:srcCompat="@drawable/ic_person"
app:layout_constraintBottom_toBottomOf="@id/amountOfPeople"
app:layout_constraintLeft_toRightOf="@id/amountOfPeople"/>
<ImageButton
android:id="@+id/imageButtonBookmark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_bookmark"
android:tint="@color/colorPrimary"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:padding="1dp"
android:layout_marginRight="10dp"
android:layout_marginTop="12dp"/>
<ImageButton
android:id="@+id/imageViewShare"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="9dp"
app:layout_constraintTop_toBottomOf="@id/imageButtonBookmark"
app:layout_constraintLeft_toLeftOf="@id/imageButtonBookmark"
app:layout_constraintRight_toRightOf="@id/imageButtonBookmark"
android:background="@drawable/ic_share"
/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

View file

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_activity_goto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context="io.highfidelity.hifiinterface.GotoActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_constraintTop_toTopOf="@id/root_activity_goto"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<EditText
android:id="@+id/url_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/HifiEditText"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:hint="@string/goto_url_hint"
android:inputType="textUri"
android:imeOptions="actionGo"
app:layout_constraintTop_toBottomOf="@id/toolbar"
/>
<android.support.v7.widget.AppCompatButton
android:id="@+id/go_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/button_horizontal_margin"
android:text="@string/go"
app:layout_constraintTop_toBottomOf="@id/url_text"
app:layout_constraintEnd_toEndOf="@id/root_activity_goto"/>
</android.support.constraint.ConstraintLayout>

View file

@ -2,75 +2,72 @@
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contentHomeRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="io.highfidelity.hifiinterface.MainActivity"
tools:showIn="@layout/activity_main">
tools:context="io.highfidelity.hifiinterface.MainActivity">
<TabHost
android:id="@+id/tabhost"
<EditText
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="@dimen/searchEditHeight"
app:layout_constraintTop_toTopOf="@id/contentHomeRoot"
android:background="@drawable/search_bg"
android:layout_marginTop="@dimen/searchEditAdditionalMarginTop"
android:gravity="center_vertical|end"
android:paddingStart="@dimen/searchEditPaddingStart"
android:paddingEnd="@dimen/searchEditPaddingEnd"
android:fontFamily="@font/raleway"
android:hint="@string/search_hint"
android:textSize="@dimen/searchEditTextSize"
android:inputType="textUri"
android:imeOptions="actionGo"
/>
<ImageView
android:id="@+id/search_mag_icon"
android:layout_width="@dimen/searchEditMagIconWidth"
android:layout_height="@dimen/searchEditMagIconHeight"
app:layout_constraintEnd_toEndOf="@id/searchView"
app:layout_constraintTop_toTopOf="@id/searchView"
app:layout_constraintBottom_toBottomOf="@id/searchView"
android:layout_marginEnd="@dimen/searchEditMagMarginEnd"
android:src="@drawable/ic_search"
/>
<ImageView
android:id="@+id/search_clear"
android:layout_width="@dimen/searchEditClearIconWidth"
android:layout_height="@dimen/searchEditClearIconHeight"
app:layout_constraintEnd_toEndOf="@id/searchView"
app:layout_constraintTop_toTopOf="@id/searchView"
app:layout_constraintBottom_toBottomOf="@id/searchView"
android:layout_marginEnd="@dimen/searchEditClearMarginEnd"
android:visibility="gone"
android:src="@drawable/ic_clear"
/>
<TextView
android:id="@+id/searchNoResultsView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/SearchText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchView"
android:layout_marginTop="32dp"
android:text="@string/search_no_results"
android:visibility="gone"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/backgroundDark"/>
<SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="42dp"
android:background="@drawable/search_bg"
android:gravity="right"
android:layout_gravity="right"
/>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/featured"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/rvDomains"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout
android:id="@+id/popular"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="@+id/bookmarks"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
<android.support.v7.widget.RecyclerView
android:id="@+id/rvDomains"
app:layout_constraintTop_toBottomOf="@id/searchView"
app:layout_constraintBottom_toBottomOf="@id/contentHomeRoot"
app:layout_constraintStart_toStartOf="@id/contentHomeRoot"
app:layout_constraintEnd_toEndOf="@id/contentHomeRoot"
android:layout_width="0dp"
android:layout_height="0dp" />
</android.support.constraint.ConstraintLayout>

View file

@ -5,8 +5,4 @@
android:id="@+id/action_home"
android:title="@string/home"
/>
<item
android:id="@+id/action_goto"
android:title="@string/action_goto"
/>
</menu>

View file

@ -15,5 +15,6 @@
<color name="editTextHint">#9e9e9e</color>
<color name="menuOption">#F2F2F2</color>
<color name="colorLoginError">#FF7171</color>
<color name="black_060">#99000000</color>
<color name="statusbar_color">#292929</color>
</resources>

View file

@ -11,7 +11,30 @@
<dimen name="text_size_subtitle_material_toolbar">12dp</dimen>
<dimen name="button_horizontal_margin">12dp</dimen>
<dimen name="edit_text_padding">8dp</dimen>
<dimen name="item_corner_radius">4dp</dimen>
<!-- Search (domains) screen dimensions -->
<dimen name="searchEditHeight">47.5dp</dimen>
<dimen name="searchEditAdditionalMarginTop">11dp</dimen>
<dimen name="searchEditPaddingStart">24dp</dimen>
<dimen name="searchEditPaddingEnd">51dp</dimen>
<dimen name="searchEditTextSize">19sp</dimen>
<dimen name="searchEditMagIconWidth">16dp</dimen>
<dimen name="searchEditMagIconHeight">16dp</dimen>
<dimen name="searchEditMagMarginEnd">22dp</dimen>
<dimen name="searchEditClearIconWidth">16dp</dimen>
<dimen name="searchEditClearIconHeight">16dp</dimen>
<dimen name="searchEditClearMarginEnd">22dp</dimen>
<dimen name="domainItemHeight">163dp</dimen>
<dimen name="domainMarginStart">14dp</dimen>
<dimen name="domainMarginEnd">14dp</dimen>
<dimen name="domainMarginTop">2dp</dimen>
<dimen name="domainMarginBottom">6dp</dimen>
<dimen name="domainNameHeight">64dp</dimen>
<dimen name="header_hifi_margin_top">56dp</dimen>
<dimen name="header_hifi_height">101dp</dimen>
<dimen name="header_hifi_width">425dp</dimen>
</resources>
</resources>

View file

@ -1,7 +1,6 @@
<resources>
<string name="app_name" translatable="false">Interface</string>
<string name="home">Home</string>
<string name="go_to">Go To</string>
<string name="web_view_action_open_in_browser" translatable="false">Open in browser</string>
<string name="web_view_action_share" translatable="false">Share link</string>
<string name="web_view_action_share_subject" translatable="false">Shared a link</string>
@ -9,9 +8,7 @@
<string name="featured">FEATURED</string>
<string name="popular">POPULAR</string>
<string name="bookmarks">BOOKMARKS</string>
<string name="action_goto">Go To</string>
<string name="goto_url_hint">Type a domain url</string>
<string name="go">Go</string>
<string name="username_or_email">Username or email</string>
<string name="password">Password</string>
<string name="login">Login</string>
@ -19,6 +16,9 @@
<string name="forgot_password">Forgot password?</string>
<string name="login_username_or_password_incorrect">Username or password incorrect.</string>
<string name="logging_in">Logging into High Fidelity</string>
<string name="search_hint"><i>Search for a place by name</i>\u00A0</string>
<string name="search_loading">Loading places…</string>
<string name="search_no_results">No places exist with that name</string>
<string name="privacyPolicy">Privacy Policy</string>
</resources>

View file

@ -42,7 +42,7 @@
<style name="SearchText">
<item name="android:fontFamily">@font/raleway_light_italic</item>
<item name="android:textSize">15sp</item>
<item name="android:textSize">19sp</item>
</style>
<!-- Overriding text size so it's not so big in portrait -->

View file

@ -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<ScriptableAvatar>();
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<unsigned char*>(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<NodeList>()->broadcastToNodes(std::move(avatarPacket),
{ NodeType::AvatarMixer });

View file

@ -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<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
AudioGate _audioGate;

View file

@ -47,7 +47,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
auto& packetReceiver = DependencyManager::get<NodeList>()->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<ReceivedMessage> mess
}
void AvatarMixer::handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
void AvatarMixer::handleAvatarQueryPacket(QSharedPointer<ReceivedMessage> 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;

View file

@ -46,7 +46,7 @@ public slots:
private slots:
void queueIncomingPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
void handleAdjustAvatarSorting(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAvatarQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);

View file

@ -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<const unsigned char*>(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);
});
}

View file

@ -28,7 +28,7 @@
#include <PortableHighResolutionClock.h>
#include <SimpleMovingAverage.h>
#include <UUIDHasher.h>
#include <ViewFrustum.h>
#include <shared/ConicalViewFrustum.h>
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<QUuid> _radiusIgnoredOthers;
ViewFrustums _currentViewFrustums;
ConicalViewFrustums _currentViewFrustums;
int _recentOtherAvatarsInView { 0 };
int _recentOtherAvatarsOutOfView { 0 };

View file

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

View file

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

View file

@ -17,8 +17,9 @@
#include "../octree/OctreeSendThread.h"
#include <DiffTraversal.h>
#include <EntityPriorityQueue.h>
#include <shared/ConicalViewFrustum.h>
#include "EntityPriorityQueue.h"
class EntityNodeData;
class EntityItem;
@ -51,7 +52,6 @@ private:
DiffTraversal _traversal;
EntityPriorityQueue _sendQueue;
std::unordered_map<EntityItem*, uint64_t> _knownState;
ConicalView _conicalView; // cached optimized view for fast priority calculations
// packet construction stuff
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };

View file

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

View file

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

View file

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

View file

@ -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<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera"));
if (!camera || !camera->isEnabled()) {
_hasSecondaryViewFrustum = false;
return;
}
ViewFrustum secondaryViewFrustum;
if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
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<EntityScriptingInterface>();
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<SceneScriptingInterface>()->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<unsigned char*>(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<NodeList>()->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<LODManager>();
_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<DdeFaceTracker>()->reset();
DependencyManager::get<EyeTracker>()->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.

View file

@ -47,6 +47,7 @@
#include <AbstractUriHandler.h>
#include <shared/RateCounter.h>
#include <ThreadSafeValueCache.h>
#include <shared/ConicalViewFrustum.h>
#include <shared/FileLogger.h>
#include <RunningMarker.h>
@ -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<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
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<MyAvatar> 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

View file

@ -35,6 +35,7 @@
#include <UsersScriptingInterface.h>
#include <UUID.h>
#include <avatars-renderer/OtherAvatar.h>
#include <shared/ConicalViewFrustum.h>
#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<SortableAvatar> sortedAvatars(views,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,

View file

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

View file

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

View file

@ -13,6 +13,8 @@
#include <OctreeUtils.h>
#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);

View file

@ -12,7 +12,7 @@
#ifndef hifi_DiffTraversal_h
#define hifi_DiffTraversal_h
#include <ViewFrustum.h>
#include <shared/ConicalViewFrustum.h>
#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(); }

View file

@ -15,44 +15,14 @@
#include <queue>
#include <unordered_set>
#include <AACube.h>
#include <DiffTraversal.h>
#include <EntityTreeElement.h>
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<ConicalViewFrustum> _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(); }

View file

@ -34,7 +34,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::MaterialData);
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::MultiFrustumQuery);
return static_cast<PacketVersion>(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<PacketVersion>(PingVersion::IncludeConnectionID);
case PacketType::ViewFrustum:
return static_cast<PacketVersion>(ViewFrustumVersion::SendMultipleFrustums);
case PacketType::AvatarQuery:
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
default:
return 20;
}

View file

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

View file

@ -18,10 +18,6 @@
#include <GLMHelpers.h>
#include <udt/PacketHeaders.h>
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<const unsigned char*>(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<const char*>(sourceBuffer), bytesLeft);
sourceBuffer += _mainViewFrustum.fromByteArray(byteArray);
}
if (_hasSecondaryFrustum) {
auto bytesLeft = endPosition - sourceBuffer;
auto byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(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

View file

@ -16,8 +16,7 @@
#include <QtCore/QReadWriteLock>
#include <NodeData.h>
#include <ViewFrustum.h>
#include <shared/ConicalViewFrustum.h>
#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;

View file

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

View file

@ -14,14 +14,14 @@
#include <iostream>
#include <NodeData.h>
#include <qqueue.h>
#include "OctreeConstants.h"
#include "OctreeElementBag.h"
#include "OctreePacketData.h"
#include "OctreeQuery.h"
#include "OctreeSceneStats.h"
#include "SentPacketHistory.h"
#include <qqueue.h>
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 };

View file

@ -12,6 +12,8 @@
#ifndef hifi_OctreeUtils_h
#define hifi_OctreeUtils_h
#include <NumericalConstants.h>
#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

View file

@ -24,6 +24,8 @@ class Transform;
class QThread;
class ViewFrustum;
class PickRay;
class ConicalViewFrustum;
using ConicalViewFrustums = std::vector<ConicalViewFrustum>;
/// 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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,7 +15,7 @@
#include <queue>
#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<T> _queue;
float _angularWeight { DEFAULT_ANGULAR_COEF };
float _centerWeight { DEFAULT_CENTER_COEF };

View file

@ -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<const unsigned char*>(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<unsigned char*>(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

View file

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

View file

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

View file

@ -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 <vector>
#include <glm/glm.hpp>
class AACube;
class AABox;
class ViewFrustum;
using ViewFrustums = std::vector<ViewFrustum>;
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<ConicalViewFrustum>;
#endif /* hifi_ConicalViewFrustum_h */

View file

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

View file

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

View file

@ -34,6 +34,7 @@
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QApplication>
#include <shared/ConicalViewFrustum.h>
#include <shared/RateCounter.h>
#include <shared/NetworkUtils.h>
#include <shared/FileLogger.h>
@ -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<graphics::Light>() };
bool _ready { false };
EntitySimulationPointer _entitySimulation;
ConicalViewFrustums _view;
QStringList _commands;
QString _commandPath;