From 8541ba6c26accfd7625186af2477737102c5dc66 Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Mon, 19 Nov 2018 20:36:29 +0100 Subject: [PATCH] Implement TimeWarp Layers --- app/CMakeLists.txt | 6 +- .../mozilla/vrbrowser/VRBrowserActivity.java | 51 +- .../vrbrowser/browser/SessionStore.java | 24 +- .../vrbrowser/browser/SettingsStore.java | 8 + .../ui/prompts/ChoicePromptWidget.java | 2 +- .../vrbrowser/ui/widgets/BrowserWidget.java | 75 ++- .../ui/widgets/CrashDialogWidget.java | 6 +- .../ui/widgets/DeveloperOptionsWidget.java | 2 +- .../ui/widgets/MediaControlsWidget.java | 4 +- .../ui/widgets/NavigationBarWidget.java | 122 ++-- .../ui/widgets/PermissionWidget.java | 4 +- .../vrbrowser/ui/widgets/SettingsWidget.java | 10 +- .../ui/widgets/SuggestionsWidget.java | 18 +- .../vrbrowser/ui/widgets/TrayWidget.java | 5 +- .../ui/widgets/UISurfaceTextureRenderer.java | 10 +- .../vrbrowser/ui/widgets/UIWidget.java | 39 +- .../ui/widgets/VoiceSearchWidget.java | 8 +- .../mozilla/vrbrowser/ui/widgets/Widget.java | 4 +- .../vrbrowser/ui/widgets/WidgetPlacement.java | 1 + app/src/main/cpp/BrowserWorld.cpp | 181 +++--- app/src/main/cpp/BrowserWorld.h | 3 +- app/src/main/cpp/Device.h | 5 + app/src/main/cpp/DeviceDelegate.h | 20 +- app/src/main/cpp/FadeAnimation.cpp | 104 ++++ app/src/main/cpp/FadeAnimation.h | 44 ++ app/src/main/cpp/FadeBlitter.cpp | 168 ------ app/src/main/cpp/FadeBlitter.h | 41 -- app/src/main/cpp/Quad.cpp | 92 ++- app/src/main/cpp/Quad.h | 9 +- app/src/main/cpp/Skybox.cpp | 198 +++++++ app/src/main/cpp/Skybox.h | 40 ++ app/src/main/cpp/SplashAnimation.cpp | 57 +- app/src/main/cpp/SplashAnimation.h | 9 +- app/src/main/cpp/VRBrowser.cpp | 27 + app/src/main/cpp/VRBrowser.h | 4 +- app/src/main/cpp/VRLayer.cpp | 389 +++++++++++++ app/src/main/cpp/VRLayer.h | 149 +++++ app/src/main/cpp/VRLayerNode.cpp | 76 +++ app/src/main/cpp/VRLayerNode.h | 49 ++ app/src/main/cpp/VRVideo.cpp | 189 ++++++- app/src/main/cpp/VRVideo.h | 9 +- app/src/main/cpp/Widget.cpp | 55 +- app/src/main/cpp/Widget.h | 5 + app/src/main/cpp/WidgetPlacement.cpp | 1 + app/src/main/cpp/WidgetPlacement.h | 1 + app/src/main/cpp/native-lib.cpp | 2 + app/src/main/cpp/vrb | 2 +- .../oculusvr/cpp/DeviceDelegateOculusVR.cpp | 523 +++++++++++++++++- app/src/oculusvr/cpp/DeviceDelegateOculusVR.h | 5 + build.gradle | 2 +- 50 files changed, 2346 insertions(+), 512 deletions(-) create mode 100644 app/src/main/cpp/FadeAnimation.cpp create mode 100644 app/src/main/cpp/FadeAnimation.h delete mode 100644 app/src/main/cpp/FadeBlitter.cpp delete mode 100644 app/src/main/cpp/FadeBlitter.h create mode 100644 app/src/main/cpp/Skybox.cpp create mode 100644 app/src/main/cpp/Skybox.h create mode 100644 app/src/main/cpp/VRLayer.cpp create mode 100644 app/src/main/cpp/VRLayer.h create mode 100644 app/src/main/cpp/VRLayerNode.cpp create mode 100644 app/src/main/cpp/VRLayerNode.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 6028a6fc68..ab8f1f7897 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -24,8 +24,7 @@ add_library( # Sets the name of the library. src/main/cpp/ControllerContainer.cpp src/main/cpp/DeviceUtils.cpp src/main/cpp/ElbowModel.cpp - src/main/cpp/FadeBlitter.cpp - src/main/cpp/GestureDelegate.cpp + src/main/cpp/FadeAnimation.cpp src/main/cpp/Quad.cpp src/main/cpp/ExternalBlitter.cpp src/main/cpp/ExternalVR.cpp @@ -33,9 +32,12 @@ add_library( # Sets the name of the library. src/main/cpp/GestureDelegate.cpp src/main/cpp/LoadingAnimation.cpp src/main/cpp/JNIUtil.cpp + src/main/cpp/Skybox.cpp src/main/cpp/SplashAnimation.cpp src/main/cpp/VRBrowser.cpp src/main/cpp/VRVideo.cpp + src/main/cpp/VRLayer.cpp + src/main/cpp/VRLayerNode.cpp src/main/cpp/Widget.cpp src/main/cpp/WidgetPlacement.cpp src/main/cpp/WidgetResizer.cpp diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index 0aab56079d..ee965f6dd8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -21,6 +21,7 @@ import android.util.Log; import android.util.Pair; import android.view.KeyEvent; +import android.view.Surface; import android.view.View; import android.widget.FrameLayout; @@ -421,6 +422,37 @@ public void onFrameAvailable(SurfaceTexture surfaceTexture) { }); } + @Keep + @SuppressWarnings("unused") + void dispatchCreateWidgetLayer(final int aHandle, final Surface aSurface, final int aWidth, final int aHeight) { + runOnUiThread(() -> { + final Widget widget = mWidgets.get(aHandle); + if (widget == null) { + Log.e(LOGTAG, "Widget " + aHandle + " not found"); + return; + } + + Runnable aFirstDrawCallback = null; + if (aSurface != null && !widget.getFirstDraw()) { + aFirstDrawCallback = () -> { + widget.setFirstDraw(true); + updateWidget(widget); + }; + } + + widget.setSurface(aSurface, aWidth, aHeight, aFirstDrawCallback); + + View view = (View) widget; + // Add widget to a virtual display for invalidation + if (aSurface != null && view.getParent() == null) { + mWidgetContainer.addView(view, new FrameLayout.LayoutParams(aWidth, aHeight)); + } else if (aSurface == null && view.getParent() != null) { + mWidgetContainer.removeView(view); + } + view.postInvalidate(); + }); + } + @Keep @SuppressWarnings("unused") void handleMotionEvent(final int aHandle, final int aDevice, final boolean aPressed, final float aX, final float aY) { @@ -428,7 +460,14 @@ void handleMotionEvent(final int aHandle, final int aDevice, final boolean aPres Widget widget = mWidgets.get(aHandle); if (widget == null) { MotionEventGenerator.dispatch(mRootWidget, aDevice, aPressed, aX, aY); - + } else if (widget == mBrowserWidget && mBrowserWidget.getBorderWidth() > 0) { + + final float extra = mBrowserWidget.getBorderWidth() * 2.0f; + final float rx = aX / mBrowserWidget.getTextureWidth(); + final float ry = aY/ mBrowserWidget.getTextureHeight(); + final float x = rx * (mBrowserWidget.getTextureWidth() - extra); + final float y = ry * (mBrowserWidget.getTextureHeight() - extra); + MotionEventGenerator.dispatch(widget, aDevice, aPressed, x, y); } else { MotionEventGenerator.dispatch(widget, aDevice, aPressed, aX, aY); } @@ -582,6 +621,12 @@ public boolean isOverrideEnvPathEnabled() { return SettingsStore.getInstance(this).isEnvironmentOverrideEnabled(); } + @Keep + @SuppressWarnings("unused") + public boolean areLayersEnabled() { + return SettingsStore.getInstance(this).getLayersEnabled(); + } + @Keep @SuppressWarnings("unused") public String getActiveEnvironment() { @@ -657,7 +702,7 @@ public void updateWidget(final Widget aWidget) { params.width = textureWidth; params.height = textureHeight; ((View)aWidget).setLayoutParams(params); - aWidget.resizeSurfaceTexture(textureWidth, textureHeight); + aWidget.resizeSurface(textureWidth, textureHeight); } boolean visible = aWidget.getPlacement().visible; @@ -803,7 +848,7 @@ public void setTrayVisible(boolean visible) { mTray.show(); } else { - mTray.hide(); + mTray.hide(false); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java index 9d752606c8..a2e07f898e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java @@ -162,6 +162,9 @@ public void setContext(Context aContext, Bundle aExtras) { runtimeSettingsBuilder.displayDpiOverride(SettingsStore.getInstance(aContext).getDisplayDpi()); runtimeSettingsBuilder.screenSizeOverride(SettingsStore.getInstance(aContext).getMaxWindowWidth(), SettingsStore.getInstance(aContext).getMaxWindowHeight()); + if (SettingsStore.getInstance(aContext).getLayersEnabled()) { + runtimeSettingsBuilder.useMaxScreenDepth(true); + } if (BuildConfig.DEBUG) { runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" }); @@ -492,23 +495,22 @@ public String getCurrentUri() { } public Media getFullScreenVideo() { - Media result = null; if (mCurrentSession != null) { State state = mSessions.get(mCurrentSession.hashCode()); if (state == null) { - return result; - } - if (state.mMediaElements.size() > 0) { - return state.mMediaElements.get(state.mMediaElements.size() - 1); + return null; } for (Media media: state.mMediaElements) { if (media.isFullscreen()) { - result = media; - break; + return media; } } + if (state.mMediaElements.size() > 0) { + return state.mMediaElements.get(state.mMediaElements.size() - 1); + } } - return result; + + return null; } public boolean isInputActive(int aSessionId) { @@ -646,6 +648,12 @@ private void vrPrefsWorkAround(Context aContext, Bundle aExtras) { out.write("pref(\"webgl.enable-surface-texture\", true);\n".getBytes()); out.write("pref(\"apz.allow_double_tap_zooming\", false);\n".getBytes()); out.write("pref(\"dom.webcomponents.customelements.enabled\", true);\n".getBytes()); + if (SettingsStore.getInstance(aContext).getLayersEnabled()) { + out.write("pref(\"gfx.compositor.override.clear-color.r\", \"0.0\");".getBytes()); + out.write("pref(\"gfx.compositor.override.clear-color.g\", \"0.0\");".getBytes()); + out.write("pref(\"gfx.compositor.override.clear-color.b\", \"0.0\");".getBytes()); + out.write("pref(\"gfx.compositor.override.clear-color.a\", \"0.0\");".getBytes()); + } // Uncomment this to enable WebRender. WARNING NOT READY FOR USAGE. // out.write("pref(\"gfx.webrender.all\", true);\n".getBytes()); int msaaLevel = SettingsStore.getInstance(aContext).getMSAALevel(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java index 9ce2c3d456..e593e922cf 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -9,6 +9,7 @@ import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.telemetry.TelemetryHolder; +import org.mozilla.vrbrowser.BuildConfig; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; @@ -305,4 +306,11 @@ public void setMSAALevel(int level) { editor.commit(); } + public boolean getLayersEnabled() { + if (BuildConfig.FLAVOR_platform.equalsIgnoreCase("oculusvr")) { + return true; + } + return false; + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java index c678ddd74b..1627ee6a67 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java @@ -172,7 +172,7 @@ public void releaseWidget() { @Override protected void onDismiss() { - hide(); + hide(true); if (mPromptDelegate != null) { mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrowserWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrowserWidget.java index bdb2de66e3..9ac6eabca7 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrowserWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrowserWidget.java @@ -43,11 +43,13 @@ public class BrowserWidget extends View implements Widget, SessionStore.SessionC private ChoicePromptWidget mChoicePrompt; private int mWidthBackup; private int mHeightBackup; + private int mBorderWidth; public BrowserWidget(Context aContext, int aSessionId) { super(aContext); mSessionId = aSessionId; mWidgetManager = (WidgetManagerDelegate) aContext; + mBorderWidth = SettingsStore.getInstance(aContext).getLayersEnabled() ? 1 : 0; SessionStore.get().addSessionChangeListener(this); SessionStore.get().addPromptListener(this); setFocusable(true); @@ -73,6 +75,7 @@ private void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.translationZ = WidgetPlacement.unitFromMeters(context, R.dimen.browser_world_z); aPlacement.anchorX = 0.5f; aPlacement.anchorY = 0.0f; + aPlacement.visible = true; } public void pauseCompositor() { @@ -91,18 +94,22 @@ public void resumeCompositor() { return; } - mDisplay.surfaceChanged(mSurface, mWidth, mHeight); + callSurfaceChanged(); } - public void enableVRVideoMode(int aVideoWidth, int aVideoHeight) { + public void enableVRVideoMode(int aVideoWidth, int aVideoHeight, boolean aResetBorder) { mWidthBackup = mWidth; mHeightBackup = mHeight; - if (aVideoWidth == mWidth && aVideoHeight == mHeight) { + boolean borderChanged = aResetBorder && mBorderWidth > 0; + if (aVideoWidth == mWidth && aVideoHeight == mHeight && !borderChanged) { return; } + if (aResetBorder) { + mBorderWidth = 0; + } mWidgetPlacement.width = aVideoWidth; mWidgetPlacement.height = aVideoHeight; - resizeSurfaceTexture(aVideoWidth, aVideoHeight); + resizeSurface(aVideoWidth, aVideoHeight); Log.e(LOGTAG, "onMetadataChange resize browser " + aVideoWidth + " " + aVideoHeight); } @@ -110,12 +117,14 @@ public void disableVRVideoMode() { if (mWidthBackup == 0 || mHeightBackup == 0) { return; } - if (mWidthBackup == mWidth && mHeightBackup == mHeight) { + int border = SettingsStore.getInstance(getContext()).getLayersEnabled() ? 1 : 0; + if (mWidthBackup == mWidth && mHeightBackup == mHeight && border == mBorderWidth) { return; } + mBorderWidth = border; mWidgetPlacement.width = mWidthBackup; mWidgetPlacement.height = mHeightBackup; - resizeSurfaceTexture(mWidthBackup, mWidthBackup); + resizeSurface(mWidthBackup, mWidthBackup); } public void setBrowserSize(float windowWidth, float windowHeight, float multiplier) { @@ -130,6 +139,18 @@ public void setBrowserSize(float windowWidth, float windowHeight, float multipli handleResizeEvent(targetWidth, targetHeight); } + public int getBorderWidth() { + return mBorderWidth; + } + + public int getTextureWidth() { + return mWidth; + } + + public int getTextureHeight() { + return mHeight; + } + @Override public void setSurfaceTexture(SurfaceTexture aTexture, final int aWidth, final int aHeight) { GeckoSession session = SessionStore.get().getSession(mSessionId); @@ -146,15 +167,45 @@ public void setSurfaceTexture(SurfaceTexture aTexture, final int aWidth, final i } else { Log.e(LOGTAG, "GeckoDisplay was not null in BrowserWidget.setSurfaceTexture()"); } - mDisplay.surfaceChanged(mSurface, aWidth, aHeight); + callSurfaceChanged(); + } + + @Override + public void setSurface(Surface aSurface, final int aWidth, final int aHeight, Runnable aFirstDrawCallback) { + GeckoSession session = SessionStore.get().getSession(mSessionId); + if (session == null) { + return; + } + mWidth = aWidth; + mHeight = aHeight; + mSurface = aSurface; + if (mDisplay == null) { + mDisplay = session.acquireDisplay(); + } else { + Log.e(LOGTAG, "GeckoDisplay was not null in BrowserWidget.setSurfaceTexture()"); + } + if (mSurface != null) { + callSurfaceChanged(); + } else { + mDisplay.surfaceDestroyed(); + } + if (aFirstDrawCallback != null) { + aFirstDrawCallback.run(); + } + } + + private void callSurfaceChanged() { + mDisplay.surfaceChanged(mSurface, mBorderWidth, mBorderWidth, mWidth - mBorderWidth * 2, mHeight - mBorderWidth * 2); } @Override - public void resizeSurfaceTexture(final int aWidth, final int aHeight) { + public void resizeSurface(final int aWidth, final int aHeight) { mWidth = aWidth; mHeight = aHeight; - mSurfaceTexture.setDefaultBufferSize(aWidth, aHeight); - mDisplay.surfaceChanged(mSurface, aWidth, aHeight); + if (mSurfaceTexture != null) { + mSurfaceTexture.setDefaultBufferSize(aWidth, aHeight); + } + callSurfaceChanged(); } @Override @@ -281,7 +332,7 @@ public void onCurrentSessionChange(GeckoSession aSession, int aId) { mSessionId = aId; mDisplay = aSession.acquireDisplay(); Log.d(LOGTAG, "surfaceChanged: " + aId); - mDisplay.surfaceChanged(mSurface, mWidth, mHeight); + callSurfaceChanged(); aSession.getTextInput().setView(this); boolean isPrivateMode = aSession.getSettings().getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE); @@ -410,7 +461,7 @@ public void onChoicePrompt(GeckoSession session, String title, String msg, int t @Override public void onDismissed(String[] ids) { callback.confirm(ids); - mChoicePrompt.hide(); + mChoicePrompt.hide(true); } }); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/CrashDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/CrashDialogWidget.java index bc701e9ab8..fbfb4af844 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/CrashDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/CrashDialogWidget.java @@ -91,7 +91,7 @@ private void initialize(Context aContext) { mAudio.playSound(AudioEngine.Sound.CLICK); } - hide(); + hide(true); if(mCrashDialogDelegate != null) { mCrashDialogDelegate.onSendData(); @@ -142,8 +142,8 @@ public void show() { } @Override - public void hide() { - super.hide(); + public void hide(boolean aRemove) { + super.hide(aRemove); mWidgetManager.popWorldBrightness(this); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/DeveloperOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/DeveloperOptionsWidget.java index 60ee0ad396..4804c7ac2b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/DeveloperOptionsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/DeveloperOptionsWidget.java @@ -184,7 +184,7 @@ public void show() { } private void showRestartDialog() { - hide(); + hide(true); UIWidget widget = getChild(mRestartDialogHandle); if (widget == null) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java index c191695ae4..ee0df6ef59 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java @@ -318,9 +318,7 @@ public void onPlaybackRateChange(MediaElement mediaElement, double rate) { @Override public void onFullscreenChange(MediaElement mediaElement, boolean fullscreen) { - if (!fullscreen) { - hide(); - } + } @Override diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java index 717cafbf42..9cda6e8b42 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.SharedPreferences; +import android.graphics.Canvas; import android.net.Uri; import android.preference.PreferenceManager; import android.support.annotation.NonNull; @@ -331,9 +332,6 @@ public void onClick(View view) { mVoiceSearchWidget = createChild(VoiceSearchWidget.class, false); mVoiceSearchWidget.setDelegate(this); - mPopup = createChild(SuggestionsWidget.class); - mPopup.setURLBarPopupDelegate(this); - mSearchEngineWrapper = SearchEngineWrapper.get(getContext()); SessionStore.get().addSessionChangeListener(this); @@ -369,6 +367,11 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.opaque = false; } + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + } + public void setBrowserWidget(BrowserWidget aWidget) { if (aWidget != null) { mWidgetPlacement.parentHandle = aWidget.getHandle(); @@ -485,7 +488,9 @@ private void enterVRVideo(@VideoProjectionMenuWidget.VideoProjectionFlags int aP this.setVisible(false); if (fullscreenMedia != null && fullscreenMedia.getWidth() > 0 && fullscreenMedia.getHeight() > 0) { - mBrowserWidget.enableVRVideoMode(fullscreenMedia.getWidth(), fullscreenMedia.getHeight()); + boolean resetBorder = aProjection == VideoProjectionMenuWidget.VIDEO_PROJECTION_360 || + aProjection == VideoProjectionMenuWidget.VIDEO_PROJECTION_360_STEREO; + mBrowserWidget.enableVRVideoMode(fullscreenMedia.getWidth(), fullscreenMedia.getHeight(), resetBorder); } mBrowserWidget.setVisible(false); @@ -763,73 +768,76 @@ public void onCurrentSessionChange(GeckoSession aSession, int aId) { @Override public void OnVoiceSearchClicked() { if (mVoiceSearchWidget.isVisible()) { - mVoiceSearchWidget.hide(); + mVoiceSearchWidget.hide(true); } else { mVoiceSearchWidget.show(); } } + @Override public void OnShowSearchPopup() { - if (mPopup != null) { - final String text = mURLBar.getText().trim(); - final String originalText = mURLBar.getOriginalText().trim(); - if (originalText.length() > 0) { - mSearchEngineWrapper.getSuggestions( - originalText, - (suggestions) -> { - ArrayList items = new ArrayList<>(); - - if (!text.equals(originalText)) { - // Completion from browser-domains - items.add(SuggestionsWidget.SuggestionItem.create( - text, - getSearchURLOrDomain(text), - null, - SuggestionsWidget.SuggestionItem.Type.COMPLETION - )); - } - - // Original text - items.add(SuggestionsWidget.SuggestionItem.create( - originalText, - getSearchURLOrDomain(originalText), - null, - SuggestionsWidget.SuggestionItem.Type.SUGGESTION - )); - - // Suggestions - for (String suggestion : suggestions) { - String url = mSearchEngineWrapper.getSearchURL(suggestion); - items.add(SuggestionsWidget.SuggestionItem.create( - suggestion, - url, - null, - SuggestionsWidget.SuggestionItem.Type.SUGGESTION - )); - } - mPopup.setItems(items); - mPopup.setHighlightedText(originalText); - - if (!mPopup.isVisible()) { - mPopup.getPlacement().width = (int) (WidgetPlacement.convertPixelsToDp(getContext(), mURLBar.getWidth())); - mPopup.updatePlacement(); - mPopup.show(); - } - } - ); + if (mPopup == null) { + mPopup = createChild(SuggestionsWidget.class); + mPopup.setURLBarPopupDelegate(this); + } - } else { - mPopup.hide(); - } + final String text = mURLBar.getText().trim(); + final String originalText = mURLBar.getOriginalText().trim(); + if (originalText.length() <= 0) { + mPopup.setVisible(false); + return; } + + mSearchEngineWrapper.getSuggestions( + originalText, + (suggestions) -> { + ArrayList items = new ArrayList<>(); + + if (!text.equals(originalText)) { + // Completion from browser-domains + items.add(SuggestionsWidget.SuggestionItem.create( + text, + getSearchURLOrDomain(text), + null, + SuggestionsWidget.SuggestionItem.Type.COMPLETION + )); + } + + // Original text + items.add(SuggestionsWidget.SuggestionItem.create( + originalText, + getSearchURLOrDomain(originalText), + null, + SuggestionsWidget.SuggestionItem.Type.SUGGESTION + )); + + // Suggestions + for (String suggestion : suggestions) { + String url = mSearchEngineWrapper.getSearchURL(suggestion); + items.add(SuggestionsWidget.SuggestionItem.create( + suggestion, + url, + null, + SuggestionsWidget.SuggestionItem.Type.SUGGESTION + )); + } + mPopup.setItems(items); + mPopup.setHighlightedText(originalText); + + if (!mPopup.isVisible()) { + mPopup.updatePlacement((int)WidgetPlacement.convertPixelsToDp(getContext(), mURLBar.getWidth())); + mPopup.show(); + } + } + ); } @Override public void OnHideSearchPopup() { - if (mPopup != null && mPopup.isVisible()) { - mPopup.hide(); + if (mPopup != null) { + mPopup.setVisible(false); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/PermissionWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/PermissionWidget.java index 03e3779ffb..79494c89b7 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/PermissionWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/PermissionWidget.java @@ -95,8 +95,8 @@ public void show() { } @Override - public void hide() { - super.hide(); + public void hide(boolean aRemove) { + super.hide(aRemove); mWidgetManager.popWorldBrightness(this); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SettingsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SettingsWidget.java index 334167ae52..928e0137c2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SettingsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SettingsWidget.java @@ -199,7 +199,7 @@ private void onSettingsPrivacyClick() { SessionStore.get().loadUri(getContext().getString(R.string.private_policy_url)); - hide(); + hide(true); } private void onSettingsReportClick() { @@ -229,7 +229,7 @@ private void onSettingsReportClick() { } SessionStore.get().loadUri(getContext().getString(R.string.private_report_url, url)); - hide(); + hide(true); } private void onDeveloperOptionsClick() { @@ -275,7 +275,7 @@ private String versionCodeToDate(final int aVersionCode) { private void showDeveloperOptionsDialog() { mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); - hide(); + hide(true); UIWidget widget = getChild(mDeveloperOptionsDialogHandle); if (widget == null) { widget = createChild(DeveloperOptionsWidget.class, false); @@ -316,8 +316,8 @@ public void show() { } @Override - public void hide() { - super.hide(); + public void hide(boolean aRemove) { + super.hide(aRemove); mWidgetManager.popWorldBrightness(this); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java index f214814bca..ebae4a2811 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java @@ -75,7 +75,7 @@ public void onAnimationStart(Animation animation) { @Override public void onAnimationEnd(Animation animation) { - ThreadUtils.postToUiThread(() -> SuggestionsWidget.super.hide()); + ThreadUtils.postToUiThread(() -> SuggestionsWidget.super.hide(false)); } @Override @@ -119,16 +119,10 @@ public void show() { } @Override - public void hide() { + public void hide(boolean aRemove) { mList.startAnimation(mScaleDownAnimation); } - @Override - public void handleResizeEvent(float aWorldWidth, float aWorldHeight) { - mWidgetPlacement.worldWidth = aWorldWidth * (mWidgetPlacement.width/getWorldWidth()); - mWidgetManager.updateWidget(this); - } - // FocusChangeListener @Override @@ -152,16 +146,16 @@ public void setItems(List items) { mList.setAdapter(mAdapter); } - public void updatePlacement() { + public void updatePlacement(int aWidth) { + mWidgetPlacement.width = aWidth; float worldWidth = WidgetPlacement.floatDimension(getContext(), R.dimen.browser_world_width); float aspect = mWidgetPlacement.width / mWidgetPlacement.height; float worldHeight = worldWidth / aspect; float area = worldWidth * worldHeight; float targetWidth = (float) Math.sqrt(area * aspect); - float targetHeight = (float) Math.sqrt(area / aspect); - handleResizeEvent(targetWidth, targetHeight); + mWidgetPlacement.worldWidth = targetWidth * (mWidgetPlacement.width/getWorldWidth()); } public static class SuggestionItem { @@ -312,7 +306,7 @@ public View getView(int position, View convertView, ViewGroup parent) { mAudio.playSound(AudioEngine.Sound.CLICK); } - hide(); + hide(false); requestFocus(); requestFocusFromTouch(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java index 0bdf4aee80..5d30f1bf36 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java @@ -109,7 +109,6 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.parentAnchorY = 0.5f; aPlacement.rotationAxisX = 1.0f; aPlacement.rotation = -45.0f; - aPlacement.opaque = false; } @Override @@ -158,7 +157,7 @@ private void toggleSettingsDialog() { } if (widget.isVisible()) { - widget.hide(); + widget.hide(true); } else { widget.show(); } @@ -173,7 +172,7 @@ public void show() { } @Override - public void hide() { + public void hide(boolean aRemove) { if (mWidgetPlacement.visible) { mWidgetPlacement.visible = false; mWidgetManager.removeWidget(this); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UISurfaceTextureRenderer.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UISurfaceTextureRenderer.java index 4a9ff8894a..4397e1b094 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UISurfaceTextureRenderer.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UISurfaceTextureRenderer.java @@ -29,13 +29,21 @@ class UISurfaceTextureRenderer { mSurface = new Surface(mSurfaceTexture); } + UISurfaceTextureRenderer(Surface aSurface, int aWidth, int aHeight) { + mTextureWidth = aWidth; + mTextureHeight = aHeight; + mSurface = aSurface; + } + void resize(int aWidth, int aHeight) { if (aWidth == mTextureWidth && aHeight == mTextureHeight) { return; } mTextureWidth = aWidth; mTextureHeight = aHeight; - mSurfaceTexture.setDefaultBufferSize(aWidth, aHeight); + if (mSurfaceTexture != null) { + mSurfaceTexture.setDefaultBufferSize(aWidth, aHeight); + } } void release() { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java index 69d8cb2cc6..998b97aaad 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java @@ -13,6 +13,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; +import android.view.Surface; import android.view.View; import android.view.ViewParent; import android.widget.FrameLayout; @@ -41,6 +42,7 @@ public interface Delegate { protected Runnable mBackHandler; protected HashMap mChildren; protected Delegate mDelegate; + private Runnable mFirstDrawCallback; public UIWidget(Context aContext) { super(aContext); @@ -65,6 +67,11 @@ private void initialize() { mInitialWidth = mWidgetPlacement.width; mInitialHeight = mWidgetPlacement.height; mWorldWidth = WidgetPlacement.pixelDimension(getContext(), R.dimen.world_width); + // Transparent border useful for TimeWarp Layers and better aliasing. + int padding_dp = 1; // 1 dps + final float scale = getResources().getDisplayMetrics().density; + int padding_px = (int) (padding_dp * scale + 0.5f); + this.setPadding(padding_px, padding_px, padding_px, padding_px); mChildren = new HashMap<>(); mBackHandler = new Runnable() { @@ -87,6 +94,7 @@ public void setSurfaceTexture(SurfaceTexture aTexture, final int aWidth, final i mTexture = aTexture; if (mRenderer != null) { mRenderer.release(); + mRenderer = null; } if (aTexture != null) { mRenderer = new UISurfaceTextureRenderer(aTexture, aWidth, aHeight); @@ -95,7 +103,20 @@ public void setSurfaceTexture(SurfaceTexture aTexture, final int aWidth, final i } @Override - public void resizeSurfaceTexture(final int aWidth, final int aHeight) { + public void setSurface(Surface aSurface, final int aWidth, final int aHeight, Runnable aFirstDrawCallback) { + mFirstDrawCallback = aFirstDrawCallback; + if (mRenderer != null) { + mRenderer.release(); + mRenderer = null; + } + if (aSurface != null) { + mRenderer = new UISurfaceTextureRenderer(aSurface, aWidth, aHeight); + } + setWillNotDraw(mRenderer == null); + } + + @Override + public void resizeSurface(final int aWidth, final int aHeight) { if (mRenderer != null){ mRenderer.resize(aWidth, aHeight); } @@ -180,6 +201,10 @@ public void draw(Canvas aCanvas) { super.draw(textureCanvas); } mRenderer.drawEnd(); + if (mFirstDrawCallback != null) { + mFirstDrawCallback.run(); + mFirstDrawCallback = null; + } } @Override @@ -209,7 +234,7 @@ public void setDelegate(Delegate aDelegate) { public void toggle() { if (isVisible()) { - hide(); + hide(true); } else { show(); @@ -233,16 +258,18 @@ public void show(boolean focus) { } } - public void hide() { + public void hide(boolean aRemove) { for (UIWidget child : mChildren.values()) { if (child.isVisible()) { - child.hide(); + child.hide(aRemove); } } if (mWidgetPlacement.visible) { mWidgetPlacement.visible = false; - mWidgetManager.removeWidget(this); + if (aRemove) { + mWidgetManager.removeWidget(this); + } mWidgetManager.popBackHandler(mBackHandler); } @@ -300,7 +327,7 @@ protected T getChild(int aChildId) { protected void onDismiss() { - hide(); + hide(true); if (mDelegate != null) { mDelegate.onDismiss(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VoiceSearchWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VoiceSearchWidget.java index 5778086e19..7e962900f3 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VoiceSearchWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VoiceSearchWidget.java @@ -180,7 +180,7 @@ public void run() { float confidence = ((STTResult)aPayload).mConfidence; if (mDelegate != null) mDelegate.OnVoiceSearchResult(transcription, confidence); - hide(); + hide(true); break; case START_LISTEN: // Handle when the api successfully opened the microphone and started listening @@ -267,8 +267,8 @@ public void show() { } @Override - public void hide() { - super.hide(); + public void hide(boolean aRemove) { + super.hide(aRemove); mMozillaSpeechService.removeListener(mVoiceSearchListener); stopVoiceSearch(); @@ -372,7 +372,7 @@ public void onGlobalFocusChanged(View oldFocus, View newFocus) { } if (isVisible() && shouldHide) { - hide(); + hide(true); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java index 858c31cdd3..297b757b3a 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java @@ -7,10 +7,12 @@ import android.graphics.SurfaceTexture; import android.view.MotionEvent; +import android.view.Surface; public interface Widget { void setSurfaceTexture(SurfaceTexture aTexture, final int aWidth, final int aHeight); - void resizeSurfaceTexture(final int aWidth, final int aHeight); + void setSurface(Surface aSurface, final int aWidth, final int aHeight, Runnable aFirstDrawCallback); + void resizeSurface(final int aWidth, final int aHeight); int getHandle(); WidgetPlacement getPlacement(); void handleTouchEvent(MotionEvent aEvent); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java index 2f21fb147a..dee2590d63 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java @@ -38,6 +38,7 @@ public WidgetPlacement(Context aContext) { public boolean opaque = false; public boolean showPointer = true; public boolean firstDraw = false; + public boolean layer = true; public WidgetPlacement clone() { WidgetPlacement w = new WidgetPlacement(); diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp index 04fcea7452..e359fa45d9 100644 --- a/app/src/main/cpp/BrowserWorld.cpp +++ b/app/src/main/cpp/BrowserWorld.cpp @@ -6,19 +6,21 @@ #include "BrowserWorld.h" #include "Controller.h" #include "ControllerContainer.h" -#include "FadeBlitter.h" +#include "FadeAnimation.h" #include "Device.h" #include "DeviceDelegate.h" #include "ExternalBlitter.h" #include "ExternalVR.h" #include "GeckoSurfaceTexture.h" #include "LoadingAnimation.h" +#include "Skybox.h" #include "SplashAnimation.h" #include "Widget.h" #include "WidgetPlacement.h" -#include "VRBrowser.h" #include "Quad.h" +#include "VRBrowser.h" #include "VRVideo.h" +#include "VRLayer.h" #include "vrb/CameraSimple.h" #include "vrb/Color.h" #include "vrb/ConcreteClass.h" @@ -155,8 +157,8 @@ struct BrowserWorld::State { ExternalVRPtr externalVR; ExternalBlitterPtr blitter; bool windowsInitialized; - TogglePtr skybox; - FadeBlitterPtr fadeBlitter; + SkyboxPtr skybox; + FadeAnimationPtr fadeAnimation; uint32_t loaderDelay; bool exitImmersiveRequested; WidgetPtr resizingWidget; @@ -183,7 +185,7 @@ struct BrowserWorld::State { controllers = ControllerContainer::Create(create); externalVR = ExternalVR::Create(); blitter = ExternalBlitter::Create(create); - fadeBlitter = FadeBlitter::Create(create); + fadeAnimation = FadeAnimation::Create(create); loadingAnimation = LoadingAnimation::Create(create); splashAnimation = SplashAnimation::Create(create); } @@ -429,7 +431,7 @@ BrowserWorld::RegisterDeviceDelegate(DeviceDelegatePtr aDelegate) { m.device = aDelegate; if (m.device) { m.device->RegisterImmersiveDisplay(m.externalVR); - m.device->SetClearColor(vrb::Color(0.0f, 0.0f, 0.0f)); + m.device->SetClearColor(vrb::Color(0.0f, 0.0f, 0.0f, 0.0f)); m.leftCamera = m.device->GetCamera(device::Eye::Left); m.rightCamera = m.device->GetCamera(device::Eye::Right); ControllerDelegatePtr delegate = m.controllers; @@ -507,11 +509,15 @@ BrowserWorld::InitializeJava(JNIEnv* aEnv, jobject& aActivity, jobject& aAssetMa } } #if !defined(SNAPDRAGONVR) - m.skybox = CreateSkyBox(skyboxPath.c_str()); - m.rootOpaqueParent->AddNode(m.skybox); + CreateSkyBox(skyboxPath.c_str()); // Don't load the env model, we are going for skyboxes in v1.0 // CreateFloor(); #endif + m.fadeAnimation->SetFadeChangeCallback([=](const vrb::Color& aTintColor) { + if (m.skybox) { + m.skybox->SetTintColor(aTintColor); + } + }); m.modelsLoaded = true; } } @@ -531,7 +537,7 @@ BrowserWorld::InitializeGL() { return; } if (m.splashAnimation) { - m.splashAnimation->Load(); + m.splashAnimation->Load(m.context, m.device); } // delay the m.loader->InitializeGL() call to fix some issues with Daydream activities m.loaderDelay = 3; @@ -641,9 +647,7 @@ BrowserWorld::UpdateEnvironment() { ASSERT_ON_RENDER_THREAD(); std::string env = VRBrowser::GetActiveEnvironment(); VRB_LOG("Setting environment: %s", env.c_str()); - m.rootOpaqueParent->RemoveNode(*m.skybox); - m.skybox = CreateSkyBox(env.c_str()); - m.rootOpaqueParent->AddNode(m.skybox); + CreateSkyBox(env.c_str()); } void @@ -686,11 +690,22 @@ BrowserWorld::AddWidget(int32_t aHandle, const WidgetPlacementPtr& aPlacement) { worldWidth = aPlacement->width * kWorldDPIRatio; } - WidgetPtr widget = Widget::Create(m.context, - aHandle, - (int32_t)(ceilf(aPlacement->width * aPlacement->density)), - (int32_t)(ceilf(aPlacement->height * aPlacement->density)), - worldWidth); + int32_t textureWidth = (int32_t)(ceilf(aPlacement->width * aPlacement->density)); + int32_t textureHeight = (int32_t)(ceilf(aPlacement->height * aPlacement->density)); + + WidgetPtr widget; + VRLayerQuadPtr layer; + if (aPlacement->layer) { + layer = m.device->CreateLayerQuad(textureWidth, textureHeight, + VRLayerQuad::SurfaceType::AndroidSurface); + } + + if (layer) { + widget = Widget::Create(m.context, aHandle, layer, worldWidth); + } else { + widget = Widget::Create(m.context, aHandle, textureWidth, textureHeight, worldWidth); + } + if (aPlacement->opaque) { m.rootOpaque->AddNode(widget->GetRoot()); } else { @@ -754,6 +769,9 @@ BrowserWorld::RemoveWidget(int32_t aHandle) { if (it != m.widgets.end()) { m.widgets.erase(it); } + if (widget->GetLayer()) { + m.device->DeleteLayer(widget->GetLayer()); + } } } @@ -832,7 +850,7 @@ BrowserWorld::LayoutWidget(int32_t aHandle) { void BrowserWorld::SetBrightness(const float aBrightness) { ASSERT_ON_RENDER_THREAD(); - m.fadeBlitter->SetBrightness(aBrightness); + m.fadeAnimation->SetBrightness(aBrightness); } void @@ -850,23 +868,26 @@ BrowserWorld::ShowVRVideo(const int aWindowHandle, const int aVideoProjection) { } auto projection = static_cast(aVideoProjection); - m.vrVideo = VRVideo::Create(m.create, widget, projection); + m.vrVideo = VRVideo::Create(m.create, widget, projection, m.device); if (m.skybox && projection != VRVideo::VRVideoProjection::VIDEO_PROJECTION_3D_SIDE_BY_SIDE) { - m.skybox->ToggleAll(false); + m.skybox->SetVisible(false); } - if (m.fadeBlitter && projection != VRVideo::VRVideoProjection::VIDEO_PROJECTION_3D_SIDE_BY_SIDE) { - m.fadeBlitter->SetVisible(false); + if (m.fadeAnimation && projection != VRVideo::VRVideoProjection::VIDEO_PROJECTION_3D_SIDE_BY_SIDE) { + m.fadeAnimation->SetVisible(false); } } void BrowserWorld::HideVRVideo() { + if (m.vrVideo) { + m.vrVideo->Exit(); + } m.vrVideo = nullptr; if (m.skybox) { - m.skybox->ToggleAll(true); + m.skybox->SetVisible(true); } - if (m.fadeBlitter) { - m.fadeBlitter->SetVisible(true); + if (m.fadeAnimation) { + m.fadeAnimation->SetVisible(true); } } @@ -908,14 +929,15 @@ void BrowserWorld::DrawWorld() { m.externalVR->SetCompositorEnabled(true); m.device->SetRenderMode(device::RenderMode::StandAlone); + if (m.fadeAnimation) { + m.fadeAnimation->UpdateAnimation(); + } vrb::Vector headPosition = m.device->GetHeadTransform().GetTranslation(); vrb::Vector headDirection = m.device->GetHeadTransform().MultiplyDirection(vrb::Vector(0.0f, 0.0f, -1.0f)); if (m.skybox) { - vrb::TransformPtr t = std::dynamic_pointer_cast(m.skybox->GetNode(0)); - t->SetTransform(vrb::Matrix::Translation(headPosition)); + m.skybox->SetTransform(vrb::Matrix::Translation(headPosition)); } m.rootTransparent->SortNodes([=](const NodePtr& a, const NodePtr& b) { - const float kMaxFloat = 9999999.0f; float da = DistanceToPlane(GetWidgetFromNode(a), headPosition, headDirection); float db = DistanceToPlane(GetWidgetFromNode(b), headPosition, headDirection); if (da < 0.0f) { @@ -927,7 +949,6 @@ BrowserWorld::DrawWorld() { return da < db; }); m.device->StartFrame(); - m.rootOpaque->SetTransform(m.device->GetReorientTransform()); m.rootTransparent->SetTransform(m.device->GetReorientTransform()); @@ -935,9 +956,6 @@ BrowserWorld::DrawWorld() { m.drawList->Reset(); m.rootOpaqueParent->Cull(*m.cullVisitor, *m.drawList); m.drawList->Draw(*m.leftCamera); - if (m.fadeBlitter && m.fadeBlitter->IsVisible()) { - m.fadeBlitter->Draw(); - } if (m.vrVideo) { m.vrVideo->SelectEye(device::Eye::Left); m.drawList->Reset(); @@ -958,9 +976,6 @@ BrowserWorld::DrawWorld() { m.drawList->Reset(); m.rootOpaqueParent->Cull(*m.cullVisitor, *m.drawList); m.drawList->Draw(*m.rightCamera); - if (m.fadeBlitter && m.fadeBlitter->IsVisible()) { - m.fadeBlitter->Draw(); - } if (m.vrVideo) { m.vrVideo->SelectEye(device::Eye::Right); m.drawList->Reset(); @@ -1048,87 +1063,33 @@ BrowserWorld::DrawSplashAnimation() { #endif // !defined(VRBROWSER_NO_VR_API) m.device->EndFrame(); if (animationFinished) { + if (m.splashAnimation && m.splashAnimation->GetLayer()) { + m.device->DeleteLayer(m.splashAnimation->GetLayer()); + } m.splashAnimation = nullptr; - if (m.fadeBlitter) { - m.fadeBlitter->FadeIn(); + if (m.fadeAnimation) { + m.fadeAnimation->FadeIn(); } } } -vrb::TogglePtr -BrowserWorld::CreateSkyBox(const std::string& basePath) { - ASSERT_ON_RENDER_THREAD(nullptr); - vrb::TogglePtr toggle = vrb::Toggle::Create(m.create); - vrb::TransformPtr transform = vrb::Transform::Create(m.create); - transform->SetTransform(Matrix::Position(vrb::Vector(0.0f, 0.0f, 0.0f))); - LoadSkybox(transform, basePath); - toggle->AddNode(transform); - return toggle; -} - void -BrowserWorld::LoadSkybox(const vrb::TransformPtr transform, const std::string &basePath) { - LoadTask task = [basePath](CreationContextPtr &aContext) -> GroupPtr { - std::array cubeVertices{ - -1.0f, 1.0f, 1.0f, // 0 - -1.0f, -1.0f, 1.0f, // 1 - 1.0f, -1.0f, 1.0f, // 2 - 1.0f, 1.0f, 1.0f, // 3 - -1.0f, 1.0f, -1.0f, // 4 - -1.0f, -1.0f, -1.0f, // 5 - 1.0f, -1.0f, -1.0f, // 6 - 1.0f, 1.0f, -1.0f, // 7 - }; - - std::array cubeIndices{ - 0, 1, 2, 3, - 3, 2, 6, 7, - 7, 6, 5, 4, - 4, 5, 1, 0, - 0, 3, 7, 4, - 1, 5, 6, 2 - }; - - VertexArrayPtr array = VertexArray::Create(aContext); - const float kLength = 140.0f; - for (int i = 0; i < cubeVertices.size(); i += 3) { - array->AppendVertex(Vector(-kLength * cubeVertices[i], -kLength * cubeVertices[i + 1], - -kLength * cubeVertices[i + 2])); - array->AppendUV(Vector(-kLength * cubeVertices[i], -kLength * cubeVertices[i + 1], - -kLength * cubeVertices[i + 2])); - } - - vrb::GeometryPtr geometry = vrb::Geometry::Create(aContext); - geometry->SetVertexArray(array); - - for (int i = 0; i < cubeIndices.size(); i += 4) { - std::vector indices = {cubeIndices[i] + 1, cubeIndices[i + 1] + 1, - cubeIndices[i + 2] + 1, cubeIndices[i + 3] + 1}; - geometry->AddFace(indices, indices, {}); - } - - RenderStatePtr state = RenderState::Create(aContext); - TextureCubeMapPtr cubemap = vrb::TextureCubeMap::Create(aContext); - cubemap->SetTextureParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - cubemap->SetTextureParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - cubemap->SetTextureParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - cubemap->SetTextureParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - cubemap->SetTextureParameter(GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - state->SetTexture(cubemap); - - auto path = [&](const std::string &name) { return basePath + "/" + name + ".jpg"; }; - vrb::TextureCubeMap::Load(aContext, cubemap, path("posx"), path("negx"), path("posy"), - path("negy"), path("posz"), path("negz")); - - state->SetMaterial(Color(1.0f, 1.0f, 1.0f), Color(1.0f, 1.0f, 1.0f), Color(0.0f, 0.0f, 0.0f), - 0.0f); - geometry->SetRenderState(state); - vrb::GroupPtr group = vrb::Transform::Create(aContext); - group->AddNode(geometry); - return group; - }; - - m.loader->RunLoadTask(transform, task); +BrowserWorld::CreateSkyBox(const std::string& basePath) { + ASSERT_ON_RENDER_THREAD(); + const bool empty = basePath == "cubemap/void"; + if (m.skybox && empty) { + m.skybox->SetVisible(false); + return; + } else if (m.skybox) { + m.skybox->SetVisible(true); + m.skybox->Load(m.loader, basePath); + return; + } else if (!empty) { + VRLayerCubePtr layer = m.device->CreateLayerCube(1024, 1024); + m.skybox = Skybox::Create(m.create, layer); + m.rootOpaqueParent->AddNode(m.skybox->GetRoot()); + m.skybox->Load(m.loader, basePath); + } } void diff --git a/app/src/main/cpp/BrowserWorld.h b/app/src/main/cpp/BrowserWorld.h index 5f48a9b169..a25a173d91 100644 --- a/app/src/main/cpp/BrowserWorld.h +++ b/app/src/main/cpp/BrowserWorld.h @@ -65,8 +65,7 @@ class BrowserWorld { void DrawImmersive(); void DrawLoadingAnimation(); void DrawSplashAnimation(); - vrb::TogglePtr CreateSkyBox(const std::string& basePath); - void LoadSkybox(const vrb::TransformPtr transform, const std::string& basePath); + void CreateSkyBox(const std::string& basePath); void CreateFloor(); float DistanceToNode(const vrb::NodePtr& aNode, const vrb::Vector& aPosition) const; WidgetPtr GetWidgetFromNode(const vrb::NodePtr& aNode) const; diff --git a/app/src/main/cpp/Device.h b/app/src/main/cpp/Device.h index d7f2b142da..68713b4113 100644 --- a/app/src/main/cpp/Device.h +++ b/app/src/main/cpp/Device.h @@ -38,6 +38,11 @@ struct EyeRect { mHeight = aRect.mHeight; return* this; } + + bool IsDefault() const { + return mX == 0.0f && mY == 0.0f && mWidth == 1.0f && mHeight == 1.0f; + } + }; } // namespace device diff --git a/app/src/main/cpp/DeviceDelegate.h b/app/src/main/cpp/DeviceDelegate.h index 9380739a17..6b7ef84269 100644 --- a/app/src/main/cpp/DeviceDelegate.h +++ b/app/src/main/cpp/DeviceDelegate.h @@ -11,8 +11,11 @@ #include "Device.h" #include "ControllerDelegate.h" #include "GestureDelegate.h" +#include "VRLayer.h" #include +#include +#include namespace crow { @@ -22,15 +25,20 @@ typedef std::shared_ptr DeviceDelegatePtr; class ImmersiveDisplay; typedef std::shared_ptr ImmersiveDisplayPtr; +class VRLayer; +typedef std::shared_ptr VRLayerPtr; +class VRLayerQuad; +typedef std::shared_ptr VRLayerQuadPtr; + class ImmersiveDisplay { public: virtual void SetDeviceName(const std::string& aName) = 0; virtual void SetCapabilityFlags(const device::CapabilityFlags aFlags) = 0; virtual void SetFieldOfView(const device::Eye aEye, const double aLeftDegrees, - const double aRightDegrees, - const double aTopDegrees, - const double aBottomDegrees) = 0; + const double aRightDegrees, + const double aTopDegrees, + const double aBottomDegrees) = 0; virtual void SetEyeOffset(const device::Eye aEye, const float aX, const float aY, const float aZ) = 0; virtual void SetEyeResolution(const int32_t aWidth, const int32_t aHeight) = 0; virtual void CompleteEnumeration() = 0; @@ -56,6 +64,12 @@ class DeviceDelegate { virtual void StartFrame() = 0; virtual void BindEye(const device::Eye aWhich) = 0; virtual void EndFrame(bool aDiscard = false) = 0; + virtual VRLayerQuadPtr CreateLayerQuad(int32_t aWidth, + int32_t aHeight, + VRLayerQuad::SurfaceType aSurfaceType) { return nullptr; } + virtual VRLayerCubePtr CreateLayerCube(int32_t aWidth, int32_t aHeight) { return nullptr; } + virtual VRLayerEquirectPtr CreateLayerEquirect(const VRLayerQuadPtr &aSource) { return nullptr; } + virtual void DeleteLayer(const VRLayerPtr& aLayer) {}; protected: DeviceDelegate() {} diff --git a/app/src/main/cpp/FadeAnimation.cpp b/app/src/main/cpp/FadeAnimation.cpp new file mode 100644 index 0000000000..dc7c402608 --- /dev/null +++ b/app/src/main/cpp/FadeAnimation.cpp @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FadeAnimation.h" +#include "vrb/ConcreteClass.h" +#include "vrb/Color.h" +#include "vrb/Logger.h" + +namespace crow { + +static float kFadeAlpha = 0.75f; +static int kAnimationLength = 40; + +struct FadeAnimation::State { + vrb::Color fadeColor; + float animationStartAlpha; + float animationEndAlpha; + int animations; + float currentBrightness; + bool visible; + FadeChangeCallback changeCallback; + State() + : fadeColor(0.0f, 0.0f, 0.0f, 0.0f) + , animationStartAlpha(0.0f) + , animationEndAlpha(0.0f) + , animations(-1) + , currentBrightness(1.0f) + , visible(true) + {} + + void NotifyChangeCallback(const vrb::Color& aTintColor) { + if (changeCallback) { + changeCallback(aTintColor); + } + } +}; + +FadeAnimationPtr +FadeAnimation::Create(vrb::CreationContextPtr& aContext) { + return std::make_shared >(aContext); +} + +bool +FadeAnimation::IsVisible() const { + return m.visible && (m.animations >= 0 || m.fadeColor.Alpha() > 0.0f); +} + +vrb::Color +FadeAnimation::GetTintColor() const { + if (IsVisible()) { + const float a = 1.0f - m.fadeColor.Alpha(); + return vrb::Color(a, a, a, 1.0f); + } else { + return vrb::Color(1.0f, 1.0f, 1.0f, 1.0f); + } +} + + +void FadeAnimation::SetBrightness(const float aBrightness) { + m.currentBrightness = aBrightness; + m.animationStartAlpha = m.fadeColor.Alpha(); + m.animationEndAlpha = 1.0f - aBrightness; + m.animations = kAnimationLength; + m.NotifyChangeCallback(GetTintColor()); +} + +void FadeAnimation::UpdateAnimation() { + if (m.animations >= 0) { + float t = (float)(kAnimationLength - m.animations) / (float) kAnimationLength; + m.fadeColor.SetAlpha(m.animationStartAlpha + (m.animationEndAlpha - m.animationStartAlpha) * t); + m.animations--; + m.NotifyChangeCallback(GetTintColor()); + } +} + +void FadeAnimation::FadeIn() { + m.animationStartAlpha = kFadeAlpha; + m.animationEndAlpha = 1.0f - m.currentBrightness; + m.animations = kAnimationLength; + m.NotifyChangeCallback(GetTintColor()); +} + +void +FadeAnimation::SetVisible(const bool aVisible) { + if (aVisible != IsVisible()) { + m.visible = aVisible; + m.NotifyChangeCallback(GetTintColor()); + } +} + +void +FadeAnimation::SetFadeChangeCallback(const FadeChangeCallback& aCallback) { + m.changeCallback = aCallback; +} + +FadeAnimation::FadeAnimation(State& aState, vrb::CreationContextPtr& aContext) + : m(aState) +{} + +FadeAnimation::~FadeAnimation() {} + +} // namespace crow diff --git a/app/src/main/cpp/FadeAnimation.h b/app/src/main/cpp/FadeAnimation.h new file mode 100644 index 0000000000..14a6aeed28 --- /dev/null +++ b/app/src/main/cpp/FadeAnimation.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VRBROWSER_FADE_ANIMATION_H +#define VRBROWSER_FADE_ANIMATION_H + +#include "vrb/MacroUtils.h" +#include "vrb/Forward.h" +#include "vrb/ResourceGL.h" +#include +#include + +namespace crow { + +class FadeAnimation; +typedef std::shared_ptr FadeAnimationPtr; + +class FadeAnimation { +public: + static FadeAnimationPtr Create(vrb::CreationContextPtr& aContext); + typedef std::function FadeChangeCallback; + + bool IsVisible() const; + vrb::Color GetTintColor() const; + void SetBrightness(const float aBrightness); + void UpdateAnimation(); + void FadeIn(); + void SetVisible(const bool aVisible); + void SetFadeChangeCallback(const FadeChangeCallback& aCallback); +protected: + struct State; + FadeAnimation(State& aState, vrb::CreationContextPtr& aContext); + ~FadeAnimation(); +private: + State& m; + FadeAnimation() = delete; + VRB_NO_DEFAULTS(FadeAnimation); +}; + +} // namespace crow + +#endif //VRBROWSER_FADE_ANIMATION_H diff --git a/app/src/main/cpp/FadeBlitter.cpp b/app/src/main/cpp/FadeBlitter.cpp deleted file mode 100644 index 4b5c9f5222..0000000000 --- a/app/src/main/cpp/FadeBlitter.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "FadeBlitter.h" -#include "vrb/ConcreteClass.h" -#include "vrb/Color.h" -#include "vrb/private/ResourceGLState.h" -#include "vrb/gl.h" -#include "vrb/GLError.h" -#include "vrb/Logger.h" -#include "vrb/ShaderUtil.h" - -#include - -namespace { -static const char* sVertexShader = R"SHADER( -attribute vec4 a_position; -void main(void) { - gl_Position = a_position; -} -)SHADER"; - -static const char* sFragmentShader = R"SHADER( -precision mediump float; - -uniform vec4 u_color; - -void main() { - gl_FragColor = u_color; -} -)SHADER"; - -static float kFadeAlpha = 0.75f; -static int kAnimationLength = 40; - -static const GLfloat sVerticies[] = { - -1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 0.0f -}; - -} - -namespace crow { - -struct FadeBlitter::State : public vrb::ResourceGL::State { - GLuint vertexShader; - GLuint fragmentShader; - GLuint program; - GLint aPosition; - GLint uColor; - vrb::Color fadeColor; - float animationStartAlpha; - float animationEndAlpha; - int animations; - float currentBrightness; - bool visible; - State() - : vertexShader(0) - , fragmentShader(0) - , program(0) - , aPosition(0) - , uColor(0) - , fadeColor(0.0f, 0.0f, 0.0f, 0.0f) - , animationStartAlpha(0.0f) - , animationEndAlpha(0.0f) - , animations(-1) - , currentBrightness(1.0f) - , visible(true) - {} -}; - -FadeBlitterPtr -FadeBlitter::Create(vrb::CreationContextPtr& aContext) { - return std::make_shared >(aContext); -} - - -void -FadeBlitter::Draw() { - if (!m.program) { - VRB_ERROR("FadeBlitter::Draw Failed! Shader not found"); - return; - } - - if (m.animations >= 0) { - float t = (float)(kAnimationLength - m.animations) / (float) kAnimationLength; - m.fadeColor.SetAlpha(m.animationStartAlpha + (m.animationEndAlpha - m.animationStartAlpha) * t); - m.animations--; - } - - VRB_GL_CHECK(glDisable(GL_DEPTH_TEST)); - VRB_GL_CHECK(glDepthMask(GL_FALSE)); - - VRB_GL_CHECK(glUseProgram(m.program)); - VRB_GL_CHECK(glUniform4f(m.uColor, m.fadeColor.Red(), m.fadeColor.Green(), m.fadeColor.Blue(), m.fadeColor.Alpha())); - VRB_GL_CHECK(glVertexAttribPointer((GLuint)m.aPosition, 3, GL_FLOAT, GL_FALSE, 0, sVerticies)); - VRB_GL_CHECK(glEnableVertexAttribArray((GLuint)m.aPosition)); - VRB_GL_CHECK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - VRB_GL_CHECK(glEnable(GL_DEPTH_TEST)); - VRB_GL_CHECK(glDepthMask(GL_TRUE)); -} - -bool -FadeBlitter::IsVisible() const { - return m.visible && (m.animations >= 0 || m.fadeColor.Alpha() > 0.0f); -} - - -void FadeBlitter::SetBrightness(const float aBrightness) { - m.currentBrightness = aBrightness; - m.animationStartAlpha = m.fadeColor.Alpha(); - m.animationEndAlpha = 1.0f - aBrightness; - m.animations = kAnimationLength; -} - -void FadeBlitter::FadeIn() { - m.animationStartAlpha = kFadeAlpha; - m.animationEndAlpha = 1.0f - m.currentBrightness; - m.animations = kAnimationLength; -} - -void -FadeBlitter::SetVisible(const bool aVisible) { - m.visible = aVisible; -} - -FadeBlitter::FadeBlitter(State& aState, vrb::CreationContextPtr& aContext) - : vrb::ResourceGL(aState, aContext) - , m(aState) -{} - -FadeBlitter::~FadeBlitter() {} - -void -FadeBlitter::InitializeGL() { - m.vertexShader = vrb::LoadShader(GL_VERTEX_SHADER, sVertexShader); - m.fragmentShader = vrb::LoadShader(GL_FRAGMENT_SHADER, sFragmentShader); - if (m.vertexShader && m.fragmentShader) { - m.program = vrb::CreateProgram(m.vertexShader, m.fragmentShader); - } - if (m.program) { - m.aPosition = vrb::GetAttributeLocation(m.program, "a_position"); - m.uColor = vrb::GetUniformLocation(m.program, "u_color"); - } -} - -void -FadeBlitter::ShutdownGL() { - if (m.program) { - VRB_GL_CHECK(glDeleteProgram(m.program)); - m.program = 0; - } - if (m.vertexShader) { - VRB_GL_CHECK(glDeleteShader(m.vertexShader)); - m.vertexShader = 0; - } - if (m.fragmentShader) { - VRB_GL_CHECK(glDeleteShader(m.fragmentShader)); - m.fragmentShader = 0; - } -} - -} // namespace crow diff --git a/app/src/main/cpp/FadeBlitter.h b/app/src/main/cpp/FadeBlitter.h deleted file mode 100644 index 238da0319b..0000000000 --- a/app/src/main/cpp/FadeBlitter.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef VRBROWSER_FADE_BLITTER_H -#define VRBROWSER_FADE_BLITTER_H - -#include "vrb/MacroUtils.h" -#include "vrb/Forward.h" -#include "vrb/ResourceGL.h" -#include - -namespace crow { - -class FadeBlitter; -typedef std::shared_ptr FadeBlitterPtr; - -class FadeBlitter : protected vrb::ResourceGL { -public: - static FadeBlitterPtr Create(vrb::CreationContextPtr& aContext); - void Draw(); - bool IsVisible() const; - void SetBrightness(const float aBrightness); - void FadeIn(); - void SetVisible(const bool aVisible); -protected: - struct State; - FadeBlitter(State& aState, vrb::CreationContextPtr& aContext); - ~FadeBlitter(); - void InitializeGL() override; - void ShutdownGL() override; -private: - State& m; - FadeBlitter() = delete; - VRB_NO_DEFAULTS(FadeBlitter); -}; - -} // namespace crow - -#endif //VRBROWSER_FADE_BLITTER_H diff --git a/app/src/main/cpp/Quad.cpp b/app/src/main/cpp/Quad.cpp index b42fd8d282..36317bb57a 100644 --- a/app/src/main/cpp/Quad.cpp +++ b/app/src/main/cpp/Quad.cpp @@ -4,6 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Quad.h" +#include "VRLayer.h" +#include "VRLayerNode.h" #include "vrb/ConcreteClass.h" #include "vrb/Color.h" @@ -22,6 +24,8 @@ namespace crow { struct Quad::State { vrb::CreationContextWeak context; + VRLayerQuadPtr layer; + VRLayerNodePtr layerNode; int32_t textureWidth; int32_t textureHeight; vrb::TogglePtr root; @@ -44,9 +48,17 @@ struct Quad::State { void Initialize() { vrb::CreationContextPtr create = context.lock(); - geometry = Quad::CreateGeometry(create, worldMin, worldMax); transform = vrb::Transform::Create(create); - transform->AddNode(geometry); + if (layer) { + textureWidth = layer->GetWidth(); + textureHeight = layer->GetHeight(); + layer->SetWorldSize(GetWorldWidth(), GetWorldHeight()); + layerNode = VRLayerNode::Create(create, layer); + transform->AddNode(layerNode); + } else { + geometry = Quad::CreateGeometry(create, worldMin, worldMax); + transform->AddNode(geometry); + } root = vrb::Toggle::Create(create); root->AddNode(transform); if (scaleMode != ScaleMode::Fill) { @@ -66,7 +78,6 @@ struct Quad::State { if (textureWidth == 0|| textureHeight == 0) { return; } - vrb::VertexArrayPtr array = geometry->GetVertexArray(); vrb::Vector min = worldMin; vrb::Vector max = worldMax; @@ -75,7 +86,6 @@ struct Quad::State { const float textureAspect = (float) textureWidth / (float) textureHeight; const float worldAspect = worldWidth / worldHeight; - if (scaleMode == ScaleMode::AspectFit) { const float newWidth = worldAspect > textureAspect ? worldHeight * textureAspect : worldWidth; const float newHeight = worldAspect > textureAspect ? worldHeight : worldWidth / textureAspect; @@ -97,19 +107,30 @@ struct Quad::State { u0 = 0.5f - ul; } - array->SetUV(0, vrb::Vector(u0, v0 + vl, 0.0f)); - array->SetUV(1, vrb::Vector(u0 + ul, v0 + vl, 0.0f)); - array->SetUV(2, vrb::Vector(u0 + ul, v0, 0.0f)); - array->SetUV(3, vrb::Vector(u0, v0, 0.0f)); + if (layer) { + device::EyeRect textureRect(u0, v0, u0 + ul, v0 + vl); + layer->SetTextureRect(device::Eye::Left, textureRect); + layer->SetTextureRect(device::Eye::Right, textureRect); + } else { + vrb::VertexArrayPtr array = geometry->GetVertexArray(); + array->SetUV(0, vrb::Vector(u0, v0 + vl, 0.0f)); + array->SetUV(1, vrb::Vector(u0 + ul, v0 + vl, 0.0f)); + array->SetUV(2, vrb::Vector(u0 + ul, v0, 0.0f)); + array->SetUV(3, vrb::Vector(u0, v0, 0.0f)); + } } - const vrb::Vector bottomRight(max.x(), min.y(), min.z()); - array->SetVertex(0, min); // Bottom left - array->SetVertex(1, bottomRight); // Bottom right - array->SetVertex(2, max); // Top right - array->SetVertex(3, vrb::Vector(min.x(), max.y(), max.z())); // Top left - - geometry->UpdateBuffers(); + if (layer) { + layer->SetWorldSize(max.x() - min.x(), max.y() - min.y()); + } else { + const vrb::Vector bottomRight(max.x(), min.y(), min.z()); + vrb::VertexArrayPtr array = geometry->GetVertexArray(); + array->SetVertex(0, min); // Bottom left + array->SetVertex(1, bottomRight); // Bottom right + array->SetVertex(2, max); // Top right + array->SetVertex(3, vrb::Vector(min.x(), max.y(), max.z())); // Top left + geometry->UpdateBuffers(); + } } void LayoutBackground() { @@ -126,19 +147,21 @@ struct Quad::State { }; QuadPtr -Quad::Create(vrb::CreationContextPtr aContext, const vrb::Vector& aMin, const vrb::Vector& aMax) { +Quad::Create(vrb::CreationContextPtr aContext, const vrb::Vector& aMin, const vrb::Vector& aMax, const VRLayerQuadPtr& aLayer) { QuadPtr result = std::make_shared >(aContext); result->m.worldMin = aMin; result->m.worldMax = aMax; + result->m.layer = aLayer; result->m.Initialize(); return result; } QuadPtr -Quad::Create(vrb::CreationContextPtr aContext, const float aWorldWidth, const float aWorldHeight) { +Quad::Create(vrb::CreationContextPtr aContext, const float aWorldWidth, const float aWorldHeight, const VRLayerQuadPtr& aLayer) { QuadPtr result = std::make_shared >(aContext); result->m.worldMin = vrb::Vector(-aWorldWidth * 0.5f, -aWorldHeight * 0.5f, 0.0f); result->m.worldMax = vrb::Vector(aWorldWidth * 0.5f, aWorldHeight * 0.5f, 0.0f); + result->m.layer = aLayer; result->m.Initialize(); return result; } @@ -236,6 +259,18 @@ Quad::SetBackgroundColor(const vrb::Color& aColor) { if (m.backgroundColor == aColor || (aColor.Alpha() == 0.0f && m.backgroundColor.Alpha() == 0.0f)) { return; } + if (m.layer) { + /* + * We disable the background when using layers because there is a difficult to fix glitch. + * We can create 2 nodes to simulate the background color on the sides of the ScaleToFit mode. + * It works while resizing, but at the end, when the actual GV surface is really changed + * there is some SurfaceTexture update delay that causes a glitch. Background color becomes + * transparent or the background color is shown on top of the GV quad surface. + * This glitch doesn't happen with normal scenegraph code because the background color is shown + * on the back of the Quad surface. But with Layers we can't do that. + */ + return; + } m.backgroundColor = aColor; if (!m.backgroundGeometry) { vrb::CreationContextPtr create = m.context.lock(); @@ -252,18 +287,17 @@ Quad::SetBackgroundColor(const vrb::Color& aColor) { void Quad::GetTextureSize(int32_t& aWidth, int32_t& aHeight) const { - if (aWidth == m.textureWidth && aHeight == m.textureHeight) { - return; - } aWidth = m.textureWidth; aHeight = m.textureHeight; - m.UpdateVertexArray(); } void Quad::SetTextureSize(int32_t aWidth, int32_t aHeight) { m.textureWidth = aWidth; m.textureHeight = aHeight; + if (m.layer) { + m.layer->Resize(aWidth, aHeight); + } } void @@ -313,13 +347,27 @@ Quad::SetWorldSize(const vrb::Vector& aMin, const vrb::Vector& aMax) const { m.worldMin = aMin; m.worldMax = aMax; + if (m.layer) { + m.layer->SetWorldSize(GetWorldWidth(), GetWorldHeight()); + } + m.UpdateVertexArray(); m.LayoutBackground(); } +void +Quad::SetTintColor(const vrb::Color& aColor) { + if (m.layer) { + m.layer->SetTintColor(aColor); + } else if (m.geometry && m.geometry->GetRenderState()) { + m.geometry->GetRenderState()->SetTintColor(aColor); + } +} + vrb::Vector Quad::GetNormal() const { - return m.geometry->GetVertexArray()->GetNormal(0); + const vrb::Vector bottomRight(m.worldMax.x(), m.worldMin.y(), m.worldMin.z()); + return (bottomRight - m.worldMin).Cross(m.worldMax - m.worldMin).Normalize(); } vrb::NodePtr diff --git a/app/src/main/cpp/Quad.h b/app/src/main/cpp/Quad.h index 2d9f53855d..398d7c39dd 100644 --- a/app/src/main/cpp/Quad.h +++ b/app/src/main/cpp/Quad.h @@ -17,6 +17,10 @@ namespace crow { + +class VRLayerQuad; +typedef std::shared_ptr VRLayerQuadPtr; + class Quad; typedef std::shared_ptr QuadPtr; @@ -27,8 +31,8 @@ class Quad { AspectFit, AspectFill, }; - static QuadPtr Create(vrb::CreationContextPtr aContext, const vrb::Vector& aMin, const vrb::Vector& aMax); - static QuadPtr Create(vrb::CreationContextPtr aContext, const float aWorldWidth, const float aWorldHeight); + static QuadPtr Create(vrb::CreationContextPtr aContext, const vrb::Vector& aMin, const vrb::Vector& aMax, const VRLayerQuadPtr& aLayer = nullptr); + static QuadPtr Create(vrb::CreationContextPtr aContext, const float aWorldWidth, const float aWorldHeight, const VRLayerQuadPtr& aLayer = nullptr); static vrb::GeometryPtr CreateGeometry(vrb::CreationContextPtr aContext, const vrb::Vector& aMin, const vrb::Vector& aMax); static vrb::GeometryPtr CreateGeometry(vrb::CreationContextPtr aContext, const float aWorldWidth, const float aWorldHeight); static vrb::GeometryPtr CreateGeometry(vrb::CreationContextPtr aContext, const vrb::Vector& aMin, const vrb::Vector& aMax, const device::EyeRect& aRect); @@ -46,6 +50,7 @@ class Quad { void GetWorldSize(float& aWidth, float& aHeight) const; void SetWorldSize(const float aWidth, const float aHeight) const; void SetWorldSize(const vrb::Vector& aMin, const vrb::Vector& aMax) const; + void SetTintColor(const vrb::Color& aColor); vrb::Vector GetNormal() const; vrb::NodePtr GetRoot() const; vrb::TransformPtr GetTransformNode() const; diff --git a/app/src/main/cpp/Skybox.cpp b/app/src/main/cpp/Skybox.cpp new file mode 100644 index 0000000000..3d27f5bc97 --- /dev/null +++ b/app/src/main/cpp/Skybox.cpp @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Skybox.h" +#include "VRLayer.h" +#include "VRLayerNode.h" +#include "vrb/ConcreteClass.h" +#include "vrb/Color.h" +#include "vrb/CreationContext.h" +#include "vrb/Geometry.h" +#include "vrb/Matrix.h" +#include "vrb/ModelLoaderAndroid.h" +#include "vrb/RenderState.h" +#include "vrb/RenderContext.h" +#include "vrb/TextureCubeMap.h" +#include "vrb/Toggle.h" +#include "vrb/Transform.h" +#include "vrb/VertexArray.h" + +#include + +using namespace vrb; + +namespace crow { + +static TextureCubeMapPtr LoadTextureCube(vrb::CreationContextPtr& aContext, const std::string& aBasePath, GLuint targetTexture = 0) { + TextureCubeMapPtr cubemap = vrb::TextureCubeMap::Create(aContext, targetTexture); + cubemap->SetTextureParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); + cubemap->SetTextureParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + cubemap->SetTextureParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + cubemap->SetTextureParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + cubemap->SetTextureParameter(GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + auto path = [&](const std::string &name) { return aBasePath + "/" + name + ".jpg"; }; + vrb::TextureCubeMap::Load(aContext, cubemap, path("posx"), path("negx"), path("posy"), + path("negy"), path("posz"), path("negz")); + return cubemap; +} + +struct Skybox::State { + vrb::CreationContextWeak context; + vrb::TogglePtr root; + VRLayerCubePtr layer; + GLuint layerTextureHandle; + vrb::TransformPtr transform; + vrb::GeometryPtr geometry; + vrb::ModelLoaderAndroidPtr loader; + std::string basePath; + TextureCubeMapPtr texture; + State(): + layerTextureHandle(0) + {} + + void Initialize() { + vrb::CreationContextPtr create = context.lock(); + root = vrb::Toggle::Create(create); + transform = vrb::Transform::Create(create); + if (layer) { + root->AddNode(VRLayerNode::Create(create, layer)); + layer->SetInitializeDelegate([=](const VRLayer& aLayer) { + this->layerTextureHandle = layer->GetTextureHandle(); + LoadLayer(); + }); + } else { + root->AddNode(transform); + } + } + + void LoadGeometry() { + LoadTask task = [=](CreationContextPtr &aContext) -> GroupPtr { + std::array cubeVertices{ + -1.0f, 1.0f, 1.0f, // 0 + -1.0f, -1.0f, 1.0f, // 1 + 1.0f, -1.0f, 1.0f, // 2 + 1.0f, 1.0f, 1.0f, // 3 + -1.0f, 1.0f, -1.0f, // 4 + -1.0f, -1.0f, -1.0f, // 5 + 1.0f, -1.0f, -1.0f, // 6 + 1.0f, 1.0f, -1.0f, // 7 + }; + + std::array cubeIndices{ + 0, 1, 2, 3, + 3, 2, 6, 7, + 7, 6, 5, 4, + 4, 5, 1, 0, + 0, 3, 7, 4, + 1, 5, 6, 2 + }; + + VertexArrayPtr array = VertexArray::Create(aContext); + const float kLength = 140.0f; + for (int i = 0; i < cubeVertices.size(); i += 3) { + array->AppendVertex(Vector(-kLength * cubeVertices[i], -kLength * cubeVertices[i + 1], + -kLength * cubeVertices[i + 2])); + array->AppendUV(Vector(-kLength * cubeVertices[i], -kLength * cubeVertices[i + 1], + -kLength * cubeVertices[i + 2])); + } + + if (!geometry) { + geometry = vrb::Geometry::Create(aContext); + geometry->SetVertexArray(array); + + for (int i = 0; i < cubeIndices.size(); i += 4) { + std::vector indices = {cubeIndices[i] + 1, cubeIndices[i + 1] + 1, + cubeIndices[i + 2] + 1, cubeIndices[i + 3] + 1}; + geometry->AddFace(indices, indices, {}); + } + RenderStatePtr state = RenderState::Create(aContext); + geometry->SetRenderState(state); + } + + + texture = LoadTextureCube(aContext, basePath); + vrb::RenderStatePtr state = geometry->GetRenderState(); + state->SetTexture(texture); + state->SetMaterial(Color(1.0f, 1.0f, 1.0f), Color(1.0f, 1.0f, 1.0f), Color(0.0f, 0.0f, 0.0f), + 0.0f); + geometry->SetRenderState(state); + vrb::GroupPtr group = vrb::Transform::Create(aContext); + group->AddNode(geometry); + return group; + }; + + loader->RunLoadTask(transform, task); + } + + void LoadLayer() { + if (basePath.empty() || layerTextureHandle == 0) { + return; + } + vrb::CreationContextPtr create = context.lock(); + texture = LoadTextureCube(create, basePath, layerTextureHandle); + texture->Bind(); + layer->SetLoaded(true); + } +}; + +void +Skybox::Load(const vrb::ModelLoaderAndroidPtr& aLoader, const std::string& aBasePath) { + if (m.basePath == aBasePath) { + return; + } + m.loader = aLoader; + m.basePath = aBasePath; + if (m.layer) { + m.LoadLayer(); + } else { + m.LoadGeometry(); + } +} + +void +Skybox::SetVisible(bool aVisible) { + m.root->ToggleAll(aVisible); +} + +void +Skybox::SetTransform(const vrb::Matrix& aTransform) { + if (m.transform) { + m.transform->SetTransform(aTransform); + } +} + +void +Skybox::SetTintColor(const vrb::Color &aTintColor) { + if (m.layer) { + m.layer->SetTintColor(aTintColor); + } else if (m.geometry) { + m.geometry->GetRenderState()->SetTintColor(aTintColor); + } + +} + +vrb::NodePtr +Skybox::GetRoot() const { + return m.root; +} + + +SkyboxPtr +Skybox::Create(vrb::CreationContextPtr aContext, const VRLayerCubePtr& aLayer) { + SkyboxPtr result = std::make_shared >(aContext); + result->m.layer = aLayer; + result->m.Initialize(); + return result; +} + + +Skybox::Skybox(State& aState, vrb::CreationContextPtr& aContext) : m(aState) { + m.context = aContext; +} + +Skybox::~Skybox() {} + +} // namespace crow diff --git a/app/src/main/cpp/Skybox.h b/app/src/main/cpp/Skybox.h new file mode 100644 index 0000000000..b196d80b80 --- /dev/null +++ b/app/src/main/cpp/Skybox.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VRBROWSER_SKYBOX_H +#define VRBROWSER_SKYBOX_H + +#include "vrb/Forward.h" +#include "vrb/MacroUtils.h" + +namespace crow { + +class Skybox; +typedef std::shared_ptr SkyboxPtr; + +class VRLayerCube; +typedef std::shared_ptr VRLayerCubePtr; + +class Skybox { +public: + static SkyboxPtr Create(vrb::CreationContextPtr aContext, const VRLayerCubePtr& aLayer = nullptr); + void Load(const vrb::ModelLoaderAndroidPtr& aLoader, const std::string &basePath); + void SetVisible(bool aVisible); + void SetTransform(const vrb::Matrix& aTransform); + void SetTintColor(const vrb::Color& aTintColor); + vrb::NodePtr GetRoot() const; + + struct State; + Skybox(State& aState, vrb::CreationContextPtr& aContext); + ~Skybox(); +private: + State& m; + Skybox() = delete; + VRB_NO_DEFAULTS(Skybox) +}; + +} // namespace crow + +#endif // VRBROWSER_SKYBOX_H diff --git a/app/src/main/cpp/SplashAnimation.cpp b/app/src/main/cpp/SplashAnimation.cpp index fb5d4111cf..03f435a6ac 100644 --- a/app/src/main/cpp/SplashAnimation.cpp +++ b/app/src/main/cpp/SplashAnimation.cpp @@ -4,13 +4,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SplashAnimation.h" +#include "DeviceDelegate.h" +#include "VRLayer.h" +#include "VRLayerNode.h" #include "vrb/ConcreteClass.h" #include "vrb/Color.h" #include "vrb/CreationContext.h" +#include "vrb/GLError.h" +#include "vrb/FBO.h" #include "vrb/Geometry.h" #include "vrb/Matrix.h" #include "vrb/ModelLoaderAndroid.h" #include "vrb/RenderState.h" +#include "vrb/RenderContext.h" #include "vrb/TextureGL.h" #include "vrb/Toggle.h" #include "vrb/Transform.h" @@ -24,9 +30,10 @@ namespace crow { struct SplashAnimation::State { vrb::CreationContextWeak context; - vrb::TextureGLPtr texture; vrb::TogglePtr root; + vrb::FBOPtr read; QuadPtr logo; + VRLayerQuadPtr layer; timespec start; float time; bool firstDraw; @@ -49,11 +56,27 @@ struct SplashAnimation::State { clock_gettime(CLOCK_MONOTONIC, &spec); time = (float)(spec.tv_sec - start.tv_sec) + (float)(spec.tv_nsec - start.tv_nsec) / 1e09f; } + + void HandleLayerInitialized(const vrb::TextureGLPtr& aTexture) { + if (!read || !read->IsValid()) { + return; + } + + read->Bind(GL_READ_FRAMEBUFFER); + layer->Bind(GL_DRAW_FRAMEBUFFER); + VRB_GL_CHECK(glClearColor(1.0, 0.0f, 0.0f, 1.0f)); + VRB_GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); + VRB_GL_CHECK(glBlitFramebuffer(0, 0, aTexture->GetWidth(), aTexture->GetHeight(), + 0, 0, aTexture->GetWidth(), aTexture->GetHeight(), + GL_COLOR_BUFFER_BIT, GL_LINEAR)); + layer->Unbind(); + read->Unbind(); + } }; void -SplashAnimation::Load() { +SplashAnimation::Load(vrb::RenderContextPtr& aContext, const DeviceDelegatePtr& aDeviceDelegate) { if (m.logo) { return; } @@ -63,8 +86,27 @@ SplashAnimation::Load() { texture->SetTextureParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); const float w = 0.75f; const float aspect = (float)texture->GetWidth() / (float)texture->GetHeight(); - m.logo = Quad::Create(create, w, w / aspect); - m.logo->SetTexture(texture, texture->GetWidth(), texture->GetHeight()); + m.layer = aDeviceDelegate->CreateLayerQuad(texture->GetWidth(), texture->GetHeight(), VRLayerQuad::SurfaceType::FBO); + if (m.layer) { + texture->Bind(); + m.read = vrb::FBO::Create(aContext); + vrb::FBO::Attributes attributes; + attributes.depth = false; + attributes.samples = 0; + m.read->SetTextureHandle(texture->GetHandle(), texture->GetWidth(), texture->GetHeight(), attributes); + if (!m.read->IsValid()) { + VRB_WARN("Splash FBO is not valid"); + } + m.layer->SetInitializeDelegate([=](const VRLayer& aLayer){ + if (aLayer.IsInitialized()) { + m.HandleLayerInitialized(texture); + } + }); + } + m.logo = Quad::Create(create, w, w / aspect, m.layer); + if (!m.layer) { + m.logo->SetTexture(texture, texture->GetWidth(), texture->GetHeight()); + } m.root->AddNode(m.logo->GetRoot()); } @@ -82,7 +124,7 @@ SplashAnimation::Update(const vrb::Matrix& aHeadTransform) { m.UpdateTime(); if (m.time >= SPLASH_SECONDS && m.time <= (SPLASH_SECONDS + FADE_OUT_TIME)) { float t = 1.0f - (m.time - SPLASH_SECONDS) / FADE_OUT_TIME; - m.logo->GetGeometry()->GetRenderState()->SetTintColor(vrb::Color(t, t, t, 1.0f)); + m.logo->SetTintColor(vrb::Color(t, t, t, 1.0f)); } return m.time >= SPLASH_SECONDS + FADE_OUT_TIME; } @@ -92,6 +134,11 @@ SplashAnimation::GetRoot() const { return m.root; } +VRLayerQuadPtr +SplashAnimation::GetLayer() const { + return m.layer; +} + SplashAnimationPtr SplashAnimation::Create(vrb::CreationContextPtr aContext) { return std::make_shared >(aContext); diff --git a/app/src/main/cpp/SplashAnimation.h b/app/src/main/cpp/SplashAnimation.h index 96fe3e312a..1c0e77005a 100644 --- a/app/src/main/cpp/SplashAnimation.h +++ b/app/src/main/cpp/SplashAnimation.h @@ -16,15 +16,22 @@ namespace crow { +class DeviceDelegate; +typedef std::shared_ptr DeviceDelegatePtr; + class SplashAnimation; typedef std::shared_ptr SplashAnimationPtr; +class VRLayerQuad; +typedef std::shared_ptr VRLayerQuadPtr; + class SplashAnimation { public: static SplashAnimationPtr Create(vrb::CreationContextPtr aContext); - void Load(); + void Load(vrb::RenderContextPtr& aContext, const DeviceDelegatePtr& aDeviceDelegate); bool Update(const vrb::Matrix& aHeadTransform); vrb::NodePtr GetRoot() const; + VRLayerQuadPtr GetLayer() const; struct State; SplashAnimation(State& aState, vrb::CreationContextPtr& aContext); diff --git a/app/src/main/cpp/VRBrowser.cpp b/app/src/main/cpp/VRBrowser.cpp index 290baad948..377ea13716 100644 --- a/app/src/main/cpp/VRBrowser.cpp +++ b/app/src/main/cpp/VRBrowser.cpp @@ -12,6 +12,8 @@ namespace { static const char* kDispatchCreateWidgetName = "dispatchCreateWidget"; static const char* kDispatchCreateWidgetSignature = "(ILandroid/graphics/SurfaceTexture;II)V"; +static const char* kDispatchCreateWidgetLayerName = "dispatchCreateWidgetLayer"; +static const char* kDispatchCreateWidgetLayerSignature = "(ILandroid/view/Surface;II)V"; static const char* kHandleMotionEventName = "handleMotionEvent"; static const char* kHandleMotionEventSignature = "(IIZFF)V"; static const char* kHandleScrollEventName = "handleScrollEvent"; @@ -38,10 +40,13 @@ static const char* kGetActiveEnvironment = "getActiveEnvironment"; static const char* kGetActiveEnvironmentSignature = "()Ljava/lang/String;"; static const char* kGetPointerColor = "getPointerColor"; static const char* kGetPointerColorSignature = "()I"; +static const char* kAreLayersEnabled = "areLayersEnabled"; +static const char* kAreLayersEnabledSignature = "()Z"; static JNIEnv* sEnv; static jobject sActivity; static jmethodID sDispatchCreateWidget; +static jmethodID sDispatchCreateWidgetLayer; static jmethodID sHandleMotionEvent; static jmethodID sHandleScrollEvent; static jmethodID sHandleAudioPose; @@ -55,6 +60,7 @@ static jmethodID sGetStorageAbsolutePath; static jmethodID sIsOverrideEnvPathEnabled; static jmethodID sGetActiveEnvironment; static jmethodID sGetPointerColor; +static jmethodID sAreLayersEnabled; } namespace crow { @@ -75,6 +81,7 @@ VRBrowser::InitializeJava(JNIEnv* aEnv, jobject aActivity) { } sDispatchCreateWidget = FindJNIMethodID(sEnv, browserClass, kDispatchCreateWidgetName, kDispatchCreateWidgetSignature); + sDispatchCreateWidgetLayer = FindJNIMethodID(sEnv, browserClass, kDispatchCreateWidgetLayerName, kDispatchCreateWidgetLayerSignature); sHandleMotionEvent = FindJNIMethodID(sEnv, browserClass, kHandleMotionEventName, kHandleMotionEventSignature); sHandleScrollEvent = FindJNIMethodID(sEnv, browserClass, kHandleScrollEventName, kHandleScrollEventSignature); sHandleAudioPose = FindJNIMethodID(sEnv, browserClass, kHandleAudioPoseName, kHandleAudioPoseSignature); @@ -88,6 +95,7 @@ VRBrowser::InitializeJava(JNIEnv* aEnv, jobject aActivity) { sIsOverrideEnvPathEnabled = FindJNIMethodID(sEnv, browserClass, kIsOverrideEnvPathEnabledName, kIsOverrideEnvPathEnabledSignature); sGetActiveEnvironment = FindJNIMethodID(sEnv, browserClass, kGetActiveEnvironment, kGetActiveEnvironmentSignature); sGetPointerColor = FindJNIMethodID(sEnv, browserClass, kGetPointerColor, kGetPointerColorSignature); + sAreLayersEnabled = FindJNIMethodID(sEnv, browserClass, kAreLayersEnabled, kAreLayersEnabledSignature); } void @@ -101,6 +109,7 @@ VRBrowser::ShutdownJava() { } sDispatchCreateWidget = nullptr; + sDispatchCreateWidgetLayer = nullptr; sHandleMotionEvent = nullptr; sHandleScrollEvent = nullptr; sHandleAudioPose = nullptr; @@ -120,6 +129,15 @@ VRBrowser::DispatchCreateWidget(jint aWidgetHandle, jobject aSurface, jint aWidt CheckJNIException(sEnv, __FUNCTION__); } + +void +VRBrowser::DispatchCreateWidgetLayer(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight) { + if (!ValidateMethodID(sEnv, sActivity, sDispatchCreateWidgetLayer, __FUNCTION__)) { return; } + sEnv->CallVoidMethod(sActivity, sDispatchCreateWidgetLayer, aWidgetHandle, aSurface, aWidth, aHeight); + CheckJNIException(sEnv, __FUNCTION__); +} + + void VRBrowser::HandleMotionEvent(jint aWidgetHandle, jint aController, jboolean aPressed, jfloat aX, jfloat aY) { if (!ValidateMethodID(sEnv, sActivity, sHandleMotionEvent, __FUNCTION__)) { return; } @@ -237,4 +255,13 @@ VRBrowser::GetPointerColor() { return (int32_t )jHexColor; } +bool +VRBrowser::AreLayersEnabled() { + if (!ValidateMethodID(sEnv, sActivity, sAreLayersEnabled, __FUNCTION__)) { return false; } + jboolean enabled = sEnv->CallBooleanMethod(sActivity, sAreLayersEnabled); + CheckJNIException(sEnv, __FUNCTION__); + + return enabled; +} + } // namespace crow diff --git a/app/src/main/cpp/VRBrowser.h b/app/src/main/cpp/VRBrowser.h index d598d66d9a..8527dd75c3 100644 --- a/app/src/main/cpp/VRBrowser.h +++ b/app/src/main/cpp/VRBrowser.h @@ -17,7 +17,8 @@ namespace crow { namespace VRBrowser { void InitializeJava(JNIEnv* aEnv, jobject aActivity); void ShutdownJava(); -void DispatchCreateWidget(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight); +void DispatchCreateWidget(jint aWidgetHandle, jobject aSurfaceTexture, jint aWidth, jint aHeight); +void DispatchCreateWidgetLayer(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight); void HandleMotionEvent(jint aWidgetHandle, jint aController, jboolean aPressed, jfloat aX, jfloat aY); void HandleScrollEvent(jint aWidgetHandle, jint aController, jfloat aX, jfloat aY); void HandleAudioPose(jfloat qx, jfloat qy, jfloat qz, jfloat qw, jfloat px, jfloat py, jfloat pz); @@ -31,6 +32,7 @@ std::string GetStorageAbsolutePath(const std::string& aRelativePath); bool isOverrideEnvPathEnabled(); std::string GetActiveEnvironment(); int32_t GetPointerColor(); +bool AreLayersEnabled(); } // namespace VRBrowser; } // namespace crow diff --git a/app/src/main/cpp/VRLayer.cpp b/app/src/main/cpp/VRLayer.cpp new file mode 100644 index 0000000000..a005b5ceb4 --- /dev/null +++ b/app/src/main/cpp/VRLayer.cpp @@ -0,0 +1,389 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VRLayer.h" +#include "vrb/ConcreteClass.h" +#include "vrb/Color.h" +#include "vrb/Matrix.h" + +namespace crow { + +static uint64_t sIndex = 0; + +struct VRLayer::State { + bool initialized; + int32_t priority; + uint64_t drawIndex; + bool drawRequested; + VRLayer::LayerType layerType; + vrb::Matrix modelTransform[2]; + vrb::Matrix modelView[2]; + device::Eye currentEye; + vrb::Color tintColor; + device::EyeRect textureRect[2]; + InitializeDelegate initDelegate; + State(): + initialized(false), + priority(0), + drawIndex(0), + drawRequested(false), + currentEye(device::Eye::Left), + tintColor(1.0f, 1.0f, 1.0f, 1.0f) + { + for (int i = 0; i < 2; ++i) { + modelTransform[i] = vrb::Matrix::Identity(); + modelView[i] = vrb::Matrix::Identity(); + textureRect[i] = device::EyeRect(0.0f, 0.0f, 1.0f, 1.0f); + } + } +}; + +VRLayer::LayerType +VRLayer::GetLayerType() const { + return m.layerType; +} + +bool +VRLayer::IsInitialized() const { + return m.initialized; +} + +bool +VRLayer::IsDrawRequested() const { + return m.drawRequested; +} + +const vrb::Matrix& +VRLayer::GetModelTransform(device::Eye aEye) const { + return m.modelTransform[device::EyeIndex(aEye)]; +} + + +const vrb::Matrix& +VRLayer::GetModelView(device::Eye aEye) const { + return m.modelView[device::EyeIndex(aEye)]; +} + +device::Eye +VRLayer::GetCurrentEye() const { + return m.currentEye; +} + +int32_t +VRLayer::GetPriority() const { + return m.priority; +} + +const vrb::Color& +VRLayer::GetTintColor() const { + return m.tintColor; +} + +const device::EyeRect& +VRLayer::GetTextureRect(crow::device::Eye aEye) const { + return m.textureRect[device::EyeIndex(aEye)]; +} + +bool +VRLayer::ShouldDrawBefore(const VRLayer& aLayer) { + if (m.layerType == VRLayer::LayerType::CUBEMAP || m.layerType == VRLayer::LayerType::EQUIRECTANGULAR) { + return true; + } + + if (aLayer.m.layerType == VRLayer::LayerType::CUBEMAP || aLayer.m.layerType == VRLayer::LayerType::EQUIRECTANGULAR) { + return false; + } + + if (m.priority != aLayer.m.priority) { + return m.priority > aLayer.m.priority; + } + + return m.drawIndex < aLayer.m.drawIndex; +} + +void +VRLayer::SetInitialized(bool aInitialized) { + if (m.initialized != aInitialized) { + m.initialized = aInitialized; + if (m.initDelegate) { + m.initDelegate(*this); + } + } + +} + +void +VRLayer::RequestDraw() { + if (!m.drawRequested) { + m.drawIndex = ++sIndex; + } + m.drawRequested = true; +} + +void +VRLayer::ClearRequestDraw() { + m.drawRequested = false; +} + +void +VRLayer::SetModelTransform(device::Eye aEye, const vrb::Matrix& aModelTransform) { + m.modelTransform[device::EyeIndex(aEye)] = aModelTransform; +} + +void +VRLayer::SetModelView(device::Eye aEye, const vrb::Matrix& aModelView) { + m.modelView[device::EyeIndex(aEye)] = aModelView; +} + +void +VRLayer::SetCurrentEye(crow::device::Eye aEye) { + m.currentEye = aEye; +} + +void +VRLayer::SetPriority(int32_t aPriority) { + m.priority = aPriority; +} + +VRLayer::VRLayer(State& aState, LayerType aLayerType): m(aState) { + m.layerType = aLayerType; +} + +void +VRLayer::SetTintColor(const vrb::Color &aTintColor) { + m.tintColor = aTintColor; +} + +void +VRLayer::SetTextureRect(device::Eye aEye, const crow::device::EyeRect &aTextureRect) { + m.textureRect[device::EyeIndex(aEye)] = aTextureRect; +} + +void +VRLayer::SetInitializeDelegate(const VRLayer::InitializeDelegate& aDelegate) { + m.initDelegate = aDelegate; + if (m.initialized && m.initDelegate) { + m.initDelegate(*this); + } + +} + +// Layer Quad + +struct VRLayerQuad::State: public VRLayer::State { + VRLayerQuad::SurfaceType surfaceType; + int32_t width; + int32_t height; + int32_t priority; + float worldWidth; + float worldHeight; + GLenum boundTarget; + VRLayerQuad::ResizeDelegate resizeDelegate; + VRLayerQuad::BindDelegate bindDelegate; + jobject surface; + State(): + surfaceType(VRLayerQuad::SurfaceType::AndroidSurface), + width(0), + height(0), + worldWidth(0), + worldHeight(0), + boundTarget(GL_FRAMEBUFFER), + priority(0), + surface(nullptr) + {} +}; + +VRLayerQuadPtr +VRLayerQuad::Create(const int32_t aWidth, const int32_t aHeight, VRLayerQuad::SurfaceType aSurfaceType) { + auto result = std::make_shared>(); + result->m.width = aWidth; + result->m.height = aHeight; + result->m.surfaceType = aSurfaceType; + return result; +} + + +VRLayerQuad::SurfaceType +VRLayerQuad::GetSurfaceType() const { + return m.surfaceType; +} + +int32_t +VRLayerQuad::GetWidth() const { + return m.width; +} + +int32_t +VRLayerQuad::GetHeight() const { + return m.height; +} +float +VRLayerQuad::GetWorldWidth() const { + return m.worldWidth; +} + +float +VRLayerQuad::GetWorldHeight() const { + return m.worldHeight; +} + +jobject +VRLayerQuad::GetSurface() const { + return m.surface; +} + +void +VRLayerQuad::Bind(GLenum aTarget) { + m.boundTarget = aTarget; + if (m.bindDelegate) { + m.bindDelegate(aTarget, true); + } +} + +void +VRLayerQuad::Unbind(){ + if (m.bindDelegate) { + m.bindDelegate(m.boundTarget, false); + } +} + + +void +VRLayerQuad::SetWorldSize(const float aWidth, const float aHeight) { + m.worldWidth = aWidth; + m.worldHeight = aHeight; +} + +void +VRLayerQuad::Resize(const int32_t aWidth, const int32_t aHeight) { + if (m.width == aWidth && m.height == aHeight) { + return; + } + m.width = aWidth; + m.height = aHeight; + if (m.resizeDelegate) { + m.resizeDelegate(); + } +} + +void +VRLayerQuad::SetResizeDelegate(const ResizeDelegate& aDelegate) { + m.resizeDelegate = aDelegate; +} + +void +VRLayerQuad::SetBindDelegate(const BindDelegate& aDelegate) { + m.bindDelegate = aDelegate; +} + +void +VRLayerQuad::SetSurface(jobject aSurface) { + m.surface = aSurface; +} + +VRLayerQuad::VRLayerQuad(State& aState): VRLayer(aState, LayerType::QUAD), m(aState) { + +} + +VRLayerQuad::~VRLayerQuad() {} + + +// Layer Cube + +struct VRLayerCube::State: public VRLayer::State { + int32_t width; + int32_t height; + bool loaded; + uint32_t textureHandle; + State(): + width(0), + height(0), + loaded(false), + textureHandle(0) + {} +}; + +VRLayerCubePtr +VRLayerCube::Create(const int32_t aWidth, const int32_t aHeight) { + auto result = std::make_shared>(); + result->m.width = aWidth; + result->m.height = aHeight; + return result; +} + +int32_t +VRLayerCube::GetWidth() const { + return m.width; +} + +int32_t +VRLayerCube::GetHeight() const { + return m.height; +} + +bool +VRLayerCube::IsLoaded() const { + return m.loaded; +} + +GLuint +VRLayerCube::GetTextureHandle() const { + return m.textureHandle; +} + +void +VRLayerCube::SetTextureHandle(uint32_t aTextureHandle){ + m.textureHandle = aTextureHandle; +} + +void +VRLayerCube::SetLoaded(bool aLoaded) { + m.loaded = aLoaded; +} + +VRLayerCube::VRLayerCube(State& aState): VRLayer(aState, LayerType::CUBEMAP), m(aState) { + +} + +VRLayerCube::~VRLayerCube() {} + + +// Layer Equirect + +struct VRLayerEquirect::State: public VRLayer::State { + vrb::Matrix uvTransform[2]; + State() { + uvTransform[0] = vrb::Matrix::Identity(); + uvTransform[1] = vrb::Matrix::Identity(); + } +}; + +VRLayerEquirectPtr +VRLayerEquirect::Create() { + auto result = std::make_shared>(); + return result; +} + + +const vrb::Matrix& +VRLayerEquirect::GetUVTransform(device::Eye aEye) const { + return m.uvTransform[device::EyeIndex(aEye)]; +} + + +void +VRLayerEquirect::SetUVTransform(device::Eye aEye, const vrb::Matrix& aTransform) { + m.uvTransform[device::EyeIndex(aEye)] = aTransform; +} + + +VRLayerEquirect::VRLayerEquirect(State& aState): VRLayer(aState, LayerType::EQUIRECTANGULAR), m(aState) { + +} + +VRLayerEquirect::~VRLayerEquirect() {} + + +} // namespace crow diff --git a/app/src/main/cpp/VRLayer.h b/app/src/main/cpp/VRLayer.h new file mode 100644 index 0000000000..746d137866 --- /dev/null +++ b/app/src/main/cpp/VRLayer.h @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VR_LAYER_DOT_H +#define VR_LAYER_DOT_H + +#include "vrb/gl.h" +#include "vrb/MacroUtils.h" +#include "vrb/Forward.h" + +#include "Device.h" + +#include +#include +#include + +namespace crow { + +class VRLayer; +typedef std::shared_ptr VRLayerPtr; + +class VRLayer { +public: + enum class LayerType { + QUAD, + CUBEMAP, + EQUIRECTANGULAR + }; + + typedef std::function InitializeDelegate; + + VRLayer::LayerType GetLayerType() const; + bool IsInitialized() const; + bool IsDrawRequested() const; + const vrb::Matrix& GetModelTransform(device::Eye aEye) const; + const vrb::Matrix& GetModelView(device::Eye aEye) const; + device::Eye GetCurrentEye() const; + int32_t GetPriority() const; + const vrb::Color& GetTintColor() const; + const device::EyeRect& GetTextureRect(device::Eye aEye) const; + + bool ShouldDrawBefore(const VRLayer& aLayer); + void SetInitialized(bool aInitialized); + void RequestDraw(); + void ClearRequestDraw(); + void SetModelTransform(device::Eye aEye, const vrb::Matrix& aModelTransform); + void SetModelView(device::Eye aEye, const vrb::Matrix& aModelView); + void SetCurrentEye(device::Eye aEye); + void SetPriority(int32_t aPriority); + void SetTintColor(const vrb::Color& aTintColor); + void SetTextureRect(device::Eye aEye, const device::EyeRect& aTextureRect); + void SetInitializeDelegate(const InitializeDelegate& aDelegate); +protected: + struct State; + VRLayer(State& aState, LayerType aLayerType); + virtual ~VRLayer() {}; +private: + State& m; + VRB_NO_DEFAULTS(VRLayer) +}; + + +class VRLayerQuad; +typedef std::shared_ptr VRLayerQuadPtr; + +class VRLayerQuad: public VRLayer { +public: + typedef std::function ResizeDelegate; + typedef std::function BindDelegate; + + enum class SurfaceType { + AndroidSurface, + FBO, + }; + + static VRLayerQuadPtr Create(const int32_t aWidth, const int32_t aHeight, VRLayerQuad::SurfaceType aSurfaceType); + + SurfaceType GetSurfaceType() const; + int32_t GetWidth() const; + int32_t GetHeight() const; + float GetWorldWidth() const; + float GetWorldHeight() const; + jobject GetSurface() const; + + // Only works with SurfaceType::FBO + void Bind(GLenum aTarget = GL_FRAMEBUFFER); + // Only works with SurfaceType::FBO + void Unbind(); + + void SetWorldSize(const float aWidth, const float aHeight); + void Resize(const int32_t aWidth, const int32_t aHeight); + void SetResizeDelegate(const ResizeDelegate& aDelegate); + void SetBindDelegate(const BindDelegate& aDelegate); + void SetSurface(jobject aSurface); +protected: + struct State; + VRLayerQuad(State& aState); + virtual ~VRLayerQuad(); +private: + State& m; + VRB_NO_DEFAULTS(VRLayerQuad) +}; + +class VRLayerCube; +typedef std::shared_ptr VRLayerCubePtr; + +class VRLayerCube: public VRLayer { +public: + static VRLayerCubePtr Create(const int32_t aWidth, const int32_t aHeight); + + int32_t GetWidth() const; + int32_t GetHeight() const; + GLuint GetTextureHandle() const; + bool IsLoaded() const; + + + void SetTextureHandle(uint32_t aTextureHandle); + void SetLoaded(bool aReady); +protected: + struct State; + VRLayerCube(State& aState); + virtual ~VRLayerCube(); +private: + State& m; + VRB_NO_DEFAULTS(VRLayerCube) +}; + +class VRLayerEquirect; +typedef std::shared_ptr VRLayerEquirectPtr; + +class VRLayerEquirect: public VRLayer { +public: + static VRLayerEquirectPtr Create(); + const vrb::Matrix& GetUVTransform(device::Eye aEye) const; + void SetUVTransform(device::Eye aEye, const vrb::Matrix& aTransform); +protected: + struct State; + VRLayerEquirect(State& aState); + virtual ~VRLayerEquirect(); +private: + State& m; + VRB_NO_DEFAULTS(VRLayerEquirect) +}; + +} // namespace crow + +#endif // VR_LAYER_DOT_H diff --git a/app/src/main/cpp/VRLayerNode.cpp b/app/src/main/cpp/VRLayerNode.cpp new file mode 100644 index 0000000000..dd98988bb1 --- /dev/null +++ b/app/src/main/cpp/VRLayerNode.cpp @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VRLayerNode.h" +#include "VRLayer.h" + +#include "vrb/private/DrawableState.h" +#include "vrb/private/NodeState.h" +#include "vrb/private/ResourceGLState.h" + +#include "vrb/Camera.h" +#include "vrb/ConcreteClass.h" +#include "vrb/CullVisitor.h" +#include "vrb/DrawableList.h" +#include "vrb/GLError.h" +#include "vrb/Logger.h" +#include "vrb/Matrix.h" +#include "vrb/RenderState.h" + +#include + +using namespace vrb; + +namespace crow { + +struct VRLayerNode::State : public Node::State, public Drawable::State { + RenderStatePtr renderState; + VRLayerPtr layer; + + State() {} +}; + +VRLayerNodePtr +VRLayerNode::Create(CreationContextPtr& aContext, const VRLayerPtr& aLayer) { + auto result = std::make_shared >(aContext); + result->m.layer = aLayer; + result->m.renderState = vrb::RenderState::Create(aContext); + return result; +} + +// Node interface +void +VRLayerNode::Cull(CullVisitor& aVisitor, DrawableList& aDrawables) { + aDrawables.AddDrawable(std::move(CreateDrawablePtr()), aVisitor.GetTransform()); +} + +// Drawable interface +RenderStatePtr& +VRLayerNode::GetRenderState() { + return m.renderState; +} + +void +VRLayerNode::SetRenderState(const RenderStatePtr& aRenderState) { + m.renderState = aRenderState; +} + +void +VRLayerNode::Draw(const Camera& aCamera, const Matrix& aModelTransform) { + m.layer->RequestDraw(); + device::Eye eye = m.layer->GetCurrentEye(); + m.layer->SetModelTransform(eye, aModelTransform); + m.layer->SetModelView(eye, aCamera.GetView().PostMultiply(aModelTransform)); +} + +VRLayerNode::VRLayerNode(State& aState, CreationContextPtr& aContext) : + Node(aState, aContext), + Drawable(aState, aContext), + m(aState) +{} +VRLayerNode::~VRLayerNode() {} + +} + diff --git a/app/src/main/cpp/VRLayerNode.h b/app/src/main/cpp/VRLayerNode.h new file mode 100644 index 0000000000..8202c604e9 --- /dev/null +++ b/app/src/main/cpp/VRLayerNode.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VR_LAYER_NODE_H +#define VR_LAYER_NODE_H + +#include "vrb/Forward.h" +#include "vrb/MacroUtils.h" +#include "vrb/Drawable.h" +#include "vrb/Node.h" +#include "vrb/gl.h" + +#include + +namespace crow { + +class VRLayer; +typedef std::shared_ptr VRLayerPtr; + +class VRLayerNode; +typedef std::shared_ptr VRLayerNodePtr; + +class VRLayerNode : public vrb::Node, public vrb::Drawable { +public: + static VRLayerNodePtr Create(vrb::CreationContextPtr& aContext, const VRLayerPtr& aLayer); + + // Node interface + void Cull(vrb::CullVisitor& aVisitor, vrb::DrawableList& aDrawables) override; + + // From Drawable + vrb::RenderStatePtr& GetRenderState() override; + void SetRenderState(const vrb::RenderStatePtr& aRenderState) override; + void Draw(const vrb::Camera& aCamera, const vrb::Matrix& aModelTransform) override; +protected: + struct State; + VRLayerNode(State& aState, vrb::CreationContextPtr& aContext); + ~VRLayerNode(); + +private: + State& m; + VRLayerNode() = delete; + VRB_NO_DEFAULTS(VRLayerNode) +}; + +} + +#endif // VR_LAYER_NODE_H diff --git a/app/src/main/cpp/VRVideo.cpp b/app/src/main/cpp/VRVideo.cpp index 3b22734527..65ffc8a35b 100644 --- a/app/src/main/cpp/VRVideo.cpp +++ b/app/src/main/cpp/VRVideo.cpp @@ -4,6 +4,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "VRVideo.h" +#include "DeviceDelegate.h" +#include "VRLayer.h" +#include "VRLayerNode.h" #include "vrb/ConcreteClass.h" #include "vrb/Color.h" #include "vrb/CreationContext.h" @@ -25,12 +28,19 @@ namespace crow { struct VRVideo::State { vrb::CreationContextWeak context; + std::weak_ptr deviceWeak; WidgetPtr window; VRVideoProjection projection; vrb::TogglePtr root; vrb::TogglePtr leftEye; vrb::TogglePtr rightEye; + VRLayerPtr layer; + device::EyeRect layerTextureBackup[2]; + float mWorldWidthBackup; + float mWorlHeightBackup; State() + : mWorldWidthBackup(0) + , mWorlHeightBackup(0) { } @@ -39,7 +49,16 @@ struct VRVideo::State { window = aWindow; projection = aProjection; root = vrb::Toggle::Create(create); - updateProjection(); + VRLayerQuadPtr windowLayer = aWindow->GetLayer(); + if (windowLayer) { + layerTextureBackup[0] = windowLayer->GetTextureRect(device::Eye::Left); + layerTextureBackup[1] = windowLayer->GetTextureRect(device::Eye::Right); + mWorldWidthBackup = windowLayer->GetWorldWidth(); + mWorlHeightBackup = windowLayer->GetWorldHeight(); + updateProjectionLayer(); + } else { + updateProjection(); + } root->AddNode(leftEye); if (rightEye) { root->AddNode(rightEye); @@ -73,6 +92,31 @@ struct VRVideo::State { } } + void updateProjectionLayer() { + switch (projection) { + case VRVideoProjection::VIDEO_PROJECTION_3D_SIDE_BY_SIDE: + window->GetLayer()->SetWorldSize(mWorldWidthBackup * 2.0f, mWorlHeightBackup); + leftEye = createQuadProjectionLayer(device::Eye::Left, device::EyeRect(0.0f, 0.0f, 0.5f, 1.0f)); + rightEye = createQuadProjectionLayer(device::Eye::Right, device::EyeRect(0.5f, 0.0f, 0.5f, 1.0f)); + break; + case VRVideoProjection::VIDEO_PROJECTION_360: + create360ProjectionLayer(); + break; + case VRVideoProjection::VIDEO_PROJECTION_360_STEREO: + create360StereoProjectionLayer(); + break; + case VRVideoProjection::VIDEO_PROJECTION_180: + create180ProjectionLayer(); + break; + case VRVideoProjection::VIDEO_PROJECTION_180_STEREO_LEFT_RIGHT: + create180LRProjectionLayer(); + break; + case VRVideoProjection::VIDEO_PROJECTION_180_STEREO_TOP_BOTTOM: + create180TBProjectionLayer(); + break; + } + } + vrb::TogglePtr createSphereProjection(bool half, device::EyeRect aUVRect) { const int kCols = 30; const int kRows = 30; @@ -151,6 +195,114 @@ struct VRVideo::State { return result; } + void create360ProjectionLayer() { + vrb::CreationContextPtr create = context.lock(); + DeviceDelegatePtr device = deviceWeak.lock(); + VRLayerEquirectPtr equirect = device->CreateLayerEquirect(window->GetLayer()); + layer = equirect; + + leftEye = vrb::Toggle::Create(create); + leftEye->AddNode(VRLayerNode::Create(create, equirect)); + } + + void create360StereoProjectionLayer() { + vrb::CreationContextPtr create = context.lock(); + DeviceDelegatePtr device = deviceWeak.lock(); + VRLayerEquirectPtr equirect = device->CreateLayerEquirect(window->GetLayer()); + layer = equirect; + + vrb::Matrix leftTransform = vrb::Matrix::Identity(); + leftTransform.ScaleInPlace(vrb::Vector(1.0f, 0.5f, 1.0f)); + equirect->SetUVTransform(device::Eye::Left, leftTransform); + + vrb::Matrix rightTransform = vrb::Matrix::Position(vrb::Vector(0.0f, 0.5f, 0.0f)); + rightTransform.ScaleInPlace(vrb::Vector(1.0f, 0.5f, 1.0f)); + equirect->SetUVTransform(device::Eye::Right, rightTransform); + + leftEye = vrb::Toggle::Create(create); + leftEye->AddNode(VRLayerNode::Create(create, equirect)); + rightEye = vrb::Toggle::Create(create); + rightEye->AddNode(VRLayerNode::Create(create, equirect)); + } + + vrb::TogglePtr create180LayerToggle(const VRLayerEquirectPtr& aLayer) { + vrb::CreationContextPtr create = context.lock(); + vrb::TogglePtr result = vrb::Toggle::Create(create); + vrb::Matrix rotation = vrb::Matrix::Rotation(vrb::Vector(0.0f, 1.0f, 0.0f), (float)M_PI * 0.5f); + vrb::TransformPtr transform = vrb::Transform::Create(create); + transform->AddNode(VRLayerNode::Create(create, aLayer)); + transform->SetTransform(rotation); + result->AddNode(transform); + return result; + } + + void create180ProjectionLayer() { + vrb::CreationContextPtr create = context.lock(); + DeviceDelegatePtr device = deviceWeak.lock(); + VRLayerEquirectPtr equirect = device->CreateLayerEquirect(window->GetLayer()); + layer = equirect; + + vrb::Matrix uvTransform = vrb::Matrix::Identity(); + uvTransform.ScaleInPlace(vrb::Vector(2.0f, 1.0f, 1.0f)); + + equirect->SetUVTransform(device::Eye::Left, uvTransform); + equirect->SetUVTransform(device::Eye::Right, uvTransform); + + leftEye = create180LayerToggle(equirect); + } + + void create180LRProjectionLayer() { + vrb::CreationContextPtr create = context.lock(); + DeviceDelegatePtr device = deviceWeak.lock(); + VRLayerEquirectPtr equirect = device->CreateLayerEquirect(window->GetLayer()); + layer = equirect; + + equirect->SetTextureRect(device::Eye::Left, device::EyeRect(0.0f, 0.0f, 0.5f, 1.0f)); + equirect->SetTextureRect(device::Eye::Right, device::EyeRect(0.5f, 0.0f, 0.5f, 1.0f)); + equirect->SetUVTransform(device::Eye::Right, vrb::Matrix::Position(vrb::Vector(0.5f, 0.0f, 0.0f))); + + leftEye = create180LayerToggle(equirect); + rightEye = create180LayerToggle(equirect); + } + + void create180TBProjectionLayer() { + vrb::CreationContextPtr create = context.lock(); + DeviceDelegatePtr device = deviceWeak.lock(); + VRLayerEquirectPtr equirect = device->CreateLayerEquirect(window->GetLayer()); + layer = equirect; + + equirect->SetTextureRect(device::Eye::Right, device::EyeRect(0.0f, 0.5f, 1.0f, 0.5f)); + equirect->SetTextureRect(device::Eye::Left, device::EyeRect(0.0f, 0.0f, 1.0f, 0.5f)); + + vrb::Matrix uvTransform = vrb::Matrix::Identity(); + uvTransform.ScaleInPlace(vrb::Vector(2.0f, 0.5f, 1.0f)); + equirect->SetUVTransform(device::Eye::Left, uvTransform); + uvTransform.TranslateInPlace(vrb::Vector(0.0f, 0.5f, 0.0f)); + equirect->SetUVTransform(device::Eye::Right, uvTransform); + + leftEye = create180LayerToggle(equirect); + rightEye = create180LayerToggle(equirect); + } + + vrb::TogglePtr createQuadProjectionLayer(device::Eye aEye, const device::EyeRect& aUVRect) { + vrb::CreationContextPtr create = context.lock(); + VRLayerQuadPtr windowLayer = window->GetLayer(); + windowLayer->SetTextureRect(aEye, aUVRect); + layer = windowLayer; + + vrb::TransformPtr transform = vrb::Transform::Create(create); + vrb::Matrix matrix = window->GetTransform(); + float translation = windowLayer->GetWorldWidth() * 0.25f * (aEye == device::Eye::Left ? 1.0f : -1.0f); + matrix.TranslateInPlace(vrb::Vector(translation, 0.0f, 0.0f)); + + transform->SetTransform(matrix); + transform->AddNode(VRLayerNode::Create(create, layer)); + vrb::TogglePtr result = vrb::Toggle::Create(create); + result->AddNode(transform); + + return result; + } + vrb::TogglePtr createQuadProjection(device::EyeRect aUVRect) { vrb::CreationContextPtr create = context.lock(); vrb::Vector min, max; @@ -173,12 +325,17 @@ struct VRVideo::State { void VRVideo::SelectEye(device::Eye aEye) { - if (!m.rightEye) { - // Not stereo projection, always show the left eye. - return; + if (m.layer) { + m.layer->SetCurrentEye(aEye); + } + + if (m.leftEye) { + m.leftEye->ToggleAll(aEye == device::Eye::Left || !m.rightEye); + } + + if (m.rightEye) { + m.rightEye->ToggleAll(aEye == device::Eye::Right); } - m.leftEye->ToggleAll(aEye == device::Eye::Left); - m.rightEye->ToggleAll(aEye == device::Eye::Right); } vrb::NodePtr @@ -186,9 +343,27 @@ VRVideo::GetRoot() const { return m.root; } +void +VRVideo::Exit() { + VRLayerQuadPtr windowLayer = m.window->GetLayer(); + if (windowLayer) { + windowLayer->SetTextureRect(device::Eye::Left, m.layerTextureBackup[0]); + windowLayer->SetTextureRect(device::Eye::Right, m.layerTextureBackup[1]); + windowLayer->SetWorldSize(m.mWorldWidthBackup, m.mWorlHeightBackup); + } + if (m.layer && m.layer != windowLayer) { + DeviceDelegatePtr device = m.deviceWeak.lock(); + device->DeleteLayer(m.layer); + } +} + VRVideoPtr -VRVideo::Create(vrb::CreationContextPtr aContext, const WidgetPtr& aWindow, const VRVideoProjection aProjection) { +VRVideo::Create(vrb::CreationContextPtr aContext, + const WidgetPtr& aWindow, + const VRVideoProjection aProjection, + const DeviceDelegatePtr& aDevice) { VRVideoPtr result = std::make_shared >(aContext); + result->m.deviceWeak = aDevice; result->m.Initialize(aWindow, aProjection); return result; } diff --git a/app/src/main/cpp/VRVideo.h b/app/src/main/cpp/VRVideo.h index c2718d387f..52277212aa 100644 --- a/app/src/main/cpp/VRVideo.h +++ b/app/src/main/cpp/VRVideo.h @@ -17,6 +17,9 @@ namespace crow { +class DeviceDelegate; +typedef std::shared_ptr DeviceDelegatePtr; + class VRVideo; typedef std::shared_ptr VRVideoPtr; @@ -34,9 +37,13 @@ class VRVideo { VIDEO_PROJECTION_180_STEREO_LEFT_RIGHT = 4, VIDEO_PROJECTION_180_STEREO_TOP_BOTTOM = 5, }; - static VRVideoPtr Create(vrb::CreationContextPtr aContext, const WidgetPtr& aWindow, const VRVideoProjection aProjection); + static VRVideoPtr Create(vrb::CreationContextPtr aContext, + const WidgetPtr& aWindow, + const VRVideoProjection aProjection, + const DeviceDelegatePtr& aDevice); void SelectEye(device::Eye aEye); vrb::NodePtr GetRoot() const; + void Exit(); struct State; VRVideo(State& aState, vrb::CreationContextPtr& aContext); diff --git a/app/src/main/cpp/Widget.cpp b/app/src/main/cpp/Widget.cpp index 279ec2a0d9..4029b32ade 100644 --- a/app/src/main/cpp/Widget.cpp +++ b/app/src/main/cpp/Widget.cpp @@ -5,6 +5,8 @@ #include "Widget.h" #include "Quad.h" +#include "VRLayer.h" +#include "VRBrowser.h" #include "WidgetPlacement.h" #include "WidgetResizer.h" #include "vrb/ConcreteClass.h" @@ -31,6 +33,7 @@ struct Widget::State { std::string name; uint32_t handle; QuadPtr quad; + VRLayerQuadPtr layer; vrb::TogglePtr root; vrb::TransformPtr transform; vrb::TextureSurfacePtr surface; @@ -93,18 +96,29 @@ struct Widget::State { return geometry; } - void Initialize(const int aHandle, const vrb::Vector& aWindowMin, const vrb::Vector& aWindowMax, const int32_t aTextureWidth, const int32_t aTextureHeight) { + void Initialize(const int aHandle, const vrb::Vector& aWindowMin, const vrb::Vector& aWindowMax, + const int32_t aTextureWidth, const int32_t aTextureHeight, const VRLayerQuadPtr& aLayer) { handle = aHandle; name = "crow::Widget-" + std::to_string(handle); vrb::RenderContextPtr render = context.lock(); if (!render) { return; } - surface = vrb::TextureSurface::Create(render, name); + layer = aLayer; + if (layer) { + layer->SetInitializeDelegate([=](const VRLayer& aLayer) { + const VRLayerQuad& layerQuad = static_cast(aLayer); + VRBrowser::DispatchCreateWidgetLayer((jint)aHandle, layerQuad.GetSurface(), layerQuad.GetWidth(), layerQuad.GetHeight()); + }); + } else { + surface = vrb::TextureSurface::Create(render, name); + } vrb::CreationContextPtr create = render->GetRenderThreadCreationContext(); - quad = Quad::Create(create, aWindowMin, aWindowMax); - quad->SetTexture(surface, aTextureWidth, aTextureHeight); - quad->SetMaterial(vrb::Color(0.4f, 0.4f, 0.4f), vrb::Color(1.0f, 1.0f, 1.0f), vrb::Color(0.0f, 0.0f, 0.0f), 0.0f); + quad = Quad::Create(create, aWindowMin, aWindowMax, layer); + if (surface) { + quad->SetTexture(surface, aTextureWidth, aTextureHeight); + quad->SetMaterial(vrb::Color(0.4f, 0.4f, 0.4f), vrb::Color(1.0f, 1.0f, 1.0f), vrb::Color(0.0f, 0.0f, 0.0f), 0.0f); + } transform = vrb::Transform::Create(create); pointerToggle = vrb::Toggle::Create(create); @@ -135,7 +149,12 @@ struct Widget::State { pointer->AddNode(pointerScale); pointerGeometry = geometry; pointerToggle->AddNode(pointer); - root->ToggleAll(false); + if (layer) { + toggleState = true; + root->ToggleAll(true); + } else { + root->ToggleAll(false); + } } bool FirstDraw() { @@ -153,14 +172,25 @@ Widget::Create(vrb::RenderContextPtr& aContext, const int aHandle, const int32_t const float worldHeight = aWorldWidth / aspect; vrb::Vector windowMin(-aWorldWidth * 0.5f, -worldHeight * 0.5f, 0.0f); vrb::Vector windowMax(aWorldWidth *0.5f, worldHeight * 0.5f, 0.0f); - result->m.Initialize(aHandle, windowMin, windowMax, aWidth, aHeight); + result->m.Initialize(aHandle, windowMin, windowMax, aWidth, aHeight, nullptr); + return result; +} + +WidgetPtr +Widget::Create(vrb::RenderContextPtr& aContext, const int aHandle, const VRLayerQuadPtr& aLayer, float aWorldWidth) { + WidgetPtr result = std::make_shared >(aContext); + const float aspect = (float)aLayer->GetWidth() / (float)aLayer->GetHeight(); + const float worldHeight = aWorldWidth / aspect; + vrb::Vector windowMin(-aWorldWidth * 0.5f, -worldHeight * 0.5f, 0.0f); + vrb::Vector windowMax(aWorldWidth *0.5f, worldHeight * 0.5f, 0.0f); + result->m.Initialize(aHandle, windowMin, windowMax, aLayer->GetWidth(), aLayer->GetHeight(), aLayer); return result; } WidgetPtr Widget::Create(vrb::RenderContextPtr& aContext, const int aHandle, const int32_t aWidth, const int32_t aHeight, const vrb::Vector& aMin, const vrb::Vector& aMax) { WidgetPtr result = std::make_shared >(aContext); - result->m.Initialize(aHandle, aMin, aMax, aWidth, aHeight); + result->m.Initialize(aHandle, aMin, aMax, aWidth, aHeight, nullptr); return result; } @@ -272,9 +302,7 @@ Widget::SetTransform(const vrb::Matrix& aTransform) { void Widget::ToggleWidget(const bool aEnabled) { m.toggleState = aEnabled; - if (m.FirstDraw()) { - m.root->ToggleAll(aEnabled); - } + m.root->ToggleAll(aEnabled && m.FirstDraw()); } void @@ -298,6 +326,11 @@ Widget::GetQuad() const { return m.quad; } +const VRLayerQuadPtr& +Widget::GetLayer() const { + return m.layer; +} + vrb::TransformPtr Widget::GetTransformNode() const { return m.transform; diff --git a/app/src/main/cpp/Widget.h b/app/src/main/cpp/Widget.h index 3e03d9ce33..c6770c186a 100644 --- a/app/src/main/cpp/Widget.h +++ b/app/src/main/cpp/Widget.h @@ -20,6 +20,9 @@ namespace crow { class Quad; typedef std::shared_ptr QuadPtr; +class VRLayerQuad; +typedef std::shared_ptr VRLayerQuadPtr; + class Widget; typedef std::shared_ptr WidgetPtr; @@ -29,6 +32,7 @@ typedef std::shared_ptr WidgetPlacementPtr; class Widget { public: static WidgetPtr Create(vrb::RenderContextPtr& aContext, const int aHandle, const int32_t aWidth, const int32_t aHeight, float aWorldWidth); + static WidgetPtr Create(vrb::RenderContextPtr& aContext, const int aHandle, const VRLayerQuadPtr& aLayer, float aWorldWidth); static WidgetPtr Create(vrb::RenderContextPtr& aContext, const int aHandle, const int32_t aWidth, const int32_t aHeight, const vrb::Vector& aMin, const vrb::Vector& aMax); uint32_t GetHandle() const; void ResetFirstDraw(); @@ -49,6 +53,7 @@ class Widget { bool IsVisible() const; vrb::NodePtr GetRoot() const; QuadPtr GetQuad() const; + const VRLayerQuadPtr& GetLayer() const; vrb::TransformPtr GetTransformNode() const; vrb::NodePtr GetPointerGeometry() const; void SetPointerGeometry(vrb::NodePtr& aNode); diff --git a/app/src/main/cpp/WidgetPlacement.cpp b/app/src/main/cpp/WidgetPlacement.cpp index f6c5ceb6be..60d772c1d5 100644 --- a/app/src/main/cpp/WidgetPlacement.cpp +++ b/app/src/main/cpp/WidgetPlacement.cpp @@ -52,6 +52,7 @@ WidgetPlacement::FromJava(JNIEnv* aEnv, jobject& aObject) { GET_BOOLEAN_FIELD(opaque); GET_BOOLEAN_FIELD(showPointer); GET_BOOLEAN_FIELD(firstDraw); + GET_BOOLEAN_FIELD(layer); return result; } diff --git a/app/src/main/cpp/WidgetPlacement.h b/app/src/main/cpp/WidgetPlacement.h index 94809f3afb..57a1527221 100644 --- a/app/src/main/cpp/WidgetPlacement.h +++ b/app/src/main/cpp/WidgetPlacement.h @@ -30,6 +30,7 @@ struct WidgetPlacement { bool opaque; bool showPointer; bool firstDraw; + bool layer; static WidgetPlacementPtr FromJava(JNIEnv* aEnv, jobject& aObject); private: diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 8ba39f4846..622ba96e70 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -22,6 +22,7 @@ #include #include +#include "VRBrowser.h" #define JNI_METHOD(return_type, method_name) \ JNIEXPORT return_type JNICALL \ @@ -202,6 +203,7 @@ android_main(android_app *aAppState) { // Create Browser context sAppContext = std::make_shared(); sAppContext->mQueue = vrb::RunnableQueue::Create(aAppState->activity->vm); + crow::VRBrowser::InitializeJava(jniEnv, aAppState->activity->clazz); // Create device delegate sAppContext->mDevice = PlatformDeviceDelegate::Create(BrowserWorld::Instance().GetRenderContext(), diff --git a/app/src/main/cpp/vrb b/app/src/main/cpp/vrb index 439f056c87..57ce321225 160000 --- a/app/src/main/cpp/vrb +++ b/app/src/main/cpp/vrb @@ -1 +1 @@ -Subproject commit 439f056c8767b15f46f5b4b5f279f45bcaab8e5e +Subproject commit 57ce3212257513bd2cdcb5af89cb85476a39701b diff --git a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp index 7586dca839..e9c35eec2f 100644 --- a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp +++ b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp @@ -7,8 +7,10 @@ #include "DeviceUtils.h" #include "ElbowModel.h" #include "BrowserEGLContext.h" +#include "VRLayer.h" #include +#include #include #include "vrb/CameraEye.h" #include "vrb/Color.h" @@ -23,14 +25,38 @@ #include #include #include +#include #include "VrApi.h" #include "VrApi_Helpers.h" #include "VrApi_Input.h" #include "VrApi_SystemUtils.h" +#include "VRBrowser.h" + namespace crow { +static ovrMatrix4f ovrMatrixFrom(const vrb::Matrix& aMatrix) { + ovrMatrix4f m; + m.M[0][0] = aMatrix.At(0, 0); + m.M[0][1] = aMatrix.At(1, 0); + m.M[0][2] = aMatrix.At(2, 0); + m.M[0][3] = aMatrix.At(3, 0); + m.M[1][0] = aMatrix.At(0, 1); + m.M[1][1] = aMatrix.At(1, 1); + m.M[1][2] = aMatrix.At(2, 1); + m.M[1][3] = aMatrix.At(3, 1); + m.M[2][0] = aMatrix.At(0, 2); + m.M[2][1] = aMatrix.At(1, 2); + m.M[2][2] = aMatrix.At(2, 2); + m.M[2][3] = aMatrix.At(3, 2); + m.M[3][0] = aMatrix.At(0, 3); + m.M[3][1] = aMatrix.At(1, 3); + m.M[3][2] = aMatrix.At(2, 3); + m.M[3][3] = aMatrix.At(3, 3); + return m; +} + class OculusEyeSwapChain; typedef std::shared_ptr OculusEyeSwapChainPtr; @@ -44,7 +70,8 @@ struct OculusEyeSwapChain { return std::make_shared(); } - void Init(vrb::RenderContextPtr& aContext, device::RenderMode aMode, uint32_t aWidth, uint32_t aHeight) { + void Init(vrb::RenderContextPtr &aContext, device::RenderMode aMode, uint32_t aWidth, + uint32_t aHeight) { Destroy(); ovrSwapChain = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_2D, VRAPI_TEXTURE_FORMAT_8888, @@ -66,7 +93,7 @@ struct OculusEyeSwapChain { attributes.samples = 0; } else { attributes.depth = true; - attributes.samples = 2; + attributes.samples = 4; } VRB_GL_CHECK(fbo->SetTextureHandle(texture, aWidth, aHeight, attributes)); @@ -88,15 +115,321 @@ struct OculusEyeSwapChain { } }; +template +class OculusLayer { +public: + ovrTextureSwapChain * swapChain = nullptr; + T layer; + U ovrLayer; + + void Init() { + layer->SetInitialized(true); + } + + virtual void Update(const ovrTracking2& aTracking) { + vrb::Color tintColor = layer->GetTintColor(); + ovrLayer.Header.ColorScale.x = tintColor.Red(); + ovrLayer.Header.ColorScale.y = tintColor.Green(); + ovrLayer.Header.ColorScale.z = tintColor.Blue(); + ovrLayer.Header.ColorScale.w = tintColor.Alpha(); + } + + virtual const ovrLayerHeader2 * Header() const = 0; + + void SetCurrentEye(device::Eye aEye) { + layer->SetCurrentEye(aEye); + } + + bool IsDrawRequested() const { + return swapChain && layer->IsDrawRequested(); + } + + void ClearRequestDraw() const { + layer->ClearRequestDraw(); + } + + void SetClipEnabled(bool aEnabled) { + if (aEnabled) { + ovrLayer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_CLIP_TO_TEXTURE_RECT; + } else { + ovrLayer.Header.Flags &= ~VRAPI_FRAME_LAYER_FLAG_CLIP_TO_TEXTURE_RECT; + } + } + + + void Destroy() { + if (swapChain != nullptr) { + vrapi_DestroyTextureSwapChain(swapChain); + swapChain = nullptr; + } + layer->SetInitialized(false); + } + + virtual ~OculusLayer() { + Destroy(); + } + +}; + +class OculusLayerQuad; +typedef std::shared_ptr OculusLayerQuadPtr; + +class OculusLayerQuad: public OculusLayer { +public: + ANativeWindow * nativeWindow = nullptr; + jobject surface = nullptr; + vrb::FBOPtr fbo; + + static OculusLayerQuadPtr Create(const VRLayerQuadPtr& aLayer) { + auto result = std::make_shared(); + result->layer = aLayer; + return result; + } + + void Init(JNIEnv * aEnv, vrb::RenderContextPtr& aContext) { + if (swapChain) { + return; + } + + ovrLayer = vrapi_DefaultLayerProjection2(); + ovrLayer.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA; + ovrLayer.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA; + + if (layer->GetSurfaceType() == VRLayerQuad::SurfaceType::AndroidSurface) { + swapChain = vrapi_CreateAndroidSurfaceSwapChain(layer->GetWidth(), layer->GetHeight()); + surface = vrapi_GetTextureSwapChainAndroidSurface(swapChain); + surface = aEnv->NewGlobalRef(surface); + nativeWindow = ANativeWindow_fromSurface(aEnv, surface); + layer->SetSurface(surface); + } else { + swapChain = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_2D, VRAPI_TEXTURE_FORMAT_8888, + layer->GetWidth(), layer->GetHeight(), 1, false); + fbo = vrb::FBO::Create(aContext); + GLuint texture = vrapi_GetTextureSwapChainHandle(swapChain, 0); + VRB_GL_CHECK(glBindTexture(GL_TEXTURE_2D, texture)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + vrb::FBO::Attributes attributes; + attributes.depth = false; + attributes.samples = 0; + VRB_GL_CHECK(fbo->SetTextureHandle(texture, layer->GetWidth(), layer->GetHeight(), attributes)); + if (fbo->IsValid()) { + fbo->Bind(); + VRB_GL_CHECK(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + VRB_GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); + fbo->Unbind(); + } else { + VRB_WARN("FAILED to make valid FBO for OculusLayerQuad"); + } + } + + layer->SetResizeDelegate([=]{ + Resize(); + }); + OculusLayer::Init(); + } + + void Destroy(JNIEnv * aEnv) { + fbo = nullptr; + if (surface) { + aEnv->DeleteGlobalRef(surface); + surface = nullptr; + layer->SetSurface(nullptr); + } + OculusLayer::Destroy(); + } + + void Update(const ovrTracking2& aTracking) override { + OculusLayer::Update(aTracking); + const float w = layer->GetWorldWidth(); + const float h = layer->GetWorldHeight(); + + vrb::Matrix scale = vrb::Matrix::Identity(); + scale.ScaleInPlace(vrb::Vector(w * 0.5f, h * 0.5f, 1.0f)); + + bool clip = false; + + for (int i = 0; i < VRAPI_FRAME_LAYER_EYE_MAX; ++i) { + device::Eye eye = i == 0 ? device::Eye::Left : device::Eye::Right; + vrb::Matrix matrix = layer->GetModelView(eye); + matrix.PostMultiplyInPlace(scale); + ovrMatrix4f modelView = ovrMatrixFrom(matrix); + + device::EyeRect textureRect = layer->GetTextureRect(eye); + + ovrLayer.Textures[i].ColorSwapChain = swapChain; + ovrLayer.Textures[i].SwapChainIndex = 0; + ovrLayer.Textures[i].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromUnitSquare(&modelView); + ovrLayer.Textures[i].TextureRect.x = textureRect.mX; + ovrLayer.Textures[i].TextureRect.y = textureRect.mY; + ovrLayer.Textures[i].TextureRect.width = textureRect.mWidth; + ovrLayer.Textures[i].TextureRect.height = textureRect.mHeight; + clip = clip || !textureRect.IsDefault(); + } + SetClipEnabled(clip); + + ovrLayer.HeadPose = aTracking.HeadPose; + } + + void Resize() { + if (nativeWindow && swapChain) { + ANativeWindow_setBuffersGeometry(nativeWindow, layer->GetWidth(), layer->GetHeight(), 0 /* Format unchanged */); + } + } + + const ovrLayerHeader2 * Header() const override { + return &ovrLayer.Header; + } +}; + +class OculusLayerCube; +typedef std::shared_ptr OculusLayerCubePtr; + +class OculusLayerCube: public OculusLayer { +public: + static OculusLayerCubePtr Create(const VRLayerCubePtr& aLayer) { + auto result = std::make_shared(); + result->layer = aLayer; + return result; + } + + void Init() { + if (swapChain) { + return; + } + + ovrLayer = vrapi_DefaultLayerCube2(); + ovrLayer.Offset.x = 0.0f; + ovrLayer.Offset.y = 0.0f; + ovrLayer.Offset.z = 0.0f; + swapChain = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_CUBE, VRAPI_TEXTURE_FORMAT_8888, layer->GetWidth(), layer->GetHeight(), 1, false); + layer->SetTextureHandle(vrapi_GetTextureSwapChainHandle(swapChain, 0)); + OculusLayer::Init(); + } + + void Destroy() { + if (swapChain == nullptr) { + return; + } + layer->SetTextureHandle(0); + layer->SetLoaded(false); + OculusLayer::Destroy(); + } + + bool IsLoaded() const { + return layer->IsLoaded(); + } + + void Update(const ovrTracking2& aTracking) override { + OculusLayer::Update(aTracking); + const ovrMatrix4f centerEyeViewMatrix = vrapi_GetViewMatrixFromPose(&aTracking.HeadPose.Pose); + const ovrMatrix4f cubeMatrix = ovrMatrix4f_TanAngleMatrixForCubeMap(¢erEyeViewMatrix); + ovrLayer.HeadPose = aTracking.HeadPose; + ovrLayer.TexCoordsFromTanAngles = cubeMatrix; + + for (int i = 0; i < VRAPI_FRAME_LAYER_EYE_MAX; ++i) { + ovrLayer.Textures[i].ColorSwapChain = swapChain; + ovrLayer.Textures[i].SwapChainIndex = 0; + } + } + + const ovrLayerHeader2 * Header() const override { + return &ovrLayer.Header; + } +}; + + +class OculusLayerEquirect; +typedef std::shared_ptr OculusLayerEquirectPtr; + +class OculusLayerEquirect: public OculusLayer { +public: + std::weak_ptr sourceLayer; + + static OculusLayerEquirectPtr Create(const VRLayerEquirectPtr& aLayer, const OculusLayerQuadPtr& aSourceLayer = nullptr) { + auto result = std::make_shared(); + result->layer = aLayer; + result->sourceLayer = aSourceLayer; + return result; + } + + void Init() { + OculusLayerQuadPtr source = sourceLayer.lock(); + if (!source) { + return; + } + + swapChain = source->swapChain; + ovrLayer = vrapi_DefaultLayerEquirect2(); + ovrLayer.HeadPose.Pose.Position.x = 0.0f; + ovrLayer.HeadPose.Pose.Position.y = 0.0f; + ovrLayer.HeadPose.Pose.Position.z = 0.0f; + ovrLayer.HeadPose.Pose.Orientation.x = 0.0f; + ovrLayer.HeadPose.Pose.Orientation.y = 0.0f; + ovrLayer.HeadPose.Pose.Orientation.z = 0.0f; + ovrLayer.HeadPose.Pose.Orientation.w = 1.0f; + ovrLayer.TexCoordsFromTanAngles = ovrMatrix4f_CreateIdentity(); + OculusLayer::Init(); + } + + void Destroy() { + swapChain = nullptr; + OculusLayer::Destroy(); + } + + void Update(const ovrTracking2& aTracking) override { + OculusLayer::Update(aTracking); + + vrb::Quaternion q(layer->GetModelTransform(device::Eye::Left)); + ovrLayer.HeadPose.Pose.Orientation.x = q.x(); + ovrLayer.HeadPose.Pose.Orientation.y = q.y(); + ovrLayer.HeadPose.Pose.Orientation.z = q.z(); + ovrLayer.HeadPose.Pose.Orientation.w = q.w(); + + bool clip = false; + for (int i = 0; i < VRAPI_FRAME_LAYER_EYE_MAX; ++i) { + const device::Eye eye = i == 0 ? device::Eye::Left : device::Eye::Right; + ovrLayer.Textures[i].ColorSwapChain = swapChain; + ovrLayer.Textures[i].SwapChainIndex = 0; + const vrb::Vector scale = layer->GetUVTransform(eye).GetScale(); + const vrb::Vector translation = layer->GetUVTransform(eye).GetTranslation(); + + ovrLayer.Textures[i].TextureMatrix.M[0][0] = scale.x(); + ovrLayer.Textures[i].TextureMatrix.M[1][1] = scale.y(); + ovrLayer.Textures[i].TextureMatrix.M[0][2] = translation.x(); + ovrLayer.Textures[i].TextureMatrix.M[1][2] = translation.y(); + + device::EyeRect textureRect = layer->GetTextureRect(eye); + ovrLayer.Textures[i].TextureRect.x = textureRect.mX; + ovrLayer.Textures[i].TextureRect.y = textureRect.mY; + ovrLayer.Textures[i].TextureRect.width = textureRect.mWidth; + ovrLayer.Textures[i].TextureRect.height = textureRect.mHeight; + clip = clip || !textureRect.IsDefault(); + } + SetClipEnabled(clip); + } + + const ovrLayerHeader2 * Header() const override { + return &ovrLayer.Header; + } +}; + struct DeviceDelegateOculusVR::State { vrb::RenderContextWeak context; android_app* app = nullptr; bool initialized = false; + bool layersEnabled = true; ovrJava java = {}; ovrMobile* ovr = nullptr; OculusEyeSwapChainPtr eyeSwapChains[VRAPI_EYE_COUNT]; + OculusLayerCubePtr cubeLayer; + OculusLayerEquirectPtr equirectLayer; + std::vector uiLayers; device::RenderMode renderMode = device::RenderMode::StandAlone; vrb::FBOPtr currentFBO; + vrb::FBOPtr previousFBO; vrb::CameraEyePtr cameras[2]; uint32_t frameIndex = 0; double predictedDisplayTime = 0; @@ -139,6 +472,7 @@ struct DeviceDelegateOculusVR::State { void Initialize() { elbow = ElbowModel::Create(); + layersEnabled = VRBrowser::AreLayersEnabled(); vrb::RenderContextPtr localContext = context.lock(); java.Vm = app->activity->vm; @@ -178,8 +512,9 @@ struct DeviceDelegateOculusVR::State { } void GetStandaloneRenderSize(uint32_t& aWidth, uint32_t& aHeight) { - aWidth = 1.5f * (uint32_t)(vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH)); - aHeight = 1.5f * (uint32_t)(vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT)); + const float scale = layersEnabled ? 1.0 : 1.5f; + aWidth = scale * (uint32_t)(vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH)); + aHeight = scale * (uint32_t)(vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT)); } void SetRenderSize(device::RenderMode aRenderMode) { @@ -297,6 +632,33 @@ struct DeviceDelegateOculusVR::State { trackpadTouched ? trackpadY * 2.0f - 1.0f : 0.0f }; controller->SetAxes(0, axes, kNumAxes); } + + void HandleQuadLayerBind(const OculusLayerQuadPtr& aLayer, GLenum aTarget, bool bound) { + if (!bound) { + if (currentFBO && currentFBO == aLayer->fbo) { + currentFBO->Unbind(); + currentFBO = nullptr; + } + if (previousFBO) { + previousFBO->Bind(); + currentFBO = previousFBO; + previousFBO = nullptr; + } + return; + } + + if (currentFBO == aLayer->fbo) { + // Layer already bound + return; + } + + if (currentFBO) { + currentFBO->Unbind(); + } + previousFBO = currentFBO; + aLayer->fbo->Bind(aTarget); + currentFBO = aLayer->fbo; + } }; DeviceDelegateOculusVRPtr @@ -489,6 +851,10 @@ DeviceDelegateOculusVR::BindEye(const device::Eye aWhich) { } else { VRB_LOG("No Swap chain FBO found"); } + + for (const OculusLayerQuadPtr& layer: m.uiLayers) { + layer->SetCurrentEye(aWhich); + } } void @@ -506,18 +872,52 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) { return; } - auto layer = vrapi_DefaultLayerProjection2(); - layer.HeadPose = m.predictedTracking.HeadPose; + uint32_t layerCount = 0; + const ovrLayerHeader2* layers[ovrMaxLayerCount] = {}; + + if (m.cubeLayer && m.cubeLayer->IsLoaded() && m.cubeLayer->IsDrawRequested()) { + m.cubeLayer->Update(m.predictedTracking); + layers[layerCount++] = m.cubeLayer->Header(); + m.cubeLayer->ClearRequestDraw(); + } + + if (m.equirectLayer && m.equirectLayer->IsDrawRequested()) { + m.equirectLayer->Update(m.predictedTracking); + layers[layerCount++] = m.equirectLayer->Header(); + m.equirectLayer->ClearRequestDraw(); + } + + // Sort quad layers by draw priority + std::sort(m.uiLayers.begin(), m.uiLayers.end(), [](const OculusLayerQuadPtr & a, OculusLayerQuadPtr & b) -> bool { + return a->layer->ShouldDrawBefore(*b->layer); + }); + + for (const OculusLayerQuadPtr& layer: m.uiLayers) { + if (layer->IsDrawRequested() && layerCount < ovrMaxLayerCount) { + layer->Update(m.predictedTracking); + layers[layerCount++] = layer->Header(); + layer->ClearRequestDraw(); + } + } + + // Add main eye buffer layer + ovrLayerProjection2 projection = vrapi_DefaultLayerProjection2(); + projection.HeadPose = m.predictedTracking.HeadPose; + projection.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA; + projection.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA; for (int i = 0; i < VRAPI_FRAME_LAYER_EYE_MAX; ++i) { const auto &eyeSwapChain = m.eyeSwapChains[i]; int swapChainIndex = m.frameIndex % eyeSwapChain->swapChainLength; // Set up OVR layer textures - layer.Textures[i].ColorSwapChain = eyeSwapChain->ovrSwapChain; - layer.Textures[i].SwapChainIndex = swapChainIndex; - layer.Textures[i].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( + projection.Textures[i].ColorSwapChain = eyeSwapChain->ovrSwapChain; + projection.Textures[i].SwapChainIndex = swapChainIndex; + projection.Textures[i].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( &m.predictedTracking.Eye[i].ProjectionMatrix); } + layers[layerCount++] = &projection.Header; + + // Submit all layers to TimeWarp ovrSubmitFrameDescription2 frameDesc = {}; frameDesc.Flags = 0; if (m.renderMode == device::RenderMode::Immersive) { @@ -528,13 +928,91 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) { frameDesc.DisplayTime = m.predictedDisplayTime; frameDesc.CompletionFence = 0; - ovrLayerHeader2* layers[] = {&layer.Header}; - frameDesc.LayerCount = sizeof(layers) / sizeof(layers[0]); + frameDesc.LayerCount = layerCount; frameDesc.Layers = layers; vrapi_SubmitFrame2(m.ovr, &frameDesc); } +VRLayerQuadPtr +DeviceDelegateOculusVR::CreateLayerQuad(int32_t aWidth, int32_t aHeight, + VRLayerQuad::SurfaceType aSurfaceType) { + if (!m.layersEnabled) { + return nullptr; + } + VRLayerQuadPtr layer = VRLayerQuad::Create(aWidth, aHeight, aSurfaceType); + OculusLayerQuadPtr oculusLayer = OculusLayerQuad::Create(layer); + if (m.ovr) { + vrb::RenderContextPtr context = m.context.lock(); + oculusLayer->Init(m.java.Env, context); + } + m.uiLayers.push_back(oculusLayer); + if (aSurfaceType == VRLayerQuad::SurfaceType::FBO) { + std::weak_ptr weakLayer = oculusLayer; + layer->SetBindDelegate([=](GLenum aTarget, bool bound){ + OculusLayerQuadPtr layer = weakLayer.lock(); + if (layer) { + m.HandleQuadLayerBind(layer, aTarget, bound); + } + }); + if (m.currentFBO) { + m.currentFBO->Bind(); + } + } + return layer; +} + +VRLayerCubePtr +DeviceDelegateOculusVR::CreateLayerCube(int32_t aWidth, int32_t aHeight) { + if (!m.layersEnabled) { + return nullptr; + } + if (m.cubeLayer) { + m.cubeLayer->Destroy(); + } + VRLayerCubePtr layer = VRLayerCube::Create(aWidth, aHeight); + m.cubeLayer = OculusLayerCube::Create(layer); + if (m.ovr) { + m.cubeLayer->Init(); + } + return layer; +} + +VRLayerEquirectPtr +DeviceDelegateOculusVR::CreateLayerEquirect(const VRLayerQuadPtr &aSource) { + VRLayerEquirectPtr result = VRLayerEquirect::Create(); + OculusLayerQuadPtr source; + for (const OculusLayerQuadPtr& layer: m.uiLayers) { + if (layer->layer == aSource) { + source = layer; + break; + } + } + if (m.equirectLayer) { + m.equirectLayer->Destroy(); + } + m.equirectLayer = OculusLayerEquirect::Create(result, source); + if (m.ovr) { + m.equirectLayer->Init(); + } + return result; +} + +void +DeviceDelegateOculusVR::DeleteLayer(const VRLayerPtr& aLayer) { + if (m.cubeLayer && m.cubeLayer->layer == aLayer) { + m.cubeLayer->Destroy(); + m.cubeLayer = nullptr; + return; + } + for (int i = 0; i < m.uiLayers.size(); ++i) { + if (m.uiLayers[i]->layer.get() == aLayer.get()) { + m.uiLayers.erase(m.uiLayers.begin() + i); + return; + } + } +} + void DeviceDelegateOculusVR::EnterVR(const crow::BrowserEGLContext& aEGLContext) { if (m.ovr) { @@ -545,6 +1023,16 @@ DeviceDelegateOculusVR::EnterVR(const crow::BrowserEGLContext& aEGLContext) { for (int i = 0; i < VRAPI_EYE_COUNT; ++i) { m.eyeSwapChains[i]->Init(render, m.renderMode, m.renderWidth, m.renderHeight); } + vrb::RenderContextPtr context = m.context.lock(); + for (OculusLayerQuadPtr& layer: m.uiLayers) { + layer->Init(m.java.Env, context); + } + if (m.cubeLayer) { + m.cubeLayer->Init(); + } + if (m.equirectLayer) { + m.equirectLayer->Init(); + } ovrModeParms modeParms = vrapi_DefaultModeParms(&m.java); modeParms.Flags |= VRAPI_MODE_FLAG_NATIVE_WINDOW; @@ -566,11 +1054,13 @@ DeviceDelegateOculusVR::EnterVR(const crow::BrowserEGLContext& aEGLContext) { // Reset reorientation after Enter VR m.reorientMatrix = vrb::Matrix::Identity(); - vrapi_SetRemoteEmulation(m.ovr, false); + vrapi_SetRemoteEmulation(m.ovr, true); } void DeviceDelegateOculusVR::LeaveVR() { + m.currentFBO = nullptr; + m.previousFBO = nullptr; if (m.ovr) { vrapi_LeaveVrMode(m.ovr); m.ovr = nullptr; @@ -579,6 +1069,15 @@ DeviceDelegateOculusVR::LeaveVR() { for (int i = 0; i < VRAPI_EYE_COUNT; ++i) { m.eyeSwapChains[i]->Destroy(); } + for (OculusLayerQuadPtr& layer: m.uiLayers) { + layer->Destroy(m.java.Env); + } + if (m.cubeLayer) { + m.cubeLayer->Destroy(); + } + if (m.equirectLayer) { + m.equirectLayer->Destroy(); + } } bool diff --git a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h index 70e1ff88a3..1aac67a347 100644 --- a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h +++ b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h @@ -41,6 +41,11 @@ class DeviceDelegateOculusVR : public DeviceDelegate { void StartFrame() override; void BindEye(const device::Eye aWhich) override; void EndFrame(const bool aDiscard) override; + VRLayerQuadPtr CreateLayerQuad(int32_t aWidth, int32_t aHeight, + VRLayerQuad::SurfaceType aSurfaceType) override; + VRLayerCubePtr CreateLayerCube(int32_t aWidth, int32_t aHeight) override; + VRLayerEquirectPtr CreateLayerEquirect(const VRLayerQuadPtr &aSource) override; + void DeleteLayer(const VRLayerPtr& aLayer) override; // Custom methods for NativeActivity render loop based devices. void EnterVR(const crow::BrowserEGLContext& aEGLContext); void LeaveVR(); diff --git a/build.gradle b/build.gradle index cd87570572..36b8eb54f5 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { ext.geckoNightly = [ // GeckoView versions can be found here: // https://maven.mozilla.org/?prefix=maven2/org/mozilla/geckoview/ - version: '65.0.20181110100141' + version: '65.0.20181118100059' ] // Android components version