SkAR Java: refactored project structure
1) Changed file paths 2) Added helpers that were missing previously Bug: skia: Change-Id: Idb4194fcef815a23277a17670acf46255a47451d Reviewed-on: https://skia-review.googlesource.com/143680 Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
parent
3ab3b562b1
commit
f27b479f95
@ -1,4 +1,4 @@
|
||||
package com.google.ar.core.examples.java.helloskar;
|
||||
package com.google.skar.examples.helloskar.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.ar.core.examples.java.helloskar;
|
||||
package com.google.skar.examples.helloskar.app;
|
||||
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
@ -1,4 +1,4 @@
|
||||
package com.google.skar;
|
||||
package com.google.skar.examples.helloskar.app;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Path;
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.helpers;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
/**
|
||||
* Helper to ask camera permission.
|
||||
*/
|
||||
public final class CameraPermissionHelper {
|
||||
private static final int CAMERA_PERMISSION_CODE = 0;
|
||||
private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA;
|
||||
|
||||
/**
|
||||
* Check to see we have the necessary permissions for this app.
|
||||
*/
|
||||
public static boolean hasCameraPermission(Activity activity) {
|
||||
return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see we have the necessary permissions for this app, and ask for them if we don't.
|
||||
*/
|
||||
public static void requestCameraPermission(Activity activity) {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity, new String[]{CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we need to show the rationale for this permission.
|
||||
*/
|
||||
public static boolean shouldShowRequestPermissionRationale(Activity activity) {
|
||||
return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch Application Setting to grant permission.
|
||||
*/
|
||||
public static void launchPermissionSettings(Activity activity) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.helpers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.google.ar.core.Session;
|
||||
|
||||
/**
|
||||
* Helper to track the display rotations. In particular, the 180 degree rotations are not notified
|
||||
* by the onSurfaceChanged() callback, and thus they require listening to the android display
|
||||
* events.
|
||||
*/
|
||||
public final class DisplayRotationHelper implements DisplayListener {
|
||||
private boolean viewportChanged;
|
||||
private int viewportWidth;
|
||||
private int viewportHeight;
|
||||
private final Context context;
|
||||
private final Display display;
|
||||
|
||||
/**
|
||||
* Constructs the DisplayRotationHelper but does not register the listener yet.
|
||||
*
|
||||
* @param context the Android {@link Context}.
|
||||
*/
|
||||
public DisplayRotationHelper(Context context) {
|
||||
this.context = context;
|
||||
display = context.getSystemService(WindowManager.class).getDefaultDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the display listener. Should be called from {@link Activity#onResume()}.
|
||||
*/
|
||||
public void onResume() {
|
||||
context.getSystemService(DisplayManager.class).registerDisplayListener(this, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the display listener. Should be called from {@link Activity#onPause()}.
|
||||
*/
|
||||
public void onPause() {
|
||||
context.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a change in surface dimensions. This will be later used by {@link
|
||||
* #updateSessionIfNeeded(Session)}. Should be called from {@link
|
||||
* android.opengl.GLSurfaceView.Renderer
|
||||
* #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}.
|
||||
*
|
||||
* @param width the updated width of the surface.
|
||||
* @param height the updated height of the surface.
|
||||
*/
|
||||
public void onSurfaceChanged(int width, int height) {
|
||||
viewportWidth = width;
|
||||
viewportHeight = height;
|
||||
viewportChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the session display geometry if a change was posted either by {@link
|
||||
* #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This
|
||||
* function should be called explicitly before each call to {@link Session#update()}. This
|
||||
* function will also clear the 'pending update' (viewportChanged) flag.
|
||||
*
|
||||
* @param session the {@link Session} object to update if display geometry changed.
|
||||
*/
|
||||
public void updateSessionIfNeeded(Session session) {
|
||||
if (viewportChanged) {
|
||||
int displayRotation = display.getRotation();
|
||||
session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight);
|
||||
viewportChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current rotation state of android display. Same as {@link Display#getRotation()}.
|
||||
*/
|
||||
public int getRotation() {
|
||||
return display.getRotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
viewportChanged = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.helpers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* Helper to set up the Android full screen mode.
|
||||
*/
|
||||
public final class FullScreenHelper {
|
||||
/**
|
||||
* Sets the Android fullscreen flags. Expected to be called from {@link
|
||||
* Activity#onWindowFocusChanged(boolean hasFocus)}.
|
||||
*
|
||||
* @param activity the Activity on which the full screen mode will be set.
|
||||
* @param hasFocus the hasFocus flag passed from the {@link Activity#onWindowFocusChanged(boolean
|
||||
* hasFocus)} callback.
|
||||
*/
|
||||
public static void setFullScreenOnWindowFocusChanged(Activity activity, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
// https://developer.android.com/training/system-ui/immersive.html#sticky
|
||||
activity
|
||||
.getWindow()
|
||||
.getDecorView()
|
||||
.setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.helpers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.design.widget.BaseTransientBottomBar;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Helper to manage the sample snackbar. Hides the Android boilerplate code, and exposes simpler
|
||||
* methods.
|
||||
*/
|
||||
public final class SnackbarHelper {
|
||||
private static final int BACKGROUND_COLOR = 0xbf323232;
|
||||
private Snackbar messageSnackbar;
|
||||
|
||||
private enum DismissBehavior {HIDE, SHOW, FINISH}
|
||||
|
||||
;
|
||||
|
||||
public boolean isShowing() {
|
||||
return messageSnackbar != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a snackbar with a given message.
|
||||
*/
|
||||
public void showMessage(Activity activity, String message) {
|
||||
show(activity, message, DismissBehavior.HIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a snackbar with a given message, and a dismiss button.
|
||||
*/
|
||||
public void showMessageWithDismiss(Activity activity, String message) {
|
||||
show(activity, message, DismissBehavior.SHOW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a snackbar with a given error message. When dismissed, will finish the activity. Useful
|
||||
* for notifying errors, where no further interaction with the activity is possible.
|
||||
*/
|
||||
public void showError(Activity activity, String errorMessage) {
|
||||
show(activity, errorMessage, DismissBehavior.FINISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the currently showing snackbar, if there is one. Safe to call from any thread. Safe to
|
||||
* call even if snackbar is not shown.
|
||||
*/
|
||||
public void hide(Activity activity) {
|
||||
activity.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (messageSnackbar != null) {
|
||||
messageSnackbar.dismiss();
|
||||
}
|
||||
messageSnackbar = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void show(
|
||||
final Activity activity, final String message, final DismissBehavior dismissBehavior) {
|
||||
activity.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
messageSnackbar =
|
||||
Snackbar.make(
|
||||
activity.findViewById(android.R.id.content),
|
||||
message,
|
||||
Snackbar.LENGTH_INDEFINITE);
|
||||
messageSnackbar.getView().setBackgroundColor(BACKGROUND_COLOR);
|
||||
if (dismissBehavior != DismissBehavior.HIDE) {
|
||||
messageSnackbar.setAction(
|
||||
"Dismiss",
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
messageSnackbar.dismiss();
|
||||
}
|
||||
});
|
||||
if (dismissBehavior == DismissBehavior.FINISH) {
|
||||
messageSnackbar.addCallback(
|
||||
new BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
@Override
|
||||
public void onDismissed(Snackbar transientBottomBar, int event) {
|
||||
super.onDismissed(transientBottomBar, event);
|
||||
activity.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
messageSnackbar.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* Helper to detect taps using Android GestureDetector, and pass the taps between UI thread and
|
||||
* render thread.
|
||||
*/
|
||||
public final class TapHelper implements OnTouchListener {
|
||||
private final GestureDetector gestureDetector;
|
||||
private final BlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
|
||||
private final BlockingQueue<ScrollEvent> queuedFingerHold = new ArrayBlockingQueue<>(16);
|
||||
private boolean isScrolling = false;
|
||||
private boolean previousScroll = true;
|
||||
|
||||
public static class ScrollEvent {
|
||||
public MotionEvent e;
|
||||
public boolean isStartOfScroll;
|
||||
|
||||
public ScrollEvent(MotionEvent e, boolean isStartOfScroll) {
|
||||
this.e = e;
|
||||
this.isStartOfScroll = isStartOfScroll;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the tap helper.
|
||||
*
|
||||
* @param context the application's context.
|
||||
*/
|
||||
public TapHelper(Context context) {
|
||||
gestureDetector =
|
||||
new GestureDetector(
|
||||
context,
|
||||
new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
// Queue tap if there is space. Tap is lost if queue is full.
|
||||
queuedSingleTaps.offer(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
// Queue motion events when scrolling
|
||||
if (e2.getPointerCount() == 1 && e1.getPointerCount() == 1) {
|
||||
previousScroll = isScrolling;
|
||||
isScrolling = true;
|
||||
queuedFingerHold.offer(new ScrollEvent(e2, startedScrolling()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for a tap.
|
||||
*
|
||||
* @return if a tap was queued, a MotionEvent for the tap. Otherwise null if no taps are queued.
|
||||
*/
|
||||
public MotionEvent poll() {
|
||||
return queuedSingleTaps.poll();
|
||||
}
|
||||
|
||||
public ScrollEvent holdPoll() { return queuedFingerHold.poll(); }
|
||||
|
||||
public boolean startedScrolling() {
|
||||
return isScrolling && !previousScroll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
boolean val = gestureDetector.onTouchEvent(motionEvent);
|
||||
|
||||
// If finger is up + is scrolling: don't scroll anymore, and empty Touch Hold queue
|
||||
if (motionEvent.getAction() == MotionEvent.ACTION_UP && isScrolling) {
|
||||
previousScroll = true;
|
||||
isScrolling = false;
|
||||
queuedFingerHold.clear();
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.rendering;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLSurfaceView;
|
||||
|
||||
import com.google.ar.core.Frame;
|
||||
import com.google.ar.core.Session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* This class renders the AR background from camera feed. It creates and hosts the texture given to
|
||||
* ARCore to be filled with the camera image.
|
||||
*/
|
||||
public class BackgroundRenderer {
|
||||
private static final String TAG = BackgroundRenderer.class.getSimpleName();
|
||||
|
||||
// Shader names.
|
||||
private static final String VERTEX_SHADER_NAME = "shaders/screenquad.vert";
|
||||
private static final String FRAGMENT_SHADER_NAME = "shaders/screenquad.frag";
|
||||
|
||||
private static final int COORDS_PER_VERTEX = 3;
|
||||
private static final int TEXCOORDS_PER_VERTEX = 2;
|
||||
private static final int FLOAT_SIZE = 4;
|
||||
|
||||
private FloatBuffer quadVertices;
|
||||
private FloatBuffer quadTexCoord;
|
||||
private FloatBuffer quadTexCoordTransformed;
|
||||
|
||||
private int quadProgram;
|
||||
|
||||
private int quadPositionParam;
|
||||
private int quadTexCoordParam;
|
||||
private int textureId = -1;
|
||||
|
||||
public BackgroundRenderer() {
|
||||
}
|
||||
|
||||
public int getTextureId() {
|
||||
return textureId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and initializes OpenGL resources needed by the background renderer. Must be called on
|
||||
* the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10,
|
||||
* EGLConfig)}.
|
||||
*
|
||||
* @param context Needed to access shader source.
|
||||
*/
|
||||
public void createOnGlThread(Context context) throws IOException {
|
||||
// Generate the background texture.
|
||||
int[] textures = new int[1];
|
||||
GLES20.glGenTextures(1, textures, 0);
|
||||
textureId = textures[0];
|
||||
int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
|
||||
GLES20.glBindTexture(textureTarget, textureId);
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
|
||||
|
||||
int numVertices = 4;
|
||||
if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) {
|
||||
throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer.");
|
||||
}
|
||||
|
||||
ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE);
|
||||
bbVertices.order(ByteOrder.nativeOrder());
|
||||
quadVertices = bbVertices.asFloatBuffer();
|
||||
quadVertices.put(QUAD_COORDS);
|
||||
quadVertices.position(0);
|
||||
|
||||
ByteBuffer bbTexCoords =
|
||||
ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
|
||||
bbTexCoords.order(ByteOrder.nativeOrder());
|
||||
quadTexCoord = bbTexCoords.asFloatBuffer();
|
||||
quadTexCoord.put(QUAD_TEXCOORDS);
|
||||
quadTexCoord.position(0);
|
||||
|
||||
ByteBuffer bbTexCoordsTransformed =
|
||||
ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
|
||||
bbTexCoordsTransformed.order(ByteOrder.nativeOrder());
|
||||
quadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer();
|
||||
|
||||
int vertexShader =
|
||||
ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
|
||||
int fragmentShader =
|
||||
ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);
|
||||
|
||||
quadProgram = GLES20.glCreateProgram();
|
||||
GLES20.glAttachShader(quadProgram, vertexShader);
|
||||
GLES20.glAttachShader(quadProgram, fragmentShader);
|
||||
GLES20.glLinkProgram(quadProgram);
|
||||
GLES20.glUseProgram(quadProgram);
|
||||
|
||||
ShaderUtil.checkGLError(TAG, "Program creation");
|
||||
|
||||
quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position");
|
||||
quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord");
|
||||
|
||||
ShaderUtil.checkGLError(TAG, "Program parameters");
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the AR background image. The image will be drawn such that virtual content rendered with
|
||||
* the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and
|
||||
* {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will
|
||||
* accurately follow static physical objects. This must be called <b>before</b> drawing virtual
|
||||
* content.
|
||||
*
|
||||
* @param frame The last {@code Frame} returned by {@link Session#update()}.
|
||||
*/
|
||||
public void draw(Frame frame) {
|
||||
// If display rotation changed (also includes view size change), we need to re-query the uv
|
||||
// coordinates for the screen rect, as they may have changed as well.
|
||||
if (frame.hasDisplayGeometryChanged()) {
|
||||
frame.transformDisplayUvCoords(quadTexCoord, quadTexCoordTransformed);
|
||||
}
|
||||
|
||||
// No need to test or write depth, the screen quad has arbitrary depth, and is expected
|
||||
// to be drawn first.
|
||||
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
|
||||
GLES20.glDepthMask(false);
|
||||
|
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
|
||||
|
||||
GLES20.glUseProgram(quadProgram);
|
||||
|
||||
// Set the vertex positions.
|
||||
GLES20.glVertexAttribPointer(
|
||||
quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadVertices);
|
||||
|
||||
// Set the texture coordinates.
|
||||
GLES20.glVertexAttribPointer(
|
||||
quadTexCoordParam,
|
||||
TEXCOORDS_PER_VERTEX,
|
||||
GLES20.GL_FLOAT,
|
||||
false,
|
||||
0,
|
||||
quadTexCoordTransformed);
|
||||
|
||||
// Enable vertex arrays
|
||||
GLES20.glEnableVertexAttribArray(quadPositionParam);
|
||||
GLES20.glEnableVertexAttribArray(quadTexCoordParam);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Disable vertex arrays
|
||||
GLES20.glDisableVertexAttribArray(quadPositionParam);
|
||||
GLES20.glDisableVertexAttribArray(quadTexCoordParam);
|
||||
|
||||
// Restore the depth state for further drawing.
|
||||
GLES20.glDepthMask(true);
|
||||
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
|
||||
|
||||
ShaderUtil.checkGLError(TAG, "Draw");
|
||||
}
|
||||
|
||||
private static final float[] QUAD_COORDS =
|
||||
new float[]{
|
||||
-1.0f, -1.0f, 0.0f, -1.0f, +1.0f, 0.0f, +1.0f, -1.0f, 0.0f, +1.0f, +1.0f, 0.0f,
|
||||
};
|
||||
|
||||
private static final float[] QUAD_TEXCOORDS =
|
||||
new float[]{
|
||||
0.0f, 1.0f,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
};
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.google.ar.core.examples.java.helloskar;
|
||||
package com.google.skar.examples.helloskar.rendering;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.skar.examples.helloskar.rendering;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Shader helper functions.
|
||||
*/
|
||||
public class ShaderUtil {
|
||||
/**
|
||||
* Converts a raw text file, saved as a resource, into an OpenGL ES shader.
|
||||
*
|
||||
* @param type The type of shader we will be creating.
|
||||
* @param filename The filename of the asset file about to be turned into a shader.
|
||||
* @return The shader object handler.
|
||||
*/
|
||||
public static int loadGLShader(String tag, Context context, int type, String filename)
|
||||
throws IOException {
|
||||
String code = readRawTextFileFromAssets(context, filename);
|
||||
int shader = GLES20.glCreateShader(type);
|
||||
GLES20.glShaderSource(shader, code);
|
||||
GLES20.glCompileShader(shader);
|
||||
|
||||
// Get the compilation status.
|
||||
final int[] compileStatus = new int[1];
|
||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
|
||||
|
||||
// If the compilation failed, delete the shader.
|
||||
if (compileStatus[0] == 0) {
|
||||
Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
|
||||
GLES20.glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
|
||||
if (shader == 0) {
|
||||
throw new RuntimeException("Error creating shader.");
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we've had an error inside of OpenGL ES, and if so what that error is.
|
||||
*
|
||||
* @param label Label to report in case of error.
|
||||
* @throws RuntimeException If an OpenGL error is detected.
|
||||
*/
|
||||
public static void checkGLError(String tag, String label) {
|
||||
int lastError = GLES20.GL_NO_ERROR;
|
||||
// Drain the queue of all errors.
|
||||
int error;
|
||||
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
|
||||
Log.e(tag, label + ": glError " + error);
|
||||
lastError = error;
|
||||
}
|
||||
if (lastError != GLES20.GL_NO_ERROR) {
|
||||
throw new RuntimeException(label + ": glError " + lastError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw text file into a string.
|
||||
*
|
||||
* @param filename The filename of the asset file about to be turned into a shader.
|
||||
* @return The context of the text file, or null in case of error.
|
||||
*/
|
||||
private static String readRawTextFileFromAssets(Context context, String filename)
|
||||
throws IOException {
|
||||
try (InputStream inputStream = context.getAssets().open(filename);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user