package com.asha.vrlib; import android.view.MotionEvent; import com.asha.vrlib.common.MDMainHandler; import com.asha.vrlib.common.VRUtil; import com.asha.vrlib.model.MDDirectorSnapshot; import com.asha.vrlib.model.MDHitEvent; import com.asha.vrlib.model.MDHitPoint; import com.asha.vrlib.model.MDRay; import com.asha.vrlib.plugins.MDAbsPlugin; import com.asha.vrlib.plugins.MDPluginAdapter; import com.asha.vrlib.plugins.MDPluginManager; import com.asha.vrlib.plugins.hotspot.IMDHotspot; import com.asha.vrlib.strategy.display.DisplayModeManager; import com.asha.vrlib.strategy.projection.ProjectionModeManager; import java.util.LinkedList; import java.util.List; import static com.asha.vrlib.common.VRUtil.checkGLThread; import static com.asha.vrlib.common.VRUtil.checkMainThread; /** * Created by hzqiujiadi on 16/8/7. * hzqiujiadi ashqalcn@gmail.com */ public class MDPickerManager { private static final String TAG = "MDPickerManager"; private static final int HIT_FROM_EYE = 1; private static final int HIT_FROM_TOUCH = 2; private boolean mEyePickEnable; private DisplayModeManager mDisplayModeManager; private ProjectionModeManager mProjectionModeManager; private MDPluginManager mPluginManager; private MDVRLibrary.IEyePickListener2 mEyePickChangedListener; private MDVRLibrary.ITouchPickListener2 mTouchPickListener; private EyePickPoster mEyePickPoster = new EyePickPoster(); private TouchPickPoster mTouchPickPoster = new TouchPickPoster(); private RayPickAsTouchMainTask mRayPickAsTouchRunnable = new RayPickAsTouchMainTask(); private RayPickAsEyeMainTask mRayPickAsEyeRunnable = new RayPickAsEyeMainTask(); private DirectorContext mDirectorContext = new DirectorContext(); private final Object mDirectorLock = new Object(); private MDVRLibrary.IGestureListener mTouchPicker = new MDVRLibrary.IGestureListener() { @Override public void onClick(MotionEvent e) { mRayPickAsTouchRunnable.setEvent(e.getX(), e.getY()); mRayPickAsTouchRunnable.run(); } }; private MDAbsPlugin mEyePicker = new MDPluginAdapter() { private long pickTs; // gl thread @Override public void beforeRenderer(int totalWidth, int totalHeight) { synchronized (mDirectorLock){ mDirectorContext.snapshot(mProjectionModeManager.getDirectors()); } if (isEyePickEnable()){ long current = System.currentTimeMillis(); if (current - pickTs > 100){ MDMainHandler.sharedHandler().post(mRayPickAsEyeRunnable); pickTs = current; } } } }; private MDPickerManager(Builder params) { this.mDisplayModeManager = params.displayModeManager; this.mProjectionModeManager = params.projectionModeManager; this.mPluginManager = params.pluginManager; } public boolean isEyePickEnable() { return mEyePickEnable; } public void setEyePickEnable(boolean eyePickEnable) { this.mEyePickEnable = eyePickEnable; } // main thread. private void rayPickAsTouch(float x, float y, DirectorContext directorContext) { int size = mDisplayModeManager.getVisibleSize(); if (size == 0){ return; } MDDirectorSnapshot snapshot = directorContext.getSnapshot(0); if (snapshot == null){ return; } int itemWidth = (int) snapshot.getViewportWidth(); int index = (int) (x / itemWidth); if (index >= size){ return; } snapshot = directorContext.getSnapshot(index); if (snapshot == null){ return; } MDRay ray = VRUtil.point2Ray(x - itemWidth * index, y, snapshot); pick(ray, HIT_FROM_TOUCH); } // main thread. private void rayPickAsEye(DirectorContext mDirectorContext) { MDDirectorSnapshot snapshot = mDirectorContext.getSnapshot(0); if (snapshot == null){ return; } MDRay ray = VRUtil.point2Ray(snapshot.getViewportWidth() / 2, snapshot.getViewportHeight() / 2, snapshot); pick(ray, HIT_FROM_EYE); } private IMDHotspot pick(MDRay ray, int hitType){ if (ray == null) return null; return hitTest(ray, hitType); } private IMDHotspot hitTest(MDRay ray, int hitType) { // main thread checkMainThread("hitTest must in main thread"); List<MDAbsPlugin> plugins = mPluginManager.getPlugins(); IMDHotspot hitHotspot = null; MDHitPoint currentDistance = MDHitPoint.notHit(); for (MDAbsPlugin plugin : plugins) { if (plugin instanceof IMDHotspot) { IMDHotspot hotspot = (IMDHotspot) plugin; MDHitPoint tmpDistance = hotspot.hit(ray); if (!tmpDistance.isNotHit() && tmpDistance.nearThen(currentDistance)){ hitHotspot = hotspot; currentDistance = tmpDistance; } } } switch (hitType) { case HIT_FROM_TOUCH: // only post the hotspot which is hit. if (hitHotspot != null && !currentDistance.isNotHit()){ hitHotspot.onTouchHit(ray); mTouchPickPoster.fire(hitHotspot, ray, currentDistance); } break; case HIT_FROM_EYE: mEyePickPoster.fire(hitHotspot, ray, currentDistance); break; } return hitHotspot; } public MDVRLibrary.IGestureListener getTouchPicker() { return mTouchPicker; } public MDAbsPlugin getEyePicker() { return mEyePicker; } public static Builder with() { return new Builder(); } public void setEyePickChangedListener(MDVRLibrary.IEyePickListener2 eyePickChangedListener) { this.mEyePickChangedListener = eyePickChangedListener; } public void setTouchPickListener(MDVRLibrary.ITouchPickListener2 touchPickListener) { this.mTouchPickListener = touchPickListener; } void resetEyePick(){ if (mEyePickPoster != null){ mEyePickPoster.setHit(null); } } public static class Builder { private DisplayModeManager displayModeManager; private ProjectionModeManager projectionModeManager; private MDPluginManager pluginManager; private Builder() { } public MDPickerManager build(){ return new MDPickerManager(this); } public Builder setPluginManager(MDPluginManager pluginManager) { this.pluginManager = pluginManager; return this; } public Builder setDisplayModeManager(DisplayModeManager displayModeManager) { this.displayModeManager = displayModeManager; return this; } public Builder setProjectionModeManager(ProjectionModeManager projectionModeManager) { this.projectionModeManager = projectionModeManager; return this; } } private class RayPickAsTouchMainTask implements Runnable { float x; float y; public void setEvent(float x, float y){ this.x = x; this.y = y; } @Override public void run() { synchronized (mDirectorLock){ rayPickAsTouch(x, y, mDirectorContext); } } } private class RayPickAsEyeMainTask implements Runnable { @Override public void run() { synchronized (mDirectorLock){ rayPickAsEye(mDirectorContext); } } } private class EyePickPoster { private IMDHotspot hit; private long timestamp; void fire(IMDHotspot hit, MDRay ray, MDHitPoint hitPoint) { setHit(hit); MDHitEvent event = MDHitEvent.obtain(); event.setHotspot(hit); event.setRay(ray); event.setTimestamp(timestamp); event.setHitPoint(hitPoint); if (this.hit != null){ this.hit.onEyeHitIn(event); } if (mEyePickChangedListener != null){ mEyePickChangedListener.onHotspotHit(event); } MDHitEvent.recycle(event); } void setHit(IMDHotspot hit){ if (this.hit != hit){ if (this.hit != null){ this.hit.onEyeHitOut(timestamp); } timestamp = System.currentTimeMillis(); } this.hit = hit; } } private class TouchPickPoster { void fire(IMDHotspot hitHotspot, MDRay ray, MDHitPoint hitPoint) { if (mTouchPickListener != null){ MDHitEvent event = MDHitEvent.obtain(); event.setHotspot(hitHotspot); event.setRay(ray); event.setTimestamp(System.currentTimeMillis()); event.setHitPoint(hitPoint); mTouchPickListener.onHotspotHit(event); MDHitEvent.recycle(event); } } } private static class DirectorContext { private int size; private List<MDDirectorSnapshot> list = new LinkedList<>(); public void snapshot(List<MD360Director> directorList){ checkGLThread("snapshot must in gl thread!"); ensureSize(directorList.size()); for (int i = 0; i < directorList.size(); i++){ list.get(i).copy(directorList.get(i)); } } private void ensureSize(int size){ this.size = size; while (list.size() < size){ list.add(new MDDirectorSnapshot()); } } public MDDirectorSnapshot getSnapshot(int i) { if (i < size){ return list.get(0); } return null; } } }