mirror of
https://github.com/JulianGro/overte.git
synced 2025-05-08 22:59:31 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into editControllerMapping
This commit is contained in:
commit
96d2ba660c
19 changed files with 353 additions and 84 deletions
|
@ -98,13 +98,17 @@ public class FriendsFragment extends Fragment {
|
||||||
|
|
||||||
mUsersAdapter.setListener(new UserListAdapter.AdapterListener() {
|
mUsersAdapter.setListener(new UserListAdapter.AdapterListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onEmptyAdapter() {
|
public void onEmptyAdapter(boolean shouldStopRefreshing) {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
if (shouldStopRefreshing) {
|
||||||
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNonEmptyAdapter() {
|
public void onNonEmptyAdapter(boolean shouldStopRefreshing) {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
if (shouldStopRefreshing) {
|
||||||
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,6 +119,8 @@ public class FriendsFragment extends Fragment {
|
||||||
|
|
||||||
mUsersView.setAdapter(mUsersAdapter);
|
mUsersView.setAdapter(mUsersAdapter);
|
||||||
|
|
||||||
|
mUsersAdapter.startLoad();
|
||||||
|
|
||||||
mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() {
|
mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
|
|
@ -76,18 +76,22 @@ public class HomeFragment extends Fragment {
|
||||||
});
|
});
|
||||||
mDomainAdapter.setListener(new DomainAdapter.AdapterListener() {
|
mDomainAdapter.setListener(new DomainAdapter.AdapterListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onEmptyAdapter() {
|
public void onEmptyAdapter(boolean shouldStopRefreshing) {
|
||||||
searchNoResultsView.setText(R.string.search_no_results);
|
searchNoResultsView.setText(R.string.search_no_results);
|
||||||
searchNoResultsView.setVisibility(View.VISIBLE);
|
searchNoResultsView.setVisibility(View.VISIBLE);
|
||||||
mDomainsView.setVisibility(View.GONE);
|
mDomainsView.setVisibility(View.GONE);
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
if (shouldStopRefreshing) {
|
||||||
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNonEmptyAdapter() {
|
public void onNonEmptyAdapter(boolean shouldStopRefreshing) {
|
||||||
searchNoResultsView.setVisibility(View.GONE);
|
searchNoResultsView.setVisibility(View.GONE);
|
||||||
mDomainsView.setVisibility(View.VISIBLE);
|
mDomainsView.setVisibility(View.VISIBLE);
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
if (shouldStopRefreshing) {
|
||||||
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,11 +100,20 @@ public class HomeFragment extends Fragment {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mDomainsView.setAdapter(mDomainAdapter);
|
mDomainsView.setAdapter(mDomainAdapter);
|
||||||
|
mDomainAdapter.startLoad();
|
||||||
|
|
||||||
mSearchView = rootView.findViewById(R.id.searchView);
|
mSearchView = rootView.findViewById(R.id.searchView);
|
||||||
mSearchIconView = rootView.findViewById(R.id.search_mag_icon);
|
mSearchIconView = rootView.findViewById(R.id.search_mag_icon);
|
||||||
mClearSearch = rootView.findViewById(R.id.search_clear);
|
mClearSearch = rootView.findViewById(R.id.search_clear);
|
||||||
|
|
||||||
|
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
mSearchView.addTextChangedListener(new TextWatcher() {
|
mSearchView.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||||
|
@ -142,10 +155,6 @@ public class HomeFragment extends Fragment {
|
||||||
mDomainAdapter.loadDomains(mSearchView.getText().toString(), true);
|
mDomainAdapter.loadDomains(mSearchView.getText().toString(), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
|
||||||
|
|
||||||
return rootView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.highfidelity.hifiinterface.R;
|
import io.highfidelity.hifiinterface.R;
|
||||||
|
@ -36,19 +37,41 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
||||||
// references to our domains
|
// references to our domains
|
||||||
private Domain[] mDomains = {};
|
private Domain[] mDomains = {};
|
||||||
|
|
||||||
|
private static Domain[] DOMAINS_TMP_CACHE = {};
|
||||||
|
|
||||||
public DomainAdapter(Context c, String protocol, String lastLocation) {
|
public DomainAdapter(Context c, String protocol, String lastLocation) {
|
||||||
mContext = c;
|
mContext = c;
|
||||||
this.mInflater = LayoutInflater.from(mContext);
|
this.mInflater = LayoutInflater.from(mContext);
|
||||||
mProtocol = protocol;
|
mProtocol = protocol;
|
||||||
mLastLocation = lastLocation;
|
mLastLocation = lastLocation;
|
||||||
domainProvider = new UserStoryDomainProvider(mProtocol);
|
domainProvider = new UserStoryDomainProvider(mProtocol);
|
||||||
loadDomains("", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(AdapterListener adapterListener) {
|
public void setListener(AdapterListener adapterListener) {
|
||||||
mAdapterListener = adapterListener;
|
mAdapterListener = adapterListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void startLoad() {
|
||||||
|
useTmpCachedDomains();
|
||||||
|
loadDomains("", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void useTmpCachedDomains() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (DOMAINS_TMP_CACHE != null && DOMAINS_TMP_CACHE.length > 0) {
|
||||||
|
mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
if (mAdapterListener != null) {
|
||||||
|
if (mDomains.length == 0) {
|
||||||
|
mAdapterListener.onEmptyAdapter(false);
|
||||||
|
} else {
|
||||||
|
mAdapterListener.onNonEmptyAdapter(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void loadDomains(String filterText, boolean forceRefresh) {
|
public void loadDomains(String filterText, boolean forceRefresh) {
|
||||||
domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() {
|
domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,13 +83,18 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
||||||
overrideDefaultThumbnails(domain);
|
overrideDefaultThumbnails(domain);
|
||||||
|
|
||||||
mDomains = new Domain[domain.size()];
|
mDomains = new Domain[domain.size()];
|
||||||
mDomains = domain.toArray(mDomains);
|
synchronized (this) {
|
||||||
notifyDataSetChanged();
|
domain.toArray(mDomains);
|
||||||
if (mAdapterListener != null) {
|
if (filterText.isEmpty()) {
|
||||||
if (mDomains.length == 0) {
|
DOMAINS_TMP_CACHE = Arrays.copyOf(mDomains, mDomains.length);
|
||||||
mAdapterListener.onEmptyAdapter();
|
}
|
||||||
} else {
|
notifyDataSetChanged();
|
||||||
mAdapterListener.onNonEmptyAdapter();
|
if (mAdapterListener != null) {
|
||||||
|
if (mDomains.length == 0) {
|
||||||
|
mAdapterListener.onEmptyAdapter(true);
|
||||||
|
} else {
|
||||||
|
mAdapterListener.onNonEmptyAdapter(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,8 +140,6 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
// TODO
|
|
||||||
//holder.thumbnail.setImageResource(mDomains[position].thumbnail);
|
|
||||||
Domain domain = mDomains[position];
|
Domain domain = mDomains[position];
|
||||||
holder.mDomainName.setText(domain.name);
|
holder.mDomainName.setText(domain.name);
|
||||||
Uri uri = Uri.parse(domain.thumbnail);
|
Uri uri = Uri.parse(domain.thumbnail);
|
||||||
|
@ -164,8 +190,8 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AdapterListener {
|
public interface AdapterListener {
|
||||||
void onEmptyAdapter();
|
void onEmptyAdapter(boolean shouldStopRefreshing);
|
||||||
void onNonEmptyAdapter();
|
void onNonEmptyAdapter(boolean shouldStopRefreshing);
|
||||||
void onError(Exception e, String message);
|
void onError(Exception e, String message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,28 +37,57 @@ public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHo
|
||||||
private ItemClickListener mClickListener;
|
private ItemClickListener mClickListener;
|
||||||
private AdapterListener mAdapterListener;
|
private AdapterListener mAdapterListener;
|
||||||
|
|
||||||
|
private static List<User> USERS_TMP_CACHE;
|
||||||
|
|
||||||
public UserListAdapter(Context c, UsersProvider usersProvider) {
|
public UserListAdapter(Context c, UsersProvider usersProvider) {
|
||||||
mContext = c;
|
mContext = c;
|
||||||
mInflater = LayoutInflater.from(mContext);
|
mInflater = LayoutInflater.from(mContext);
|
||||||
mProvider = usersProvider;
|
mProvider = usersProvider;
|
||||||
loadUsers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(AdapterListener adapterListener) {
|
public void setListener(AdapterListener adapterListener) {
|
||||||
mAdapterListener = adapterListener;
|
mAdapterListener = adapterListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void startLoad() {
|
||||||
|
useTmpCachedUsers();
|
||||||
|
loadUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void useTmpCachedUsers() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) {
|
||||||
|
mUsers = new ArrayList<>(USERS_TMP_CACHE.size());
|
||||||
|
mUsers.addAll(USERS_TMP_CACHE);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
if (mAdapterListener != null) {
|
||||||
|
if (mUsers.isEmpty()) {
|
||||||
|
mAdapterListener.onEmptyAdapter(false);
|
||||||
|
} else {
|
||||||
|
mAdapterListener.onNonEmptyAdapter(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void loadUsers() {
|
public void loadUsers() {
|
||||||
mProvider.retrieve(new UsersProvider.UsersCallback() {
|
mProvider.retrieve(new UsersProvider.UsersCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void retrieveOk(List<User> users) {
|
public void retrieveOk(List<User> users) {
|
||||||
mUsers = new ArrayList<>(users);
|
mUsers = new ArrayList<>(users);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
if (mAdapterListener != null) {
|
|
||||||
if (mUsers.isEmpty()) {
|
synchronized (this) {
|
||||||
mAdapterListener.onEmptyAdapter();
|
USERS_TMP_CACHE = new ArrayList<>(mUsers.size());
|
||||||
} else {
|
USERS_TMP_CACHE.addAll(mUsers);
|
||||||
mAdapterListener.onNonEmptyAdapter();
|
|
||||||
|
if (mAdapterListener != null) {
|
||||||
|
if (mUsers.isEmpty()) {
|
||||||
|
mAdapterListener.onEmptyAdapter(true);
|
||||||
|
} else {
|
||||||
|
mAdapterListener.onNonEmptyAdapter(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,8 +269,9 @@ public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHo
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AdapterListener {
|
public interface AdapterListener {
|
||||||
void onEmptyAdapter();
|
void onEmptyAdapter(boolean shouldStopRefreshing);
|
||||||
void onNonEmptyAdapter();
|
void onNonEmptyAdapter(boolean shouldStopRefreshing);
|
||||||
void onError(Exception e, String message);
|
void onError(Exception e, String message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1866,6 +1866,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
});
|
});
|
||||||
|
|
||||||
EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// try to find the renderable
|
// try to find the renderable
|
||||||
auto renderable = getEntities()->renderableForEntityId(entityID);
|
auto renderable = getEntities()->renderableForEntityId(entityID);
|
||||||
if (renderable) {
|
if (renderable) {
|
||||||
|
@ -1881,6 +1885,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
|
EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// try to find the renderable
|
// try to find the renderable
|
||||||
auto renderable = getEntities()->renderableForEntityId(entityID);
|
auto renderable = getEntities()->renderableForEntityId(entityID);
|
||||||
if (renderable) {
|
if (renderable) {
|
||||||
|
|
|
@ -312,6 +312,9 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||||
|
|
||||||
|
int getOtherAvatarsReplicaCount() { return DependencyManager::get<AvatarHashMap>()->getReplicaCount(); }
|
||||||
|
void setOtherAvatarsReplicaCount(int count) { DependencyManager::get<AvatarHashMap>()->setReplicaCount(count); }
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
void beforeEnterBackground();
|
void beforeEnterBackground();
|
||||||
void enterBackground();
|
void enterBackground();
|
||||||
|
|
|
@ -105,7 +105,7 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick
|
||||||
glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition());
|
glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition());
|
||||||
glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat;
|
glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat;
|
||||||
pos = extractTranslation(finalPosAndRotMat);
|
pos = extractTranslation(finalPosAndRotMat);
|
||||||
rot = glmExtractRotation(finalPosAndRotMat);
|
rot = props.getRotation();
|
||||||
dim = props.getDimensions();
|
dim = props.getDimensions();
|
||||||
registrationPoint = props.getRegistrationPoint();
|
registrationPoint = props.getRegistrationPoint();
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,3 +191,11 @@ void TestScriptingInterface::saveObject(QVariant variant, const QString& filenam
|
||||||
void TestScriptingInterface::showMaximized() {
|
void TestScriptingInterface::showMaximized() {
|
||||||
qApp->getWindow()->showMaximized();
|
qApp->getWindow()->showMaximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) {
|
||||||
|
qApp->setOtherAvatarsReplicaCount(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TestScriptingInterface::getOtherAvatarsReplicaCount() {
|
||||||
|
return qApp->getOtherAvatarsReplicaCount();
|
||||||
|
}
|
|
@ -149,6 +149,20 @@ public slots:
|
||||||
*/
|
*/
|
||||||
void showMaximized();
|
void showMaximized();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Values higher than 0 will create replicas of other-avatars when entering a domain for testing purpouses
|
||||||
|
* @function Test.setOtherAvatarsReplicaCount
|
||||||
|
* @param {number} count - Number of replicas we want to create
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void setOtherAvatarsReplicaCount(int count);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Return the number of replicas that are being created of other-avatars when entering a domain
|
||||||
|
* @function Test.getOtherAvatarsReplicaCount
|
||||||
|
* @returns {number} Current number of replicas of other-avatars.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE int getOtherAvatarsReplicaCount();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool waitForCondition(qint64 maxWaitMs, std::function<bool()> condition);
|
bool waitForCondition(qint64 maxWaitMs, std::function<bool()> condition);
|
||||||
QString _testResultsLocation;
|
QString _testResultsLocation;
|
||||||
|
|
|
@ -156,6 +156,10 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
|
||||||
// integrate phase forward in time.
|
// integrate phase forward in time.
|
||||||
_phase += omega * dt;
|
_phase += omega * dt;
|
||||||
|
|
||||||
|
if (_phase < 0.0f) {
|
||||||
|
_phase = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// detect loop trigger events
|
// detect loop trigger events
|
||||||
if (_phase >= 1.0f) {
|
if (_phase >= 1.0f) {
|
||||||
triggersOut.setTrigger(_id + "Loop");
|
triggersOut.setTrigger(_id + "Loop");
|
||||||
|
|
|
@ -458,7 +458,6 @@ protected:
|
||||||
glm::vec3 _lastAngularVelocity;
|
glm::vec3 _lastAngularVelocity;
|
||||||
glm::vec3 _angularAcceleration;
|
glm::vec3 _angularAcceleration;
|
||||||
glm::quat _lastOrientation;
|
glm::quat _lastOrientation;
|
||||||
|
|
||||||
glm::vec3 _worldUpDirection { Vectors::UP };
|
glm::vec3 _worldUpDirection { Vectors::UP };
|
||||||
bool _moving { false }; ///< set when position is changing
|
bool _moving { false }; ///< set when position is changing
|
||||||
|
|
||||||
|
|
|
@ -918,7 +918,18 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
|
|
||||||
PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition));
|
PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition));
|
||||||
auto data = reinterpret_cast<const AvatarDataPacket::AvatarGlobalPosition*>(sourceBuffer);
|
auto data = reinterpret_cast<const AvatarDataPacket::AvatarGlobalPosition*>(sourceBuffer);
|
||||||
auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]);
|
|
||||||
|
glm::vec3 offset = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
if (_replicaIndex > 0) {
|
||||||
|
const float SPACE_BETWEEN_AVATARS = 2.0f;
|
||||||
|
const int AVATARS_PER_ROW = 3;
|
||||||
|
int row = _replicaIndex % AVATARS_PER_ROW;
|
||||||
|
int col = floor(_replicaIndex / AVATARS_PER_ROW);
|
||||||
|
offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset;
|
||||||
if (_globalPosition != newValue) {
|
if (_globalPosition != newValue) {
|
||||||
_globalPosition = newValue;
|
_globalPosition = newValue;
|
||||||
_globalPositionChanged = usecTimestampNow();
|
_globalPositionChanged = usecTimestampNow();
|
||||||
|
|
|
@ -337,6 +337,7 @@ enum KillAvatarReason : uint8_t {
|
||||||
TheirAvatarEnteredYourBubble,
|
TheirAvatarEnteredYourBubble,
|
||||||
YourAvatarEnteredTheirBubble
|
YourAvatarEnteredTheirBubble
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(KillAvatarReason);
|
Q_DECLARE_METATYPE(KillAvatarReason);
|
||||||
|
|
||||||
class QDataStream;
|
class QDataStream;
|
||||||
|
@ -1186,6 +1187,8 @@ public:
|
||||||
|
|
||||||
virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {}
|
virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {}
|
||||||
virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {}
|
virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {}
|
||||||
|
void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; }
|
||||||
|
int getReplicaIndex() { return _replicaIndex; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
@ -1445,6 +1448,7 @@ protected:
|
||||||
udt::SequenceNumber _identitySequenceNumber { 0 };
|
udt::SequenceNumber _identitySequenceNumber { 0 };
|
||||||
bool _hasProcessedFirstIdentity { false };
|
bool _hasProcessedFirstIdentity { false };
|
||||||
float _density;
|
float _density;
|
||||||
|
int _replicaIndex { 0 };
|
||||||
|
|
||||||
// null unless MyAvatar or ScriptableAvatar sending traits data to mixer
|
// null unless MyAvatar or ScriptableAvatar sending traits data to mixer
|
||||||
std::unique_ptr<ClientTraitsHandler> _clientTraitsHandler;
|
std::unique_ptr<ClientTraitsHandler> _clientTraitsHandler;
|
||||||
|
|
|
@ -21,6 +21,84 @@
|
||||||
#include "AvatarLogging.h"
|
#include "AvatarLogging.h"
|
||||||
#include "AvatarTraits.h"
|
#include "AvatarTraits.h"
|
||||||
|
|
||||||
|
|
||||||
|
void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) {
|
||||||
|
if (parentID == QUuid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_replicasMap.find(parentID) == _replicasMap.end()) {
|
||||||
|
std::vector<AvatarSharedPointer> emptyReplicas = std::vector<AvatarSharedPointer>();
|
||||||
|
_replicasMap.insert(std::pair<QUuid, std::vector<AvatarSharedPointer>>(parentID, emptyReplicas));
|
||||||
|
}
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
replica->setReplicaIndex((int)replicas.size() + 1);
|
||||||
|
replicas.push_back(replica);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QUuid> AvatarReplicas::getReplicaIDs(const QUuid& parentID) {
|
||||||
|
std::vector<QUuid> ids;
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
for (int i = 0; i < (int)replicas.size(); i++) {
|
||||||
|
ids.push_back(replicas[i]->getID());
|
||||||
|
}
|
||||||
|
} else if (_replicaCount > 0) {
|
||||||
|
for (int i = 0; i < _replicaCount; i++) {
|
||||||
|
ids.push_back(QUuid::createUuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarReplicas::parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer) {
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
for (auto avatar : replicas) {
|
||||||
|
avatar->parseDataFromBuffer(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarReplicas::removeReplicas(const QUuid& parentID) {
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
_replicasMap.erase(parentID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) {
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
for (auto avatar : replicas) {
|
||||||
|
avatar->processAvatarIdentity(identityData, identityChanged, displayNameChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AvatarReplicas::processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData) {
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
for (auto avatar : replicas) {
|
||||||
|
avatar->processTrait(traitType, traitBinaryData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) {
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
for (auto avatar : replicas) {
|
||||||
|
avatar->processDeletedTraitInstance(traitType, instanceID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType,
|
||||||
|
AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) {
|
||||||
|
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||||
|
auto &replicas = _replicasMap[parentID];
|
||||||
|
for (auto avatar : replicas) {
|
||||||
|
avatar->processTraitInstance(traitType, instanceID, traitBinaryData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AvatarHashMap::AvatarHashMap() {
|
AvatarHashMap::AvatarHashMap() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
@ -64,6 +142,21 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::setReplicaCount(int count) {
|
||||||
|
_replicas.setReplicaCount(count);
|
||||||
|
auto avatars = getAvatarIdentifiers();
|
||||||
|
for (int i = 0; i < avatars.size(); i++) {
|
||||||
|
KillAvatarReason reason = KillAvatarReason::NoReason;
|
||||||
|
if (avatars[i] != QUuid()) {
|
||||||
|
removeAvatar(avatars[i], reason);
|
||||||
|
auto replicaIDs = _replicas.getReplicaIDs(avatars[i]);
|
||||||
|
for (auto id : replicaIDs) {
|
||||||
|
removeAvatar(id, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int AvatarHashMap::numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters) {
|
int AvatarHashMap::numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters) {
|
||||||
auto hashCopy = getHashCopy();
|
auto hashCopy = getHashCopy();
|
||||||
auto rangeMeters2 = rangeMeters * rangeMeters;
|
auto rangeMeters2 = rangeMeters * rangeMeters;
|
||||||
|
@ -135,18 +228,25 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
|
||||||
|
|
||||||
// make sure this isn't our own avatar data or for a previously ignored node
|
// make sure this isn't our own avatar data or for a previously ignored node
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
bool isNewAvatar;
|
bool isNewAvatar;
|
||||||
if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) {
|
if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) {
|
||||||
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar);
|
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar);
|
||||||
|
|
||||||
if (isNewAvatar) {
|
if (isNewAvatar) {
|
||||||
QWriteLocker locker(&_hashLock);
|
QWriteLocker locker(&_hashLock);
|
||||||
_pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar });
|
_pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar });
|
||||||
|
auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
|
||||||
|
for (auto replicaID : replicaIDs) {
|
||||||
|
auto replicaAvatar = addAvatar(replicaID, sendingNode);
|
||||||
|
_replicas.addReplica(sessionUUID, replicaAvatar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// have the matching (or new) avatar parse the data from the packet
|
// have the matching (or new) avatar parse the data from the packet
|
||||||
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
||||||
message->seek(positionBeforeRead + bytesRead);
|
message->seek(positionBeforeRead + bytesRead);
|
||||||
|
_replicas.parseDataFromBuffer(sessionUUID, byteArray);
|
||||||
|
|
||||||
return avatar;
|
return avatar;
|
||||||
} else {
|
} else {
|
||||||
// create a dummy AvatarData class to throw this data on the ground
|
// create a dummy AvatarData class to throw this data on the ground
|
||||||
|
@ -191,10 +291,13 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
||||||
bool displayNameChanged = false;
|
bool displayNameChanged = false;
|
||||||
// In this case, the "sendingNode" is the Avatar Mixer.
|
// In this case, the "sendingNode" is the Avatar Mixer.
|
||||||
avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged);
|
avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged);
|
||||||
|
_replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||||
|
|
||||||
while (message->getBytesLeftToRead()) {
|
while (message->getBytesLeftToRead()) {
|
||||||
// read the avatar ID to figure out which avatar this is for
|
// read the avatar ID to figure out which avatar this is for
|
||||||
auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||||
|
@ -202,7 +305,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
||||||
// grab the avatar so we can ask it to process trait data
|
// grab the avatar so we can ask it to process trait data
|
||||||
bool isNewAvatar;
|
bool isNewAvatar;
|
||||||
auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar);
|
auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar);
|
||||||
|
|
||||||
// read the first trait type for this avatar
|
// read the first trait type for this avatar
|
||||||
AvatarTraits::TraitType traitType;
|
AvatarTraits::TraitType traitType;
|
||||||
message->readPrimitive(&traitType);
|
message->readPrimitive(&traitType);
|
||||||
|
@ -217,13 +319,14 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
||||||
AvatarTraits::TraitWireSize traitBinarySize;
|
AvatarTraits::TraitWireSize traitBinarySize;
|
||||||
bool skipBinaryTrait = false;
|
bool skipBinaryTrait = false;
|
||||||
|
|
||||||
|
|
||||||
if (AvatarTraits::isSimpleTrait(traitType)) {
|
if (AvatarTraits::isSimpleTrait(traitType)) {
|
||||||
message->readPrimitive(&traitBinarySize);
|
message->readPrimitive(&traitBinarySize);
|
||||||
|
|
||||||
// check if this trait version is newer than what we already have for this avatar
|
// check if this trait version is newer than what we already have for this avatar
|
||||||
if (packetTraitVersion > lastProcessedVersions[traitType]) {
|
if (packetTraitVersion > lastProcessedVersions[traitType]) {
|
||||||
avatar->processTrait(traitType, message->read(traitBinarySize));
|
auto traitData = message->read(traitBinarySize);
|
||||||
|
avatar->processTrait(traitType, traitData);
|
||||||
|
_replicas.processTrait(avatarID, traitType, traitData);
|
||||||
lastProcessedVersions[traitType] = packetTraitVersion;
|
lastProcessedVersions[traitType] = packetTraitVersion;
|
||||||
} else {
|
} else {
|
||||||
skipBinaryTrait = true;
|
skipBinaryTrait = true;
|
||||||
|
@ -238,8 +341,11 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
||||||
if (packetTraitVersion > processedInstanceVersion) {
|
if (packetTraitVersion > processedInstanceVersion) {
|
||||||
if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) {
|
if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) {
|
||||||
avatar->processDeletedTraitInstance(traitType, traitInstanceID);
|
avatar->processDeletedTraitInstance(traitType, traitInstanceID);
|
||||||
|
_replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID);
|
||||||
} else {
|
} else {
|
||||||
avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize));
|
auto traitData = message->read(traitBinarySize);
|
||||||
|
avatar->processTraitInstance(traitType, traitInstanceID, traitData);
|
||||||
|
_replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData);
|
||||||
}
|
}
|
||||||
processedInstanceVersion = packetTraitVersion;
|
processedInstanceVersion = packetTraitVersion;
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,17 +371,31 @@ void AvatarHashMap::processKillAvatar(QSharedPointer<ReceivedMessage> message, S
|
||||||
KillAvatarReason reason;
|
KillAvatarReason reason;
|
||||||
message->readPrimitive(&reason);
|
message->readPrimitive(&reason);
|
||||||
removeAvatar(sessionUUID, reason);
|
removeAvatar(sessionUUID, reason);
|
||||||
|
auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
|
||||||
|
for (auto id : replicaIDs) {
|
||||||
|
removeAvatar(id, reason);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
||||||
QWriteLocker locker(&_hashLock);
|
QWriteLocker locker(&_hashLock);
|
||||||
|
|
||||||
|
auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
|
||||||
|
_replicas.removeReplicas(sessionUUID);
|
||||||
|
for (auto id : replicaIDs) {
|
||||||
|
auto removedReplica = _avatarHash.take(id);
|
||||||
|
if (removedReplica) {
|
||||||
|
handleRemovedAvatar(removedReplica, removalReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_pendingAvatars.remove(sessionUUID);
|
_pendingAvatars.remove(sessionUUID);
|
||||||
auto removedAvatar = _avatarHash.take(sessionUUID);
|
auto removedAvatar = _avatarHash.take(sessionUUID);
|
||||||
|
|
||||||
if (removedAvatar) {
|
if (removedAvatar) {
|
||||||
handleRemovedAvatar(removedAvatar, removalReason);
|
handleRemovedAvatar(removedAvatar, removalReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
|
void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
|
||||||
|
|
|
@ -41,6 +41,27 @@
|
||||||
* @hifi-assignment-client
|
* @hifi-assignment-client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class AvatarReplicas {
|
||||||
|
public:
|
||||||
|
AvatarReplicas() {}
|
||||||
|
void addReplica(const QUuid& parentID, AvatarSharedPointer replica);
|
||||||
|
std::vector<QUuid> getReplicaIDs(const QUuid& parentID);
|
||||||
|
void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer);
|
||||||
|
void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged);
|
||||||
|
void removeReplicas(const QUuid& parentID);
|
||||||
|
void processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData);
|
||||||
|
void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID);
|
||||||
|
void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType,
|
||||||
|
AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData);
|
||||||
|
void setReplicaCount(int count) { _replicaCount = count; }
|
||||||
|
int getReplicaCount() { return _replicaCount; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<QUuid, std::vector<AvatarSharedPointer>> _replicasMap;
|
||||||
|
int _replicaCount { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class AvatarHashMap : public QObject, public Dependency {
|
class AvatarHashMap : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
@ -77,6 +98,9 @@ public:
|
||||||
virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); }
|
virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); }
|
||||||
int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters);
|
int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters);
|
||||||
|
|
||||||
|
void setReplicaCount(int count);
|
||||||
|
int getReplicaCount() { return _replicas.getReplicaCount(); };
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -167,6 +191,8 @@ protected:
|
||||||
mutable QReadWriteLock _hashLock;
|
mutable QReadWriteLock _hashLock;
|
||||||
|
|
||||||
std::unordered_map<QUuid, AvatarTraits::TraitVersions> _processedTraitVersions;
|
std::unordered_map<QUuid, AvatarTraits::TraitVersions> _processedTraitVersions;
|
||||||
|
AvatarReplicas _replicas;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUuid _lastOwnerSessionUUID;
|
QUuid _lastOwnerSessionUUID;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD,
|
PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD,
|
||||||
DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic,
|
DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic,
|
||||||
getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI
|
getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI
|
||||||
Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST
|
Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST,
|
||||||
|
worldPositionToRegistrationFrameMatrix
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
|
@ -593,18 +594,9 @@ Script.include("/~/system/libraries/Xform.js");
|
||||||
|
|
||||||
this.calculateOffset = function(controllerData) {
|
this.calculateOffset = function(controllerData) {
|
||||||
if (this.distanceHolding || this.distanceRotating) {
|
if (this.distanceHolding || this.distanceRotating) {
|
||||||
var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [
|
var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
|
||||||
"position",
|
[ "position", "rotation", "registrationPoint", "dimensions" ]);
|
||||||
"rotation"
|
return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
|
||||||
]);
|
|
||||||
var zeroVector = { x: 0, y: 0, z:0, w: 0 };
|
|
||||||
var intersection = controllerData.rayPicks[this.hand].intersection;
|
|
||||||
var intersectionMat = new Xform(zeroVector, intersection);
|
|
||||||
var modelMat = new Xform(targetProps.rotation, targetProps.position);
|
|
||||||
var modelMatInv = modelMat.inv();
|
|
||||||
var xformMat = Xform.mul(modelMatInv, intersectionMat);
|
|
||||||
var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
|
|
||||||
return offsetMat;
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE,
|
makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE,
|
||||||
TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD,
|
TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD,
|
||||||
Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST,
|
Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST,
|
||||||
Uuid
|
Uuid, worldPositionToRegistrationFrameMatrix
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
|
@ -572,18 +572,9 @@ Script.include("/~/system/libraries/Xform.js");
|
||||||
|
|
||||||
this.calculateOffset = function(controllerData) {
|
this.calculateOffset = function(controllerData) {
|
||||||
if (this.distanceHolding || this.distanceRotating) {
|
if (this.distanceHolding || this.distanceRotating) {
|
||||||
var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [
|
var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
|
||||||
"position",
|
[ "position", "rotation", "registrationPoint", "dimensions" ]);
|
||||||
"rotation"
|
return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
|
||||||
]);
|
|
||||||
var zeroVector = { x: 0, y: 0, z:0, w: 0 };
|
|
||||||
var intersection = controllerData.rayPicks[this.hand].intersection;
|
|
||||||
var intersectionMat = new Xform(zeroVector, intersection);
|
|
||||||
var modelMat = new Xform(targetProps.rotation, targetProps.position);
|
|
||||||
var modelMatInv = modelMat.inv();
|
|
||||||
var xformMat = Xform.mul(modelMatInv, intersectionMat);
|
|
||||||
var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
|
|
||||||
return offsetMat;
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC,
|
Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC,
|
||||||
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation,
|
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation,
|
||||||
projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager,
|
projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager,
|
||||||
getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent
|
getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent,
|
||||||
|
worldPositionToRegistrationFrameMatrix
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
|
@ -615,18 +616,9 @@ Script.include("/~/system/libraries/Xform.js");
|
||||||
|
|
||||||
this.calculateOffset = function(controllerData) {
|
this.calculateOffset = function(controllerData) {
|
||||||
if (this.distanceHolding || this.distanceRotating) {
|
if (this.distanceHolding || this.distanceRotating) {
|
||||||
var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [
|
var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
|
||||||
"position",
|
[ "position", "rotation", "registrationPoint", "dimensions" ]);
|
||||||
"rotation"
|
return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
|
||||||
]);
|
|
||||||
var zeroVector = { x: 0, y: 0, z:0, w: 0 };
|
|
||||||
var intersection = controllerData.rayPicks[this.hand].intersection;
|
|
||||||
var intersectionMat = new Xform(zeroVector, intersection);
|
|
||||||
var modelMat = new Xform(targetProps.rotation, targetProps.position);
|
|
||||||
var modelMatInv = modelMat.inv();
|
|
||||||
var xformMat = Xform.mul(modelMatInv, intersectionMat);
|
|
||||||
var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
|
|
||||||
return offsetMat;
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform,
|
/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4,
|
||||||
Selection, Uuid,
|
Selection, Uuid,
|
||||||
MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true,
|
MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true,
|
||||||
HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
|
HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
|
||||||
|
@ -58,7 +58,8 @@
|
||||||
highlightTargetEntity:true,
|
highlightTargetEntity:true,
|
||||||
clearHighlightedEntities:true,
|
clearHighlightedEntities:true,
|
||||||
unhighlightTargetEntity:true,
|
unhighlightTargetEntity:true,
|
||||||
distanceBetweenEntityLocalPositionAndBoundingBox: true
|
distanceBetweenEntityLocalPositionAndBoundingBox: true,
|
||||||
|
worldPositionToRegistrationFrameMatrix: true
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MSECS_PER_SEC = 1000.0;
|
MSECS_PER_SEC = 1000.0;
|
||||||
|
@ -487,6 +488,30 @@ entityIsFarGrabbedByOther = function(entityID) {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
worldPositionToRegistrationFrameMatrix = function(wptrProps, pos) {
|
||||||
|
// get world matrix for intersection point
|
||||||
|
var intersectionMat = new Xform({ x: 0, y: 0, z:0, w: 1 }, pos);
|
||||||
|
|
||||||
|
// calculate world matrix for registrationPoint addjusted entity
|
||||||
|
var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 };
|
||||||
|
var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, wptrProps.registrationPoint);
|
||||||
|
var regOffset = Vec3.multiplyVbyV(regRatio, wptrProps.dimensions);
|
||||||
|
var regOffsetRot = Vec3.multiplyQbyV(wptrProps.rotation, regOffset);
|
||||||
|
var modelMat = new Xform(wptrProps.rotation, Vec3.sum(wptrProps.position, regOffsetRot));
|
||||||
|
|
||||||
|
// get inverse of model matrix
|
||||||
|
var modelMatInv = modelMat.inv();
|
||||||
|
|
||||||
|
// transform world intersection point into object's registrationPoint frame
|
||||||
|
var xformMat = Xform.mul(modelMatInv, intersectionMat);
|
||||||
|
|
||||||
|
// convert to Mat4
|
||||||
|
var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
|
||||||
|
return offsetMat;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
if (typeof module !== 'undefined') {
|
if (typeof module !== 'undefined') {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
makeDispatcherModuleParameters: makeDispatcherModuleParameters,
|
makeDispatcherModuleParameters: makeDispatcherModuleParameters,
|
||||||
|
@ -508,6 +533,7 @@ if (typeof module !== 'undefined') {
|
||||||
projectOntoEntityXYPlane: projectOntoEntityXYPlane,
|
projectOntoEntityXYPlane: projectOntoEntityXYPlane,
|
||||||
TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE,
|
TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE,
|
||||||
TRIGGER_ON_VALUE: TRIGGER_ON_VALUE,
|
TRIGGER_ON_VALUE: TRIGGER_ON_VALUE,
|
||||||
DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST
|
DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST,
|
||||||
|
worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue