Tango Project LoadADF and render object in Rajawali3D surface - android

I am trying to load the latest ADF and Localize it.If the text is localized then it would be displayed in a textView.
If it is localized then on clicking save button editText would appear and the text of button will be changed to "save Object" on clicking that the text entered would be toasted.
I am using Rajawali 3D surface view to render the Video.
But on running it My screen is black only textView and button are shown.
Localization TextView is also blank.
Here is my MainActivity Code
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final double UPDATE_INTERVAL_MS = 100.0;
private final Object mSharedLock = new Object();
// private static final String sTimestampFormat = "Timestamp: %f";
private static final int SECS_TO_MILLISECS = 1000;
private double mPreviousPoseTimeStamp;
private double mTimeToNextUpdate = UPDATE_INTERVAL_MS;
private TextView mUuidTextView;
private TextView mRelocalizationTextView;
private boolean mIsRelocalized;
private EditText mObjectValueEditText;
private Button mSaveButton;
private boolean mIsLocationSave = false;
private boolean mIsSavePoseData = false;
private static final int INVALID_TEXTURE_ID = 0;
private RajawaliSurfaceView mSurfaceView;
private VideoRenderer mRenderer;
// For all current Tango devices, color camera is in the camera id 0.
private static final int COLOR_CAMERA_ID = 0;
private TangoCameraIntrinsics mIntrinsics;
private Tango mTango;
private TangoConfig mConfig;
private boolean mIsConnected = false;
private double mCameraPoseTimestamp = 0;
// NOTE: Naming indicates which thread is in charge of updating this variable
private int mConnectedTextureIdGlThread = INVALID_TEXTURE_ID;
private AtomicBoolean mIsFrameAvailableTangoThread = new AtomicBoolean(false);
private double mRgbTimestampGlThread;
private int mColorCameraToDisplayAndroidRotation = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startActivityForResult(
Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_ADF_LOAD_SAVE),
Tango.TANGO_INTENT_ACTIVITYCODE);
mSurfaceView = (RajawaliSurfaceView) findViewById(R.id.surfaceview);
mObjectValueEditText = (EditText) findViewById(R.id.location_edittext);
mSaveButton = (Button) findViewById(R.id.save_button);
mRenderer = new VideoRenderer(this);
onClickSaveButton();
// Set-up a dummy OpenGL renderer associated with this surface view
DisplayManager displayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
if (displayManager != null) {
displayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
#Override
public void onDisplayAdded(int displayId) {}
#Override
public void onDisplayChanged(int displayId) {
synchronized (this) {
Display display = getWindowManager().getDefaultDisplay();
Camera.CameraInfo colorCameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(COLOR_CAMERA_ID, colorCameraInfo);
mColorCameraToDisplayAndroidRotation =
getColorCameraToDisplayAndroidRotation(display.getRotation(),
colorCameraInfo.orientation);
mRenderer.updateColorCameraTextureUv(mColorCameraToDisplayAndroidRotation);
}
}
#Override
public void onDisplayRemoved(int displayId) {}
}, null);
}
setupRenderer();
}
private void onClickSaveButton() {
mSaveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mIsLocationSave == false) {
mIsLocationSave = true;
mObjectValueEditText.setVisibility(View.VISIBLE);
mSaveButton.setText(getResources().getString(R.string.save_object));
} else {
mIsLocationSave = false;
String text=mObjectValueEditText.getText().toString();
Toast.makeText(getApplicationContext(),text,Toast.LENGTH_SHORT).show();
mObjectValueEditText.setVisibility(View.INVISIBLE);
//TODO:Add helper logic
mIsSavePoseData=true;
mSaveButton.setText(getResources().getString(R.string.save));
}
}
});
}
#Override
protected void onResume() {
super.onResume();
mSurfaceView.onResume();
// Set render mode to RENDERMODE_CONTINUOUSLY to force getting onDraw callbacks until the
// Tango service is properly set-up and we start getting onFrameAvailable callbacks.
mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
// Initialize Tango Service as a normal Android Service, since we call mTango.disconnect()
// in onPause, this will unbind Tango Service, so every time when onResume gets called, we
// should create a new Tango object.
mTango = new Tango(MainActivity.this, new Runnable() {
// Pass in a Runnable to be called from UI thread when Tango is ready, this Runnable
// will be running on a new thread.
// When Tango is ready, we can call Tango functions safely here only when there is no UI
// thread changes involved.
#Override
public void run() {
// Synchronize against disconnecting while the service is being used in
// the OpenGL thread or in the UI thread.
synchronized (MainActivity.this) {
try {
TangoSupport.initialize();
mConfig = setupTangoConfig(mTango);
mTango.connect(mConfig);
startupTango();
mIsConnected = true;
} catch (TangoOutOfDateException e) {
Log.e(TAG, getString(R.string.exception_out_of_date), e);
} catch (TangoErrorException e) {
Log.e(TAG, getString(R.string.exception_tango_error), e);
} catch (TangoInvalidException e) {
Log.e(TAG, getString(R.string.exception_tango_invalid), e);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
synchronized (MainActivity.this) {
setupTextViewsAndButtons(mTango, true,
true);
}
}
});
}
}
});
}
#Override
protected void onPause() {
super.onPause();
mSurfaceView.onPause();
mIsRelocalized = false;
// Synchronize against disconnecting while the service is being used in the OpenGL
// thread or in the UI thread.
// NOTE: DO NOT lock against this same object in the Tango callback thread.
// Tango.disconnect will block here until all Tango callback calls are finished.
// If you lock against this object in a Tango callback thread it will cause a deadlock.
synchronized (this) {
try {
mIsConnected = false;
mTango.disconnectCamera(TangoCameraIntrinsics.TANGO_CAMERA_COLOR);
// We need to invalidate the connected texture ID so that we cause a
// re-connection in the OpenGL thread after resume
mConnectedTextureIdGlThread = INVALID_TEXTURE_ID;
mTango.disconnect();
mIsConnected = false;
} catch (TangoErrorException e) {
Log.e(TAG, getString(R.string.exception_tango_error), e);
}
}
}
/**
* Sets up the tango configuration object. Make sure mTango object is initialized before
* making this call.
*/
private TangoConfig setupTangoConfig(Tango tango) {
// Create a new Tango Configuration and enable the Camera API
TangoConfig config = tango.getConfig(TangoConfig.CONFIG_TYPE_DEFAULT);
config.putBoolean(TangoConfig.KEY_BOOLEAN_COLORCAMERA, true);
config.putBoolean(TangoConfig.KEY_BOOLEAN_DRIFT_CORRECTION, true);
config.putBoolean(TangoConfig.KEY_BOOLEAN_LOWLATENCYIMUINTEGRATION, true);
config.putBoolean(TangoConfig.KEY_BOOLEAN_LEARNINGMODE, false);
config.putBoolean(TangoConfig.KEY_BOOLEAN_MOTIONTRACKING, true);
// Tango service should automatically attempt to recover when it enters an invalid state.
config.putBoolean(TangoConfig.KEY_BOOLEAN_AUTORECOVERY, true);
// Check for Load ADF/Constant Space relocalization mode.
ArrayList<String> fullUuidList;
// Returns a list of ADFs with their UUIDs.
fullUuidList = tango.listAreaDescriptions();
// Load the latest ADF if ADFs are found.
if (fullUuidList.size() > 0) {
config.putString(TangoConfig.KEY_STRING_AREADESCRIPTION,
fullUuidList.get(fullUuidList.size() - 1));
}
return config;
}
/**
* Set up the callback listeners for the Tango service and obtain other parameters required
* after Tango connection.
* Listen to updates from the RGB camera.
*/
private void startupTango() {
// Lock configuration and connect to Tango
// Select coordinate frame pair
ArrayList<TangoCoordinateFramePair> framePairs = new ArrayList<TangoCoordinateFramePair>();
framePairs.add(new TangoCoordinateFramePair(
TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
TangoPoseData.COORDINATE_FRAME_DEVICE));
framePairs.add(new TangoCoordinateFramePair(
TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION,
TangoPoseData.COORDINATE_FRAME_DEVICE));
framePairs.add(new TangoCoordinateFramePair(
TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION,
TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE));
// Listen for new Tango data
mTango.connectListener(framePairs, new Tango.OnTangoUpdateListener() {
#Override
public void onPoseAvailable(final TangoPoseData pose) {
// Make sure to have atomic access to Tango Data so that UI loop doesn't interfere
// while Pose call back is updating the data.
synchronized (mSharedLock) {
// Check for Device wrt ADF pose, Device wrt Start of Service pose, Start of
// Service wrt ADF pose (This pose determines if the device is relocalized or
// not).
if (pose.baseFrame == TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION
&& pose.targetFrame == TangoPoseData
.COORDINATE_FRAME_START_OF_SERVICE) {
if (pose.statusCode == TangoPoseData.POSE_VALID) {
mIsRelocalized = true;
} else {
mIsRelocalized = false;
}
}
if (mIsSavePoseData&&mIsRelocalized) {
mIsSavePoseData=false;
float [] traslations=pose.getTranslationAsFloats();
Log.i("translation",traslations[0]+" "+traslations[1]+" "+traslations[2]);
}
}
final double deltaTime = (pose.timestamp - mPreviousPoseTimeStamp) *
SECS_TO_MILLISECS;
mPreviousPoseTimeStamp = pose.timestamp;
mTimeToNextUpdate -= deltaTime;
if (mTimeToNextUpdate < 0.0) {
mTimeToNextUpdate = UPDATE_INTERVAL_MS;
runOnUiThread(new Runnable() {
#Override
public void run() {
synchronized (mSharedLock) {
mRelocalizationTextView.setText(mIsRelocalized ?
getString(R.string.localized) :
getString(R.string.not_localized));
}
}
});
}
}
//Deprecated method
#Override
public void onXyzIjAvailable(TangoXyzIjData xyzIj) {
// We are not using onXyzIjAvailable for this app.
}
#Override
public void onPointCloudAvailable(final TangoPointCloudData pointCloudData) {
}
#Override
public void onTangoEvent(final TangoEvent event) {
}
#Override
public void onFrameAvailable(int cameraId) {
Log.d(TAG, "onFrameAvailable");
if (cameraId == TangoCameraIntrinsics.TANGO_CAMERA_COLOR) {
// Now that we are receiving onFrameAvailable callbacks, we can switch
// to RENDERMODE_WHEN_DIRTY to drive the render loop from this callback.
// This will result on a frame rate of approximately 30FPS, in synchrony with
// the RGB camera driver.
// If you need to render at a higher rate (i.e.: if you want to render complex
// animations smoothly) you can use RENDERMODE_CONTINUOUSLY throughout the
// application lifecycle.
if (mSurfaceView.getRenderMode() != GLSurfaceView.RENDERMODE_WHEN_DIRTY) {
mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
mSurfaceView.requestRender();
}
}
});
}
/**
* Sets Texts views to display statistics of Poses being received. This also sets the buttons
* used in the UI. Please note that this needs to be called after TangoService and Config
* objects are initialized since we use them for the SDK related stuff like version number
* etc.
*/
private void setupTextViewsAndButtons(Tango tango, boolean isLearningMode, boolean isLoadAdf) {
mRelocalizationTextView = (TextView) findViewById(R.id.relocalization_textview);
mUuidTextView = (TextView) findViewById(R.id.adf_uuid_textview);
if (isLoadAdf) {
ArrayList<String> fullUuidList;
// Returns a list of ADFs with their UUIDs
fullUuidList = tango.listAreaDescriptions();
if (fullUuidList.size() == 0) {
mUuidTextView.setText(R.string.no_uuid);
} else {
mUuidTextView.setText(getString(R.string.number_of_adfs) + fullUuidList.size()
+ getString(R.string.latest_adf_is)
+ fullUuidList.get(fullUuidList.size() - 1));
}
}
}
/**
* Here is where you would set-up your rendering logic. We're replacing it with a minimalistic,
* dummy example using a standard GLSurfaceView and a basic renderer, for illustration purposes
* only.
*/
private void setupRenderer() {
mRenderer.getCurrentScene().registerFrameCallback(new ASceneFrameCallback() {
#Override
public void onPreFrame(long sceneTime, double deltaTime) {
// into the scene.
// Prevent concurrent access to {#code mIsFrameAvailableTangoThread} from the Tango
// callback thread and service disconnection from an onPause event.
try {
synchronized (MainActivity.this) {
// Don't execute any tango API actions if we're not connected to the service
if (!mIsConnected) {
return;
}
// Set-up scene camera projection to match RGB camera intrinsics.
if (!mRenderer.isSceneCameraConfigured()) {
mRenderer.setProjectionMatrix(
projectionMatrixFromCameraIntrinsics(mIntrinsics,
mColorCameraToDisplayAndroidRotation));
}
// Connect the camera texture to the OpenGL Texture if necessary
// NOTE: When the OpenGL context is recycled, Rajawali may re-generate the
// texture with a different ID.
if (mConnectedTextureIdGlThread != mRenderer.getTextureId()) {
mTango.connectTextureId(TangoCameraIntrinsics.TANGO_CAMERA_COLOR,
mRenderer.getTextureId());
mConnectedTextureIdGlThread = mRenderer.getTextureId();
Log.d(TAG, "connected to texture id: " + mRenderer.getTextureId());
}
if (mIsFrameAvailableTangoThread.compareAndSet(true, false)) {
mRgbTimestampGlThread =
mTango.updateTexture(TangoCameraIntrinsics.TANGO_CAMERA_COLOR);
}
// If a new RGB frame has been rendered, update the camera pose to match.
if (mRgbTimestampGlThread > mCameraPoseTimestamp) {
// Calculate the camera color pose at the camera frame update time in
// OpenGL engine.
//
// When drift correction mode is enabled in config file, we must query
// the device with respect to Area Description pose in order to use the
// drift corrected pose.
//
// Note that if you don't want to use the drift corrected pose, the
// normal device with respect to start of service pose is available.
TangoPoseData lastFramePose = TangoSupport.getPoseAtTime(
mRgbTimestampGlThread,
TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION,
TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR,
TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL,
mColorCameraToDisplayAndroidRotation);
if (lastFramePose.statusCode == TangoPoseData.POSE_VALID) {
// Update the camera pose from the renderer
mRenderer.updateRenderCameraPose(lastFramePose);
mCameraPoseTimestamp = lastFramePose.timestamp;
} else {
// When the pose status is not valid, it indicates the tracking has
// been lost. In this case, we simply stop rendering.
//
// This is also the place to display UI to suggest the user walk
// to recover tracking.
Log.w(TAG, "Can't get device pose at time: " +
mRgbTimestampGlThread);
}
}
}
// Avoid crashing the application due to unhandled exceptions
} catch (TangoErrorException e) {
Log.e(TAG, "Tango API call error within the OpenGL render thread", e);
} catch (Throwable t) {
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
#Override
public void onPreDraw(long sceneTime, double deltaTime) {
}
#Override
public void onPostFrame(long sceneTime, double deltaTime) {
}
#Override
public boolean callPreFrame() {
return true;
}
});
mSurfaceView.setSurfaceRenderer(mRenderer);
}
private static int getColorCameraToDisplayAndroidRotation(int displayRotation,
int cameraRotation) {
int cameraRotationNormalized = 0;
switch (cameraRotation) {
case 90:
cameraRotationNormalized = 1;
break;
case 180:
cameraRotationNormalized = 2;
break;
case 270:
cameraRotationNormalized = 3;
break;
default:
cameraRotationNormalized = 0;
break;
}
int ret = displayRotation - cameraRotationNormalized;
if (ret < 0) {
ret += 4;
}
return ret;
}
/**
* Use Tango camera intrinsics to calculate the projection Matrix for the Rajawali scene.
private static float[] projectionMatrixFromCameraIntrinsics(TangoCameraIntrinsics intrinsics,
int rotation) {
// Adjust camera intrinsics according to rotation
float cx = (float) intrinsics.cx;
float cy = (float) intrinsics.cy;
float width = (float) intrinsics.width;
float height = (float) intrinsics.height;
float fx = (float) intrinsics.fx;
float fy = (float) intrinsics.fy;
switch (rotation) {
case Surface.ROTATION_90:
cx = (float) intrinsics.cy;
cy = (float) intrinsics.width - (float) intrinsics.cx;
width = (float) intrinsics.height;
height = (float) intrinsics.width;
fx = (float) intrinsics.fy;
fy = (float) intrinsics.fx;
break;
case Surface.ROTATION_180:
cx = (float) intrinsics.width - cx;
cy = (float) intrinsics.height - cy;
break;
case Surface.ROTATION_270:
cx = (float) intrinsics.height - (float) intrinsics.cy;
cy = (float) intrinsics.cx;
width = (float) intrinsics.height;
height = (float) intrinsics.width;
fx = (float) intrinsics.fy;
fy = (float) intrinsics.fx;
break;
default:
break;
}
// Uses frustumM to create a projection matrix taking into account calibrated camera
// intrinsic parameter.
// Reference: http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/
float near = 0.1f;
float far = 100;
float xScale = near / fx;
float yScale = near / fy;
float xOffset = (cx - (width / 2.0f)) * xScale;
// Color camera's coordinates has y pointing downwards so we negate this term.
float yOffset = -(cy - (height / 2.0f)) * yScale;
float m[] = new float[16];
Matrix.frustumM(m, 0,
xScale * (float) -width / 2.0f - xOffset,
xScale * (float) width / 2.0f - xOffset,
yScale * (float) -height / 2.0f - yOffset,
yScale * (float) height / 2.0f - yOffset,
near, far);
return m;
}
}
VideoRender.java Code
//Extending RajawaliSurface View
/**
* Renderer that implements a basic augmented reality scene using Rajawali.
* It creates a scene with a background quad taking the whole screen, where the color camera is
*/
public class VideoRenderer extends RajawaliRenderer {
private static final String TAG = VideoRenderer.class.getSimpleName();
private float[] textureCoords0 = new float[]{0.0F, 0.0F, 1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 1.0F};
private float[] textureCoords270 = new float[]{0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 1.0F, 1.0F};
private float[] textureCoords180 = new float[]{1.0F, 1.0F, 0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 0.0F};
private float[] textureCoords90 = new float[]{1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 1.0F, 0.0F, 0.0F};
// Rajawali texture used to render the Tango color camera.
private ATexture mTangoCameraTexture;
// Keeps track of whether the scene camera has been configured.
private boolean mSceneCameraConfigured;
private ScreenQuad mBackgroundQuad = new ScreenQuad();
public VideoRenderer(Context context) {
super(context);
}
#Override
protected void initScene() {
// Create a quad covering the whole background and assign a texture to it where the
// Tango color camera contents will be rendered.
Material tangoCameraMaterial = new Material();
tangoCameraMaterial.setColorInfluence(0);
mBackgroundQuad.getGeometry().setTextureCoords(textureCoords0);
// We need to use Rajawali's {#code StreamingTexture} since it sets up the texture
// for GL_TEXTURE_EXTERNAL_OES rendering
mTangoCameraTexture =
new StreamingTexture("camera", (StreamingTexture.ISurfaceListener) null);
try {
tangoCameraMaterial.addTexture(mTangoCameraTexture);
mBackgroundQuad.setMaterial(tangoCameraMaterial);
} catch (ATexture.TextureException e) {
Log.e(TAG, "Exception creating texture for RGB camera contents", e);
}
getCurrentScene().addChildAt(mBackgroundQuad, 0);
// Add a directional light in an arbitrary direction.
DirectionalLight light = new DirectionalLight(1, 0.2, -1);
light.setColor(1, 1, 1);
light.setPower(0.8f);
light.setPosition(3, 2, 4);
getCurrentScene().addLight(light);
}
/**
* Update background texture's UV coordinates when device orientation is changed. i.e change
* between landscape and portrait mode.
*/
public void updateColorCameraTextureUv(int rotation) {
switch (rotation) {
case Surface.ROTATION_90:
mBackgroundQuad.getGeometry().setTextureCoords(textureCoords90);
break;
case Surface.ROTATION_180:
mBackgroundQuad.getGeometry().setTextureCoords(textureCoords180);
break;
case Surface.ROTATION_270:
mBackgroundQuad.getGeometry().setTextureCoords(textureCoords270);
break;
default:
mBackgroundQuad.getGeometry().setTextureCoords(textureCoords0);
break;
}
}
public void updateRenderCameraPose(TangoPoseData cameraPose) {
float[] rotation = cameraPose.getRotationAsFloats();
float[] translation = cameraPose.getTranslationAsFloats();
Quaternion quaternion = new Quaternion(rotation[3], rotation[0], rotation[1], rotation[2]);
getCurrentCamera().setRotation(quaternion.conjugate());
getCurrentCamera().setPosition(translation[0], translation[1], translation[2]);
}
/**
* It returns the ID currently assigned to the texture where the Tango color camera contents
* should be rendered.
public int getTextureId() {
return mTangoCameraTexture == null ? -1 : mTangoCameraTexture.getTextureId();
}
/**
* We need to override this method to mark the camera for re-configuration (set proper
* projection matrix) since it will be reset by Rajawali on surface changes.
*/
#Override
public void onRenderSurfaceSizeChanged(GL10 gl, int width, int height) {
super.onRenderSurfaceSizeChanged(gl, width, height);
mSceneCameraConfigured = false;
}
public boolean isSceneCameraConfigured() {
return mSceneCameraConfigured;
}
/**
* Sets the projection matrix for the scene camera to match the parameters of the color camera
public void setProjectionMatrix(float[] matrixFloats) {
getCurrentCamera().setProjectionMatrix(new Matrix4(matrixFloats));
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep,
int xPixelOffset, int yPixelOffset) {
}
#Override
public void onTouchEvent(MotionEvent event) {
}
}

Related

Control dji mavic enterprise via virtual sticks

I am trying to control drone with command sendVirtualStickFlightControlData.
To move the drone left I use following code.
sendVirtualStickDataTask = new SendVirtualStickDataTask(0, -5, 0, 0);
sendVirtualStickDataTimer = new Timer();
sendVirtualStickDataTimer.schedule(sendVirtualStickDataTask, 100, 200);
private class SendVirtualStickDataTask extends TimerTask {
private float pitch;
private float roll;
private float yaw;
private float throttle;
private long startTime = System.currentTimeMillis();
public SendVirtualStickDataTask(float inputPitch, float inputRoll, float inputYaw, float inputThrottle) {
pitch = inputPitch;
roll = inputRoll;
yaw = inputYaw;
throttle = inputThrottle;
}
#Override
public void run() {
if (System.currentTimeMillis() - startTime > 300) {
mFlightController.sendVirtualStickFlightControlData(new FlightControlData(0, 0, 0, 0),
new CommonCallbacks.CompletionCallback() {
#Override
public void onResult(final DJIError djiError) {
}
});
cancel();
} else {
mFlightController
.sendVirtualStickFlightControlData(new FlightControlData(pitch,
roll,
yaw,
throttle),
new CommonCallbacks.CompletionCallback() {
#Override
public void onResult(final DJIError djiError) {
}
});
}
}
}
However, the drone moves to the left and then drops down sharply.
What is the reason for this drone behavior?
I have no clue why it is dropping sharply downwards, since you are not changing the throttle values. Make sure to enable virtual sticks before usage. It might have to do with you needing to set maximum values. This code works fine for me.
/* controllers virtual sticks command and triggers drone movement */
private void sendVirtualStickCommands(final float pX, final float pY, final float pZ, final float pYaw){
//maximum amounts
float verticalJoyControlMaxSpeed = 2;
float yawJoyControlMaxSpeed = 30;
float pitchJoyControlMaxSpeed = 10;
float rollJoyControlMaxSpeed = 10;
//set yaw, pitch, throttle, roll
float mYaw = (float)(yawJoyControlMaxSpeed * pYaw);
float mThrottle = (float)(verticalJoyControlMaxSpeed * pZ);
float mPitch = (float)(pitchJoyControlMaxSpeed * pX);
float mRoll = (float)(rollJoyControlMaxSpeed * pY);
if (mFlightController != null){
//if virtual sticks are enabled, send the command, otherwise turn them on
if (virtualSticksEnabled){
mFlightController.sendVirtualStickFlightControlData(
new FlightControlData(
mPitch, mRoll, mYaw, mThrottle
), new CommonCallbacks.CompletionCallback() {
#Override
public void onResult(DJIError djiError) {
if (djiError!=null){
setResultToToast(djiError.getDescription());
}
}
}
);
} else {
setResultToToast("flight controller virtual mode off");
//if not enabled, enable
mFlightController.setVirtualStickModeEnabled(true, new CommonCallbacks.CompletionCallback() {
#Override
public void onResult(DJIError djiError) {
if (djiError != null){
setResultToToast(djiError.getDescription());
}else
{
setResultToToast("Enable Virtual Stick Success");
virtualSticksEnabled = true;
sendVirtualStickCommands(pX, pY, pZ, pYaw);
}
}
});
}
} else{
setResultToToast("Flight Controller Null");
}
}
Then, to move in a direction:
sendVirtualStickCommands(0.1f, 0, 0, 0);//move right
sendVirtualStickCommands(-0.1f, 0, 0, 0);//move left
sendVirtualStickCommands(0, 0.1f, 0, 0);//move forward
sendVirtualStickCommands(0, -0.1f, 0, 0);//move backwards
sendVirtualStickCommands(0, 0, 0.1f, 0);//move upwards
sendVirtualStickCommands(0, 0, -0.1f, 0);//move downwards
try this
mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY);
mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY);
mFlightController.setVerticalControlMode(VerticalControlMode.POSITION);
mFlightController.setRollPitchCoordinateSystem(FlightCoordinateSystem.BODY);
and set mThrottle >= 0.0f
in VerticalControlMode.POSITION, mThrottle is a flight height you want to set in meters above the ground.
For example mThrottle = 2.0f means to move the drone to 2 meters above the ground. Set the height you want and the drone will rise to it or "drops down" if mThrottle = 0f.

How to implement Accelerometer into app correctly

I want to control my simple android game (arcanoid) with accelerometer. Paddle in game is now being controlled with touch screen event. I want to control it with accelerometer.
I tried to implement accelerometer into GameActivity class which is "controlling" BreakOutView class like this:
public class GameActivity extends Activity implements SensorEventListener {
// gameView will be the view of the Menu_Layout
// It will also hold the logic of the Menu_Layout
// and respond to screen touches as well
BreakOutView breakoutView;
private SensorManager sManager;
Sensor accelerometer;
Paddle paddle;
float x;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
breakoutView = new BreakOutView(this);
sManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); // zisk managera
if (sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
accelerometer = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
// Initialize gameView and set it as the view
setContentView(breakoutView);
}
// This method executes when the player starts the Game
#Override
protected void onResume() {
super.onResume();
sManager.registerListener(this,accelerometer,SensorManager.SENSOR_DELAY_NORMAL);
// Tell the gameView resume method to execute
breakoutView.resume();
}
// This method executes when the player quits the Menu_Layout
#Override
protected void onPause() {
super.onPause();
sManager.unregisterListener(this);
// Tell the gameView pause method to execute
breakoutView.pause();
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
{
x = sensorEvent.values[0];
if (x > 0) {
breakoutView.paddle.setMovementState(paddle.LEFT);
}
else { breakoutView.paddle.setMovementState(paddle.RIGHT);
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
This is code for BreakOutView class.
onTouchEvent method is now disabled because I want to use accelerometer to control Paddle in application.
public class BreakOutView extends SurfaceView implements Runnable{
// This is our thread
Thread gameThread = null;
// This is new. We need a SurfaceHolder
// When we use Paint and Canvas in a thread
// We will see it in action in the draw method soon.
SurfaceHolder ourHolder;
// A boolean which we will set and unset
// when the Menu_Layout is running- or not.
volatile boolean playing;
// Game is paused at the start
boolean paused = true;
// A Canvas and a Paint object
Canvas canvas;
Paint paint;
// This variable tracks the Menu_Layout frame rate
long fps;
Bitmap bitmapBob;
Bitmap bitmapBall;
Bitmap bitmapPaddal;
Bitmap bitmapBrick1;
Bitmap bitmapBrick2;
Bitmap bitmapBrick3;
// The size of the screen in pixels
int screenX;
int screenY;
// The players paddle
Paddle paddle;
// A ball
Ball ball;
// Up to 200 bricks
Brick[] bricks = new Brick[24];
int numBricks = 0;
// For sound FX
SoundPool soundPool;
int beep1ID = -1;
int beep2ID = -1;
int beep3ID = -1;
int loseLifeID = -1;
int explodeID = -1;
// The score
int score = 0;
int level = 1;
// Lives
int lives = 3;
Rect dest;
DisplayMetrics dm;
int densityDpi;
// When we initialize (call new()) on BreakOutView
// This special constructor method runs
public BreakOutView(Context context) {
super(context);
// The next line of code asks the
// SurfaceView class to set up our object.
// How kind.
// Initialize ourHolder and paint objects
ourHolder = getHolder();
paint = new Paint();
// Get a Display object to access screen details
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
// Load the resolution into a Point object
Point size = new Point();
// TODO target API < 13
display.getSize(size);
screenX = size.x;
screenY = size.y;
// using dpi to set sizes for objects
dm = context.getResources().getDisplayMetrics();
densityDpi = dm.densityDpi;
paddle = new Paddle(screenX, screenY, densityDpi);
// Create a ball
ball = new Ball(screenX, screenY);
// Load the sounds
// This SoundPool is deprecated but don't worry
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
try {
// Create objects of the 2 required classes
AssetManager assetManager = context.getAssets();
AssetFileDescriptor descriptor;
// Load our fx in memory ready for use
descriptor = assetManager.openFd("beep1.wav");
beep1ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("beep2.wav");
beep2ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("beep3.wav");
beep3ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("loseLife.wav");
loseLifeID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("explode.wav");
explodeID = soundPool.load(descriptor, 0);
} catch (IOException e) {
// Print an error message to the console
Log.e("error", "failed to load sound files");
}
// Load Images from resource files
bitmapBob = BitmapFactory.decodeResource(this.getResources(), R.drawable.wall);
bitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
bitmapPaddal = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
bitmapBrick1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_red);
bitmapBrick2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_green);
bitmapBrick3 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_monster);
//Make Sizes Depending on DPI
int heightX = densityDpi / 8;
float length_Paddal = densityDpi / 1.50f;
int height_Paddal = densityDpi / 7;
int brickWidth = screenX / 8;
int brickHeight = screenY / 10;
bitmapBall = getResizedBitmap(bitmapBall, heightX, heightX);
bitmapPaddal = getResizedBitmap(bitmapPaddal, length_Paddal, height_Paddal);
bitmapBrick1 = getResizedBitmap(bitmapBrick1, brickWidth, brickHeight);
bitmapBrick2 = getResizedBitmap(bitmapBrick2, brickWidth, brickHeight);
bitmapBrick3 = getResizedBitmap(bitmapBrick3, brickWidth, brickHeight);
// Create bricks for level 1
createBricksAndRestart(1);
}
public void createBricksAndRestart(int Xlevel) {
// Put the ball back to the start
ball.reset(screenX, screenY);
level = Xlevel;
switch (Xlevel) {
case 2:
// level 2
ball.xVelocity = 600;
ball.yVelocity = -1000;
break;
// level 3
case 3:
ball.xVelocity = 1000;
ball.yVelocity = -1400;
break;
// level 1
default:
ball.xVelocity = 400;
ball.yVelocity = -800;
break;
}
// Brick Size
int brickWidth = screenX / 8;
int brickHeight = screenY / 10;
// Build a wall of bricks
numBricks = 0;
for (int column = 0; column < 8; column++) {
for (int row = 0; row < 3; row++) {
bricks[numBricks] = new Brick(row, column, brickWidth, brickHeight);
numBricks++;
}
}
// if Game is over reset scores ,lives &Level
if (lives == 0) {
score = 0;
lives = 3;
level = 1;
}
}
#Override
public void run() {
while (playing) {
// Capture the current time in milliseconds in startFrameTime
long startFrameTime = System.currentTimeMillis();
// Update the frame
if (!paused) {
update();
}
// Draw the frame
draw();
// Calculate the fps this frame
// We can then use the result to
// time animations and more.
long timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
// Everything that needs to be updated goes in here
// Movement, collision detection etc.
public void update() {
// Move the paddle if required
paddle.update(fps);
ball.update(fps);
// Check for ball colliding with a brick
for (int i = 0; i < numBricks; i++) {
if (bricks[i].getVisibility()) {
if (RectF.intersects(bricks[i].getRect(), ball.getRect())) {
bricks[i].setInvisible();
ball.reverseYVelocity();
score = score + 10;
soundPool.play(explodeID, 1, 1, 0, 0, 1);
}
}
}
// Check for ball colliding with paddle
if (
ball.getRect().intersect(paddle.getRect()) ||
RectF.intersects(paddle.getRect(), ball.getRect()) ||
paddle.getRect().intersect(ball.getRect())
) {
ball.reverseYVelocity();
// ReverseX Direction + IncreaseX speed
if (paddle.getMovementState() == paddle.RIGHT && ball.xVelocity < 0 || paddle.getMovementState() == paddle.LEFT && ball.xVelocity > 0) {
ball.reverseXVelocity();
}
// SameX Direction + IncreaseX speed
else if (paddle.getMovementState() == paddle.RIGHT && ball.xVelocity > 0 || paddle.getMovementState() == paddle.LEFT && ball.xVelocity < 0) {
ball.sameXVelocity();
}
/*// Paddle is still, DecreaseX speed
else if (paddle.getMovementState() == paddle.STOPPED) {
ball.zeroXVelocity();
}*/
// Some intersection Bugs
ball.clearObstacleY(paddle.getRect().top - 20);
soundPool.play(beep1ID, 1, 1, 0, 0, 1);
}
// Bounce the ball back when it hits the bottom of screen
// And Lose a life
if (ball.getRect().bottom > screenY) {
ball.reverseYVelocity();
ball.clearObstacleY(screenY - 5);
// Lose a life
lives--;
soundPool.play(loseLifeID, 1, 1, 0, 0, 1);
if (lives == 0) {
paused = true;
//draw Loss;
canvas = ourHolder.lockCanvas();
paint.setColor(getResources().getColor(R.color.orange));
paint.setTextSize(getResources().getDimension(R.dimen.text_size_big));
canvas.drawText("أنت خسرت!",
screenX / 2 - (densityDpi / 1.90f), screenY / 2 + (densityDpi), paint);
ourHolder.unlockCanvasAndPost(canvas);
try {
// Wait 3 seconds then reset a new game
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Create bricks at level 1
createBricksAndRestart(1);
}
}
// Pause if cleared screen
if (score == numBricks * 10) {
// Create bricks at level 2
createBricksAndRestart(2);
// fix for a pause bug
// so that it won't Pause After finishing the Game
score = score + 10;
// Gift the player with 1 new live
lives = lives + 1;
} else if (score == (numBricks * 20) + 10) {
// Create bricks at level 3
createBricksAndRestart(3);
// fix for a pause bug
// so that it won't Pause After finishing the Game
score = score + 10;
// Gift the player with 2 new lives
lives = lives + 2;
}
// Pause if cleared screen
// if score equals to the whole Bricks scores after 3 levels
else if (score == (numBricks * 10 * 3) + 20) {
paused = true;
}
// Bounce the ball back when it hits the top of screen
if (ball.getRect().top < 0) {
ball.reverseYVelocity();
ball.clearObstacleY(40);
soundPool.play(beep2ID, 1, 1, 0, 0, 1);
}
// If the ball hits left wall bounce
if (ball.getRect().left < 0) {
ball.reverseXVelocity();
ball.clearObstacleX(2);
soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}
// If the ball hits right wall Velocity
if (ball.getRect().right > screenX) {
ball.reverseXVelocity();
ball.clearObstacleX(screenX - 57);
soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}
}
// Draw the newly updated scene
public void draw() {
// Make sure our drawing surface is valid or we crash
if (ourHolder.getSurface().isValid()) {
// Lock the canvas ready to draw
canvas = ourHolder.lockCanvas();
// Draw the background color
// canvas.drawColor(getResources().getColor(R.color.deeppurple));
dest = new Rect(0, 0, getWidth(), getHeight());
// Draw bob as background with dest size
canvas.drawBitmap(bitmapBob, null, dest, paint);
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 255, 255, 255));
// Draw the ball
// canvas.drawCircle(ball.getRect().centerX(), ball.getRect().centerY(), 25, paint);
canvas.drawBitmap(bitmapBall, ball.getRect().left, ball.getRect().top, null);
// Draw the paddle
//canvas.drawRect(paddle.getRect(), paint);
canvas.drawBitmap(bitmapPaddal, paddle.getRect().left, paddle.getRect().top, null);
// Change the brush color for drawing
// paint.setColor(getResources().getColor(R.color.redorange));
// Draw the bricks if visible
for (int i = 0; i < numBricks; i++) {
if (bricks[i].getVisibility()) {
// canvas.drawRect(bricks[i].getRect(), paint);
switch (level) {
case 1:
canvas.drawBitmap(bitmapBrick1, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
case 2:
canvas.drawBitmap(bitmapBrick2, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
case 3:
canvas.drawBitmap(bitmapBrick3, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
}
}
}
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 255, 255, 255));
// Draw the score
paint.setTextSize(getResources().getDimension(R.dimen.text_size));
// Score Text
canvas.drawText(
"النقاط: " + score
, screenX - (densityDpi / 1.50f), screenY / 2, paint);
// Lives Text
canvas.drawText("الصحة: " + lives
, densityDpi / 5, screenY / 2, paint);
// Levels Text
canvas.drawText("المرحلة: " + level
, screenX / 2 - (densityDpi / 5), screenY / 2 + (densityDpi / 5), paint);
// Has the player cleared the screen?
if (score >= (numBricks * 10 * 3) + 20) {
paint.setColor(getResources().getColor(R.color.colorAccent));
paint.setTextSize(getResources().getDimension(R.dimen.text_size_big));
canvas.drawText("أنت كسبت!", screenX / 2 - (densityDpi / 1.90f), screenY / 2 + (densityDpi / 1), paint);
}
// Draw everything to the screen
ourHolder.unlockCanvasAndPost(canvas);
}
}
// If GameActivity is paused/stopped
// shutdown our thread.
public void pause() {
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
Log.e("Error:", "joining thread");
}
}
// If GameActivity is started
// start our thread.
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
// The SurfaceView class implements onTouchListener
// So we can override this method and detect screen touches.
/* #Override
public boolean onTouchEvent(MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
// Player has touched the screen
case MotionEvent.ACTION_DOWN:
if (!(lives == 0)) {
paused = false;
}
// If touch motion > Half of the Screen
if (motionEvent.getX() > screenX / 2) {
// move paddle right
paddle.setMovementState(paddle.RIGHT);
} else {
// move paddle left
paddle.setMovementState(paddle.LEFT);
}
break;
// Player has removed finger from screen
case MotionEvent.ACTION_UP:
// paddle stopped
paddle.setMovementState(paddle.STOPPED);
break;
}
return true;
}*/
// Resize Bitmap function to Handle all the Images from resources the right size
public Bitmap getResizedBitmap(Bitmap bm, float newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = newWidth / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
bm.recycle();
return resizedBitmap;
}
}
And this is code for Paddle class:
import android.graphics.RectF;
public class Paddle {
// Which ways can the paddle move
public final int STOPPED = 0;
public final int LEFT = 1;
public final int RIGHT = 2;
int scrX;
// RectF is an object that holds four coordinates - just what we need
private RectF rect;
// How long and high our paddle will be
private float length;
private float height;
// X is the far left of the rectangle which forms our paddle
private float x;
// Y is the top coordinate
private float y;
// This will hold the pixels per second speedthat the paddle will move
private float paddleSpeed;
// Is the paddle moving and in which direction
private int paddleMoving = STOPPED;
private int MYscreenDPI;
// This the the constructor method
// When we create an object from this class we will pass
// in the screen width and height
public Paddle(int screenX, int screenY, int screenDPI) {
// Dynamic size based on each device DPI
length = screenDPI / 2;
height = screenDPI / 5;
MYscreenDPI = screenDPI;
scrX = screenX;
// Start paddle in roughly the sceen centre
x = screenX / 2;
y = screenY - screenDPI / 4.50f;
rect = new RectF(x, y, x + length, y + height);
// How fast is the paddle in pixels per second
paddleSpeed = 800;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
// This is a getter method to make the rectangle that
// defines our paddle available in BreakoutView class
public RectF getRect() {
return rect;
}
public int getMovementState() {
return paddleMoving;
}
// This method will be used to change/set if the paddle is going left, right or nowhere
public void setMovementState(int state) {
paddleMoving = state;
}
// This update method will be called from update in BreakoutView
// It determines if the paddle needs to move and changes the coordinates
// contained in rect if necessary
public void update(long fps) {
if (paddleMoving == LEFT) {
// to fix Paddle going off the Screen
if (x >= -MYscreenDPI / 10)
// Decrement position
x = x - paddleSpeed / fps;
}
if (paddleMoving == RIGHT) {
// to fix Paddle going off the Screen
if (x <= scrX - length - MYscreenDPI / 14)
// Increment position
x = x + paddleSpeed / fps;
}
// Apply the New position
rect.left = x;
rect.right = x + length;
}
}
When I tried to run it application is always crashing and not working. When I try to use onTouchEvent method to control paddle it is not crashing but accelerometer is not working.
I know that I'm doing something bad. I'm trying to figure it out for like one week but I don't know how to make it working.
If you can tell me any suggestions I will be really thankful. Thank you for all responses.
--EDIT--
Here is error message from logcat when I clicked on new game in emulator:
2019-04-13 17:30:55.116 7082-7082/com.kazaky.breakout E/SensorManager: Exception dispatching input event. 2019-04-13 17:30:55.122 7082-7082/com.kazaky.breakout E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.kazaky.breakout, PID: 7082
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at com.kazaky.breakout.GameActivity.onSensorChanged(GameActivity.java:73)
at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:833)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 2019-04-13 17:31:06.794 7082-7117/com.kazaky.breakout E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19 2019-04-13 17:31:06.794 7082-7117/com.kazaky.breakout E/Surface: queueBuffer (handle=0xe9f16140) failed (No such device)
As suggested by the Stack Trace you are trying to access a null object, which in this case I'm guessing sensroEvent in onSensorChanged.
Add a nullability check and return if sensorEvent is null, like so:
if (sensorEvent == null) return
and write the rest of the function after the Guard Condition.

3D object Scaled while rotating y axis

I am new to OpenGL and ARCore and I am using GoogleARCore Sample as a base to create my application. I am using OpenGL-ES-2.0 version. I able to do Pinch zoom (2 fingers) using android.view.ScaleGestureDetector.SimpleOnScaleGestureListener. By using this library class of Rotation Rotation Gesture I am able to get my rotation degree and it worked well with my 3D Object.
While rotating my 3D object, my object gets scaled too. I want to stop my scaling while the user is doing the rotation. How can I achieve this? Or how can I pass my both scaling and rotation in a different method to update their respective matrix? I do not want to use any 3D party library for this.
Please help me with this. Below is my code and suggest me where I am doing anything wrong.
ScaleGesture
private class CustomScaleGesture extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
DebugHelper.log("detector.getScaleFactor(): " + detector.getScaleFactor() + " scaleFactor = " + scaleFactor);
scaleFactor *= detector.getScaleFactor();
DebugHelper.log("final scaleFactor: " + scaleFactor);
return true;
}
}
RotationGesture
private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
#Override
public boolean onRotate(RotateGestureDetector detector) {
DebugHelper.log("RotateListener called..");
mRotationDegrees -= detector.getRotationDegreesDelta();
DebugHelper.log("RotateListener: " + mRotationDegrees);
return true;
}
}
MainActivity
public class MyARActivity extends BaseActivity<MyActivityArBinding> implements GLSurfaceView.Renderer {
//AR Variables
private int mWidth;
private int mHeight;
private boolean capturePicture = false;
private boolean installRequested;
private boolean moving;
float[] projmtx = new float[16];
float[] viewmtx = new float[16];
private Session session;
private Snackbar messageSnackbar;
private DisplayRotationHelper displayRotationHelper;
private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
private ObjectRenderer virtualObject;
private ObjectRenderer virtualObjectShadow;
private final PlaneRenderer planeRenderer = new PlaneRenderer();
private PointCloudRenderer pointCloud = new PointCloudRenderer();
// Temporary matrix allocated here to reduce number of allocations for each frame.
private float[] anchorMatrix = new float[16];
// Tap handling and UI.
private ArrayBlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
private ArrayList<Anchor> anchors = new ArrayList<>();
//load and manipulate obj
private SQLiteHelper sqlHelper;
private boolean isObjectChanged = false;
private String objectPath;
private List<CharacterModel> characterModelList = new ArrayList<>();
private boolean isFirstTimeLoad = true;
//Gestures
private float mRotationDegrees = 0.f;
private RotateGestureDetector mRotateDetector;
private float scaleFactor = 1.0f;
private ScaleGestureDetector scaleDetector;
private GestureDetector gestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHeaderVisible(false);
doDefaults();
}
private void doDefaults() {
binding.setPresenter(this);
sqlHelper = SQLiteHelper.getInstance(this);
load3DCharacters();
initAR();
}
#SuppressLint("ClickableViewAccessibility")
private void initAR() {
displayRotationHelper = new DisplayRotationHelper(this);
scaleDetector = new ScaleGestureDetector(this, new CustomScaleGesture());
mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener());
// Set up tap listener.
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
if (anchors.size() <= 0) {
onSingleTap(e);
}
return true;
}
#Override
public boolean onDown(MotionEvent e) {
return true;
}
});
binding.surfaceView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
DebugHelper.log("binding.surfaceView.setOnTouchListener called..");
mRotateDetector.onTouchEvent(event);
scaleDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
moving = true;
DebugHelper.log("ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
DebugHelper.log("ACTION_UP");
moving = false;
break;
case MotionEvent.ACTION_MOVE:
DebugHelper.log("ACTION_MOVE");
if (anchors.size() > 0) {
onSecondTouch(event);
}
break;
}
return gestureDetector.onTouchEvent(event);
}
});
// Set up renderer.
binding.surfaceView.setPreserveEGLContextOnPause(true);
binding.surfaceView.setEGLContextClientVersion(2);
binding.surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
binding.surfaceView.setRenderer(this);
binding.surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
installRequested = false;
}
private void onSecondTouch(MotionEvent e) {
Log.e("Second Touch", "Executed");
if (e.getPointerCount() > 1) {
scaleDetector.onTouchEvent(e);
} else {
queuedSingleTaps.offer(e);
}
}
private void onSingleTap(MotionEvent e) {
// Queue tap if there is space. Tap is lost if queue is full.
DebugHelper.log("onSingleTap()");
queuedSingleTaps.offer(e);
}
private void load3DCharacters() {
CharacterModel model = new CharacterModel();
model.setName("Cat");
model.setObjectPath("cat/cat.obj");
model.setScaleFactor(0.25f);
model.setResourceId(R.drawable.cat);
characterModelList.add(model);
model = new CharacterModel();
model.setName("Old Man");
model.setObjectPath("man/muro.obj");
model.setScaleFactor(0.0085f);
model.setResourceId(R.drawable.old_man);
characterModelList.add(model);
model = new CharacterModel();
model.setName("Bloodwing");
model.setObjectPath("bloodwing/bloodwing.obj");
model.setScaleFactor(0.0009f);
model.setResourceId(R.drawable.bat);
characterModelList.add(model);
}
private void loadObject(CharacterModel model) {
try {
this.objectPath = model.getObjectPath();
this.scaleFactor = model.getScaleFactor();
if (virtualObject == null) {
virtualObject = new ObjectRenderer(objectPath);
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 1.0f, 1.0f, 6.0f);
} else {
// Clear screen to notify driver it should not load any pixels from previous frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
isObjectChanged = true;
virtualObject.updateObjectPath(model.getObjectPath());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void onDrawFrame(GL10 gl) {
if (isObjectChanged) {
isObjectChanged = false;
try {
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
} catch (IOException e) {
e.printStackTrace();
}
return;
}
// Clear screen to notify driver it should not load any pixels from previous frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
if (session == null) {
return;
}
// Notify ARCore session that the view size changed so that the perspective matrix and
// the video background can be properly adjusted.
displayRotationHelper.updateSessionIfNeeded(session);
try {
session.setCameraTextureName(backgroundRenderer.getTextureId());
// Obtain the current frame from ARSession. When the configuration is set to
// UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
// camera framerate.
Frame frame = session.update();
Camera camera = frame.getCamera();
// Handle taps. Handling only one tap per frame, as taps are usually low frequency
// compared to frame rate.
MotionEvent tap = queuedSingleTaps.poll();
if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon
Trackable trackable = hit.getTrackable();
// Creates an anchor if a plane or an oriented point was hit.
if ((trackable instanceof Plane && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())) || (trackable instanceof Point && ((Point) trackable).getOrientationMode() == Point.OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
// Hits are sorted by depth. Consider only closest hit on a plane or oriented point.
// Cap the number of objects created. This avoids overloading both the
// rendering system and ARCore.
//if (!isUpdate) {
DebugHelper.log("Anchor size = " + anchors.size());
if (anchors.size() >= 1) {
anchors.get(0).detach();
anchors.remove(0);
}
// Adding an Anchor tells ARCore that it should track this position in
// space. This anchor is created on the Plane to place the 3D model
// in the correct position relative both to the world and to the plane.
if (anchors.size() > 0) {
DebugHelper.log("anchor list has data");
for (Anchor anchor : anchors) {
anchor.detach();
anchors.remove(anchor);
}
}
Anchor anchor = hit.createAnchor();
if (anchor != null)
anchors.add(anchor);
else
DebugHelper.log("anchor is null");
//}
break;
}
}
}
// Draw background.
backgroundRenderer.draw(frame);
// If not tracking, don't draw 3d objects.
if (camera.getTrackingState() == TrackingState.PAUSED) {
return;
}
// Get projection matrix.
camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
// Get camera matrix and draw.
camera.getViewMatrix(viewmtx, 0);
// Compute lighting from average intensity of the image.
final float lightIntensity = frame.getLightEstimate().getPixelIntensity();
// Visualize tracked points.
PointCloud pointCloud = frame.acquirePointCloud();
this.pointCloud.update(pointCloud);
if (!capturePicture)
this.pointCloud.draw(viewmtx, projmtx);
// Application is responsible for releasing the point cloud resources after
// using it.
pointCloud.release();
// Check if we detected at least one plane. If so, hide the loading message.
if (messageSnackbar != null) {
{
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING
&& plane.getTrackingState() == TrackingState.TRACKING) {
hideLoadingMessage();
break;
}
}
}
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING && plane.getTrackingState() == TrackingState.TRACKING) {
hideLoadingMessage();
break;
}
}
}
// Visualize planes.
if (!capturePicture)
planeRenderer.drawPlanes(session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
// Visualize anchors created by touch.
for (Anchor anchor : anchors) {
if (anchor.getTrackingState() != TrackingState.TRACKING) {
continue;
}
// Get the current pose of an Anchor in world space. The Anchor pose is updated
// during calls to session.update() as ARCore refines its estimate of the world.
anchor.getPose().toMatrix(anchorMatrix, 0);
// Update and draw the model and its shadow.
if (virtualObject != null) {
//passing my scaleFector and rotationDegree to update my matrix.
virtualObject.updateModelMatrix(anchorMatrix, scaleFactor, mRotationDegrees);
if (viewmtx != null && projmtx != null) {
virtualObject.draw(viewmtx, projmtx, lightIntensity);
}
}
}
if (capturePicture) {
capturePicture = false;
onSavePicture();
}
} catch (Throwable t) {
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
ObjectRenderer
public void draw(float[] cameraView, float[] cameraPerspective, float lightIntensity) {
try {
ShaderUtil.checkGLError(TAG, "Before draw");
Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0);
Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, MyARActivity.rotationDegrees, 0.0f, 1.0f, 0.0f); //rotation degree pass to matrix
Matrix.multiplyMM(mFinalModelViewProjectionMatrix, 0, mModelViewProjectionMatrix, 0, mRotationMatrix, 0);
GLES20.glUseProgram(mProgram);
// Set the lighting environment properties.
Matrix.multiplyMV(mViewLightDirection, 0, mModelViewMatrix, 0, LIGHT_DIRECTION, 0);
normalizeVec3(mViewLightDirection);
GLES20.glUniform4f(mLightingParametersUniform, mViewLightDirection[0], mViewLightDirection[1], mViewLightDirection[2], lightIntensity);
// Set the object material properties.
GLES20.glUniform4f(mMaterialParametersUniform, mAmbient, mDiffuse, mSpecular, mSpecularPower);
// Set the ModelViewProjection matrix in the shader.
GLES20.glUniformMatrix4fv(mModelViewUniform, 1, false, mModelViewMatrix, 0);
GLES20.glUniformMatrix4fv(mModelViewProjectionUniform, 1, false, mModelViewProjectionMatrix, 0);
GLES20.glUniformMatrix4fv(mModelViewProjectionUniform, 1, false, mFinalModelViewProjectionMatrix, 0);
if (mBlendMode != null) {
GLES20.glDepthMask(false);
GLES20.glEnable(GLES20.GL_BLEND);
switch (mBlendMode) {
case Shadow:
// Multiplicative blending function for Shadow.
GLES20.glBlendFunc(GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA);
break;
case Grid:
// Grid, additive blending function.
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
break;
}
}
if (mObj != null && mObj.getNumMaterialGroups() > 0) {
//Start drawing data from each VAO
for (int i = 0; i < mObj.getNumMaterialGroups(); i++) {
// Attach the object texture.
GLES20.glUniform1i(mTextureUniform, 0);
GLES20.glBindTexture(GL_TEXTURE_2D, mTextures[i]);
GLES30.glBindVertexArray(vectorArrayObjectIds[i]);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mObj.getMaterialGroup(i).getNumFaces() * 3,
GLES20.GL_UNSIGNED_SHORT, 0);
GLES30.glBindVertexArray(0);
//Unbind texture
GLES20.glBindTexture(GL_TEXTURE_2D, 0);
}
}
if (mBlendMode != null) {
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDepthMask(true);
}
ShaderUtil.checkGLError(TAG, "After draw");
} catch (Exception ex) {
ex.printStackTrace();
}
}

Replace 3D object in google arcore

I am new to OpenGL and ARCore and I am using GoogleArCore Sample as a base to create my application. I am able to scale and zoom the 3D object but I am not able to replace the current 3D object with another 3D object. I am already detecting the shadders before loading of a new object.
Basically, I want to reload onSurfaceCreated(GL10 gl, EGLConfig config)
but I do not know how to do it.
Here is my code
public class MyARActivity extends BaseActivity<MyActivityArBinding> implements GLSurfaceView.Renderer {
private static final String TAG = DashboardActivity.class.getSimpleName();
//AR Variables
private int mWidth;
private int mHeight;
private boolean capturePicture = false;
private boolean installRequested;
private boolean moving;
float[] projmtx = new float[16];
float[] viewmtx = new float[16];
private Session session;
private GestureDetector gestureDetector;
private Snackbar messageSnackbar;
private DisplayRotationHelper displayRotationHelper;
private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
private ObjectRenderer virtualObject;// = new ObjectRenderer();
private ObjectRenderer virtualObjectShadow;// = new ObjectRenderer();
private final PlaneRenderer planeRenderer = new PlaneRenderer();
private PointCloudRenderer pointCloud = new PointCloudRenderer();
private ScaleGestureDetector scaleGestureDetector;
private MyScaleGestureDetector_1 myScaleGestureDetector;
// Temporary matrix allocated here to reduce number of allocations for each frame.
private float[] anchorMatrix = new float[16];
// Tap handling and UI.
private ArrayBlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
private ArrayList<Anchor> anchors = new ArrayList<>();
private int[] m3DCharacters = new int[]{R.drawable.cat, R.drawable.old_man, R.drawable.bat};
private SQLiteHelper sqlHelper;
private boolean isUpdate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHeaderVisible(false);
doDefaults();
}
private void doDefaults() {
binding.setPresenter(this);
sqlHelper = SQLiteHelper.getInstance(this);
initAR();
}
#SuppressLint("ClickableViewAccessibility")
private void initAR() {
displayRotationHelper = new DisplayRotationHelper(this);
myScaleGestureDetector = new MyScaleGestureDetector_1();
scaleGestureDetector = new ScaleGestureDetector(this, myScaleGestureDetector);
// Set up tap listener.
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
if (anchors.size() <= 0) {
onSingleTap(e);
}
return true;
}
#Override
public boolean onDown(MotionEvent e) {
return true;
}
});
binding.surfaceView.setOnTouchListener(
new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
moving = true;
DebugHelper.log("ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
DebugHelper.log("ACTION_UP");
moving = false;
DebugHelper.log("SF at ACTION_UP::::", String.valueOf(myScaleGestureDetector.getScaleFactor()));
break;
case MotionEvent.ACTION_MOVE:
if (anchors.size() > 0) {
onSecondTouch(event);
}
break;
}
return gestureDetector.onTouchEvent(event);
}
});
// Set up renderer.
binding.surfaceView.setPreserveEGLContextOnPause(true);
binding.surfaceView.setEGLContextClientVersion(2);
binding.surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
binding.surfaceView.setRenderer(this);
binding.surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
installRequested = false;
}
private void onSavePicture() {
setLoading(true, "Capturing image...");
Log.e("SavePicture Called", "Yes");
int pixelData[] = new int[mWidth * mHeight];
// Read the pixels from the current GL frame.
IntBuffer buf = IntBuffer.wrap(pixelData);
buf.position(0);
GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
// Convert the pixel data from RGBA to what Android wants, ARGB.
int bitmapData[] = new int[pixelData.length];
for (int i = 0; i < mHeight; i++) {
for (int j = 0; j < mWidth; j++) {
int p = pixelData[i * mWidth + j];
int b = (p & 0x00ff0000) >> 16;
int r = (p & 0x000000ff) << 16;
int ga = p & 0xff00ff00;
bitmapData[(mHeight - i - 1) * mWidth + j] = ga | r | b;
}
}
// Create a bitmap.
Bitmap capturedBitmap = Bitmap.createBitmap(bitmapData, mWidth, mHeight, Bitmap.Config.ARGB_8888);
Bitmap waterBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.watermark_camar);
Bitmap waterMarkBitmap = ImageUtil.getInstance().createWaterMaskRightBottom(this, capturedBitmap, waterBitmap, 10, 5);
sqlHelper.saveToInternalStorage(this, waterMarkBitmap);
setLoading(false, "");
}
/**
* To capture the current AR Frame
*/
public void onCaptureClick() {
if (!ExternalPermissionHelper.hasExtStoragePermission(MyARActivity.this)) {
ExternalPermissionHelper.requestExtStoragePermission(MyARActivity.this);
return;
}
capturePicture = true;
}
/**
* To open and close the character selection horizontal list
*/
public void onCharClick() {
createCharacterScrollView();
}
/**
* To close the entire AR view
*/
public void closeScreen() {
finish();
}
private void createCharacterScrollView() {
try {
binding.LinearTopSlider.setVisibility(View.VISIBLE);
LayoutInflater inflater = LayoutInflater.from(this);
binding.charContainer.removeAllViews();
binding.horizontalChar.scrollTo(0, 0);
for (int i = 0; i < m3DCharacters.length; i++) {
View cell = inflater.inflate(R.layout.item_character, null);
ImageView imgGroup = cell.findViewById(R.id.imgChar);
View view = cell.findViewById(R.id.view);
//String name = "cartoon" + (i + 1);
imgGroup.setImageResource(m3DCharacters[i]); //getResourceId(name)
view.setVisibility(i < m3DCharacters.length - 1 ? View.VISIBLE : View.GONE);
binding.charContainer.addView(cell);
cell.setTag(i);
cell.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final int position = (int) view.getTag();
switch (position) {
case 0:
loadObject(0.28f, 3.5f, 0.28f, "cat/cat.obj");
break;
case 1:
loadObject(0.0085f, 5f, 1f, "man/muro.obj");
break;
case 2:
loadObject(0.0009f, 2.75f, 0.25f, "bloodwing/bloodwing.obj");
break;
}
}
});
}
binding.charContainer.setVisibility(View.VISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
private int getResourceId(String drawableName) {
return getResources().getIdentifier(drawableName, "drawable", getPackageName());
}
private void onSecondTouch(MotionEvent e) {
Log.e("Second Touch", "Executed");
if (e.getPointerCount() > 1) {
scaleGestureDetector.onTouchEvent(e);
} else {
queuedSingleTaps.offer(e);
}
}
#Override
protected void onResume() {
super.onResume();
if (session == null) {
Exception exception = null;
String message = null;
try {
switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) {
case INSTALL_REQUESTED:
installRequested = true;
return;
case INSTALLED:
break;
}
// ARCore requires camera permissions to operate. If we did not yet obtain runtime
// permission on Android M and above, now is a good time to ask the user for it.
if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this);
return;
}
session = new Session(/* context= */ this);
} catch (UnavailableArcoreNotInstalledException
| UnavailableUserDeclinedInstallationException e) {
message = "Please install ARCore";
exception = e;
} catch (UnavailableApkTooOldException e) {
message = "Please update ARCore";
exception = e;
} catch (UnavailableSdkTooOldException e) {
message = "Please update this app";
exception = e;
} catch (Exception e) {
message = "This device does not support AR";
exception = e;
}
if (message != null) {
showMsg(message);
Log.e(TAG, "Exception creating session", exception);
return;
}
// Create default config and check if supported.
Config config = new Config(session);
if (!session.isSupported(config)) {
showMsg("This device does not support AR");
}
session.configure(config);
}
showLoadingMessage();
// Note that order matters - see the note in onPause(), the reverse applies here.
session.resume();
binding.surfaceView.onResume();
displayRotationHelper.onResume();
}
#Override
public void onPause() {
super.onPause();
if (session != null) {
// Note that the order matters - GLSurfaceView is paused first so that it does not try
// to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may
// still call session.update() and get a SessionPausedException.
displayRotationHelper.onPause();
binding.surfaceView.onPause();
session.pause();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
switch (requestCode) {
case CameraPermissionHelper.CAMERA_PERMISSION_CODE:
if (!CameraPermissionHelper.hasCameraPermission(this)) {
showMsg("Camera permission is needed to run this application");
if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
// Permission denied with checking "Do not ask again".
CameraPermissionHelper.launchPermissionSettings(this);
}
finish();
}
break;
case ExternalPermissionHelper.EXT_STORAGE_PERMISSION_CODE:
if (!ExternalPermissionHelper.hasExtStoragePermission(this)) {
showMsg("External storage permission is needed to capture the photo");
if (!ExternalPermissionHelper.shouldShowRequestPermissionRationale(this)) {
// Permission denied with checking "Do not ask again".
ExternalPermissionHelper.launchPermissionSettings(this);
}
finish();
}
break;
}
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Standard Android full-screen functionality.
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);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
private void onSingleTap(MotionEvent e) {
// Queue tap if there is space. Tap is lost if queue is full.
DebugHelper.log("onSingleTap()");
queuedSingleTaps.offer(e);
}
private void loadCatObject() {
try {
myScaleGestureDetector.setScaleFactor(0.25f);
myScaleGestureDetector.setMinValue(3.5f);
myScaleGestureDetector.setMaxValue(0.25f);
virtualObject = new ObjectRenderer("cat/cat.obj");
//virtualObject.createOnGlThread(this, "cat/cat.obj", "cat/cat.png");
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 1.0f, 1.0f, 6.0f);
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void loadBloodwingObject() {
try {
myScaleGestureDetector.setScaleFactor(0.0009f);
myScaleGestureDetector.setMinValue(2.75f);
myScaleGestureDetector.setMaxValue(0.25f);
virtualObject = new ObjectRenderer("bloodwing/bloodwing.obj");
virtualObject.createOnGlThread(this);
//virtualObject.createOnGlThread(this, "bloodwing/bloodwing.obj", "bloodwing/bloodwing.jpg");
virtualObject.setMaterialProperties(0.0f, 1.0f, 1.0f, 6.0f);
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void loadMan() {
try {
myScaleGestureDetector.setScaleFactor(0.0085f);
myScaleGestureDetector.setMinValue(5f);
myScaleGestureDetector.setMaxValue(1f);
virtualObject = new ObjectRenderer("man/muro.obj");
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 1.0f, 1.0f, 6.0f);
} catch (Exception ex) {
ex.printStackTrace();
finish();
}
}
private void loadObject(float scaleFactor, float minValue, float maxValue, String objectPath) {
try {
myScaleGestureDetector.setScaleFactor(scaleFactor);
myScaleGestureDetector.setMinValue(minValue);
myScaleGestureDetector.setMaxValue(maxValue);
if (virtualObject == null) {
virtualObject = new ObjectRenderer(objectPath);
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 1.0f, 1.0f, 6.0f);
} else {
isUpdate = true;
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
virtualObject.updateObjectPath(objectPath);
virtualObject.createOnGlThread(this);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
// Create the texture and pass it to ARCore session to be filled during update().
backgroundRenderer.createOnGlThread(/*context=*/ this);
// Prepare the other rendering objects.
loadObject(0.40f, 3.5f, 0.28f, "cat/cat.obj");
try {
planeRenderer.createOnGlThread(/*context=*/ this, "trigrid.png");
} catch (IOException e) {
Log.e(TAG, "Failed to read plane texture");
}
pointCloud.createOnGlThread(/*context=*/ this);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
displayRotationHelper.onSurfaceChanged(width, height);
GLES20.glViewport(0, 0, width, height);
mWidth = width;
mHeight = height;
}
#Override
public void onDrawFrame(GL10 gl) {
// Clear screen to notify driver it should not load any pixels from previous frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
if (session == null) {
return;
}
// Notify ARCore session that the view size changed so that the perspective matrix and
// the video background can be properly adjusted.
displayRotationHelper.updateSessionIfNeeded(session);
try {
session.setCameraTextureName(backgroundRenderer.getTextureId());
// Obtain the current frame from ARSession. When the configuration is set to
// UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
// camera framerate.
Frame frame = session.update();
Camera camera = frame.getCamera();
// Handle taps. Handling only one tap per frame, as taps are usually low frequency
// compared to frame rate.
MotionEvent tap = queuedSingleTaps.poll();
if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon
Trackable trackable = hit.getTrackable();
// Creates an anchor if a plane or an oriented point was hit.
if ((trackable instanceof Plane && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()))
|| (trackable instanceof Point
&& ((Point) trackable).getOrientationMode()
== Point.OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
// Hits are sorted by depth. Consider only closest hit on a plane or oriented point.
// Cap the number of objects created. This avoids overloading both the
// rendering system and ARCore.
//if (!isUpdate) {
DebugHelper.log("Anchor size = " + anchors.size());
if (anchors.size() >= 1) {
anchors.get(0).detach();
anchors.remove(0);
}
// Adding an Anchor tells ARCore that it should track this position in
// space. This anchor is created on the Plane to place the 3D model
// in the correct position relative both to the world and to the plane.
if (isUpdate) {
if (anchors.size() > 0) {
DebugHelper.log("anchor list has data");
for (Anchor anchor : anchors) {
anchor.detach();
anchors.remove(anchor);
}
}
}
Anchor anchor = hit.createAnchor();
if (anchor != null)
anchors.add(anchor);
else
DebugHelper.log("anchor is null");
//}
break;
}
}
}
// Draw background.
backgroundRenderer.draw(frame);
// If not tracking, don't draw 3d objects.
if (camera.getTrackingState() == TrackingState.PAUSED) {
return;
}
// Get projection matrix.
camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
// Get camera matrix and draw.
camera.getViewMatrix(viewmtx, 0);
// Compute lighting from average intensity of the image.
final float lightIntensity = frame.getLightEstimate().getPixelIntensity();
// Visualize tracked points.
PointCloud pointCloud = frame.acquirePointCloud();
this.pointCloud.update(pointCloud);
if (!capturePicture)
this.pointCloud.draw(viewmtx, projmtx);
// Application is responsible for releasing the point cloud resources after
// using it.
pointCloud.release();
// Check if we detected at least one plane. If so, hide the loading message.
if (messageSnackbar != null) {
{
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING
&& plane.getTrackingState() == TrackingState.TRACKING) {
hideLoadingMessage();
break;
}
//xgfgdfgfgd
//binding.setCharClick(true);
}
}
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING && plane.getTrackingState() == TrackingState.TRACKING) {
hideLoadingMessage();
break;
}
//dfgdfgdfgdf
//binding.setCharClick(true);
}
}
// Visualize planes.
if (!capturePicture)
planeRenderer.drawPlanes(session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
// Visualize anchors created by touch.
for (Anchor anchor : anchors) {
if (anchor.getTrackingState() != TrackingState.TRACKING) {
continue;
}
// Get the current pose of an Anchor in world space. The Anchor pose is updated
// during calls to session.update() as ARCore refines its estimate of the world.
anchor.getPose().toMatrix(anchorMatrix, 0);
// Update and draw the model and its shadow.
if (virtualObject != null) {
virtualObject.updateModelMatrix(anchorMatrix, myScaleGestureDetector.getScaleFactor());
if (viewmtx != null && projmtx != null) {
virtualObject.draw(viewmtx, projmtx, lightIntensity);
}
}
if (virtualObjectShadow != null) {
virtualObjectShadow.updateModelMatrix(anchorMatrix, myScaleGestureDetector.getScaleFactor());
if (viewmtx != null && projmtx != null)
virtualObjectShadow.draw(viewmtx, projmtx, lightIntensity);
}
}
if (capturePicture) {
capturePicture = false;
onSavePicture();
}
} catch (Throwable t) {
// Avoid crashing the application due to unhandled exceptions.
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
private void setLoading(boolean isLoading, String message) {
binding.setLoading(isLoading);
binding.setLoadingMessage(message);
}
private void showSnackbarMessage(String message, boolean finishOnDismiss) {
messageSnackbar =
Snackbar.make(
MyARActivity.this.findViewById(android.R.id.content),
message,
Snackbar.LENGTH_INDEFINITE);
messageSnackbar.getView().setBackgroundColor(0xbf323232);
if (finishOnDismiss) {
messageSnackbar.setAction(
"Dismiss",
new View.OnClickListener() {
#Override
public void onClick(View v) {
messageSnackbar.dismiss();
}
});
messageSnackbar.addCallback(
new BaseTransientBottomBar.BaseCallback<Snackbar>() {
#Override
public void onDismissed(Snackbar transientBottomBar, int event) {
super.onDismissed(transientBottomBar, event);
finish();
}
});
}
messageSnackbar.show();
}
private void showLoadingMessage() {
runOnUiThread(
new Runnable() {
#Override
public void run() {
showSnackbarMessage("Searching for surfaces...", false);
}
});
}
private void hideLoadingMessage() {
runOnUiThread(
new Runnable() {
#Override
public void run() {
if (messageSnackbar != null) {
messageSnackbar.dismiss();
}
messageSnackbar = null;
}
});
}
}
In my ObjectRenderer and BackgroundRenderer is reset my code.
Hee is my reset code
BackgroundRenderer.java
public void resetData() {
GLES20.glDeleteShader(vertexShader);
GLES20.glDeleteShader(fragmentShader);
GLES20.glDeleteProgram(quadProgram);
textureId = -1;
}
ObjectRenderer.java
Public void updateObjectPath(String objectPath) {
try {
GLES20.glDeleteShader(vertexShader);
GLES20.glDeleteShader(fragmentShader);
GLES20.glDeleteProgram(mProgram);
reset();
this.OBJ_PATH = objectPath;
}
catch (Exception e) {
e.printStackTrace();
}
}
private void reset() {
this.OBJ_PATH = null;
this.mObj = null;
this.mModelMatrix = null;
this.mModelViewMatrix = null;
this.mModelViewProjectionMatrix = null;
this.mViewLightDirection = null;
this.mTextures = null;
this.vectorArrayObjectIds = null;
this.mMaterialParametersUniform = -1;
this.mModelViewProjectionUniform = -1;
this.mVerticesBaseAddress = -1;
this.mTexCoordsBaseAddress = -1;
this.mNormalAttribute = -1;
this.mNormalsBaseAddress = -1;
this.mIndexBufferId = -1;
this.mVertexBufferId = -1;
this.mBlendMode = null;
this.mProgram = -1;
this.mLightingParametersUniform = -1;
this.mIndexCount = -1;
this.mModelViewUniform = -1;
this.mPositionAttribute = -1;
this.mTexCoordAttribute = -1;
this.vertexShader = -1;
this.fragmentShader = -1;
this.mAmbient = 0.3f;
this.mDiffuse = 1.0f;
this.mSpecular = 1.0f;
this.mSpecularPower = 6.0f;
this.mModelMatrix = new float[16];
this.mModelViewMatrix = new float[16];
this.mModelViewProjectionMatrix = new float[16];
this.mViewLightDirection = new float[4];
}
Can anyone guide me to same?
Thanks in advance.
You need to pass new object name and its texture to onDrawFrame method this is because of its work only on GL thread.
Also, note that you're using GL version 2.
glSurfaceView.setEGLContextClientVersion(2);
#Override
public void onDrawFrame(GL10 gl) {
if (isObjChanged) {
isObjChanged = false;
try {
virtualObject.createOnGlThread(getContext(), objName, textureName);
virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
} catch (IOException e) {
e.printStackTrace();
}
return;
}
// Clear screen to notify driver it should not load any pixels from previous frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
if (session == null) {
return;
}
// Notify ARCore session that the view size changed so that the perspective matrix and
// the video background can be properly adjusted.
displayRotationHelper.updateSessionIfNeeded(session);
try {
session.setCameraTextureName(backgroundRenderer.getTextureId());
// Obtain the current frame from ARSession. When the configuration is set to
// UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
// camera framerate.
Frame frame = session.update();
Camera camera = frame.getCamera();
// Handle taps. Handling only one tap per frame, as taps are usually low frequency
// compared to frame rate.
MotionEvent tap = tapHelper.poll();
if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon
Trackable trackable = hit.getTrackable();
// Creates an anchor if a plane or an oriented point was hit.
if ((trackable instanceof Plane && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()))
|| (trackable instanceof Point
&& ((Point) trackable).getOrientationMode()
== Point.OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
// Hits are sorted by depth. Consider only closest hit on a plane or oriented point.
// Cap the number of objects created. This avoids overloading both the
// rendering system and ARCore.
if (anchors.size() >= 1) {
anchors.get(0).detach();
anchors.remove(0);
}
// Adding an Anchor tells ARCore that it should track this position in
// space. This anchor is created on the Plane to place the 3D model
// in the correct position relative both to the world and to the plane.
anchors.add(hit.createAnchor());
break;
}
}
}
// Draw background.
backgroundRenderer.draw(frame);
// If not tracking, don't draw 3d objects.
if (camera.getTrackingState() == TrackingState.PAUSED) {
return;
}
// Get projection matrix.
float[] projmtx = new float[16];
camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
// Get camera matrix and draw.
float[] viewmtx = new float[16];
camera.getViewMatrix(viewmtx, 0);
// Compute lighting from average intensity of the image.
// The first three components are color scaling factors.
// The last one is the average pixel intensity in gamma space.
final float[] colorCorrectionRgba = new float[4];
frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);
// Visualize tracked points.
PointCloud pointCloud = frame.acquirePointCloud();
pointCloudRenderer.update(pointCloud);
pointCloudRenderer.draw(viewmtx, projmtx);
// Application is responsible for releasing the point cloud resources after
// using it.
pointCloud.release();
// Check if we detected at least one plane. If so, hide the loading message.
if (messageSnackbarHelper.isShowing()) {
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == com.google.ar.core.Plane.Type.HORIZONTAL_UPWARD_FACING
&& plane.getTrackingState() == TrackingState.TRACKING) {
messageSnackbarHelper.hide(getActivity());
break;
}
}
}
// Visualize planes.
planeRenderer.drawPlanes(
session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
// Visualize anchors created by touch.
for (Anchor anchor : anchors) {
if (anchor.getTrackingState() != TrackingState.TRACKING) {
continue;
}
// Get the current pose of an Anchor in world space. The Anchor pose is updated
// during calls to session.update() as ARCore refines its estimate of the world.
anchor.getPose().toMatrix(anchorMatrix, 0);
// Update and draw the model and its shadow.
virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba);
}
} catch (Throwable t) {
// Avoid crashing the application due to unhandled exceptions.
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
In OpenGL ES defines the following 6 error values Code:
1280 GL_INVALID_ENUM
1281 GL_INVALID_VALUE
1282 GL_INVALID_OPERATION
1283 GL_STACK_OVERFLOW
1284 GL_STACK_UNDERFLOW
1285 GL_OUT_OF_MEMORY
You got a GL_INVALID_ENUM error, which means you passed an unsupported enum value to a GL function.

unable to make 2nd circle follow its own path

I am unable to create more circles which follows its own path with drawCircle .
I have used the code below which creates another circle but follows the path along the lines of 1st circle but not independent .How do I move both circles independent of each other?
I have added
c.drawCircle(ballX-100, ballY-100, 50, ballPaintyellow);
How do I make the above circle independent from the 1st circle?. I really appreciate any help.Thanks in Advance.
BouncingBallActivity.java
package com.stuffthathappens.games;
import static android.hardware.SensorManager.DATA_X;
import static android.hardware.SensorManager.DATA_Y;
import static android.hardware.SensorManager.SENSOR_ACCELEROMETER;
import static android.hardware.SensorManager.SENSOR_DELAY_GAME;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
/**
* This activity shows a ball that bounces around. The phone's
* accelerometer acts as gravity on the ball. When the ball hits
* the edge, it bounces back and triggers the phone vibrator.
*/
#SuppressWarnings("deprecation")
public class BouncingBallActivity extends Activity implements Callback, SensorListener {
private static final int BALL_RADIUS =20;
private SurfaceView surface;
private SurfaceHolder holder;
private final BouncingBallModel model = new BouncingBallModel(BALL_RADIUS);
private GameLoop gameLoop;
private Paint backgroundPaint;
private Paint ballPaint;
private SensorManager sensorMgr;
private long lastSensorUpdate = -1;
private Paint ballPaintyellow;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bouncing_ball);
surface = (SurfaceView) findViewById(R.id.bouncing_ball_surface);
holder = surface.getHolder();
surface.getHolder().addCallback(this);
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.WHITE);
ballPaint = new Paint();
ballPaint.setColor(Color.BLUE);
ballPaint.setAntiAlias(true);
ballPaintyellow = new Paint();
ballPaintyellow.setColor(Color.YELLOW);
ballPaintyellow.setAntiAlias(true);
}
#Override
protected void onPause() {
super.onPause();
model.setVibrator(null);
sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
sensorMgr = null;
model.setAccel(0, 0);
}
#Override
protected void onResume() {
super.onResume();
sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
boolean accelSupported = sensorMgr.registerListener(this,
SENSOR_ACCELEROMETER,
SENSOR_DELAY_GAME);
if (!accelSupported) {
// on accelerometer on this device
sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
// TODO show an error
}
// NOTE 1: you cannot get system services before onCreate()
// NOTE 2: AndroidManifest.xml must contain this line:
// <uses-permission android:name="android.permission.VIBRATE"/>
Vibrator vibrator = (Vibrator) getSystemService(Activity.VIBRATOR_SERVICE);
model.setVibrator(vibrator);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
model.setSize(width, height);
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoop = new GameLoop();
gameLoop.start();
}
private void draw() {
// thread safety - the SurfaceView could go away while we are drawing
Canvas c = null;
try {
// NOTE: in the LunarLander they don't have any synchronization here,
// so I guess this is OK. It will return null if the holder is not ready
c = holder.lockCanvas();
// this needs to synchronize on something
if (c != null) {
doDraw(c);
}
} finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
private void doDraw(Canvas c) {
int width = c.getWidth();
int height = c.getHeight();
c.drawRect(0, 0, width, height, backgroundPaint);
float ballX, ballY;
synchronized (model.LOCK) {
ballX = model.ballPixelX;
ballY = model.ballPixelY;
}
c.drawCircle(ballX, ballY, BALL_RADIUS, ballPaint);
c.drawCircle(ballX-100, ballY-100, 50, ballPaintyellow);
}
public void surfaceDestroyed(SurfaceHolder holder) {
try {
model.setSize(0,0);
gameLoop.safeStop();
} finally {
gameLoop = null;
}
}
private class GameLoop extends Thread {
private volatile boolean running = true;
public void run() {
while (running) {
try {
// don't like this hardcoding
TimeUnit.MILLISECONDS.sleep(5);
draw();
model.updatePhysics();
} catch (InterruptedException ie) {
running = false;
}
}
}
public void safeStop() {
running = false;
interrupt();
}
}
public void onAccuracyChanged(int sensor, int accuracy) {
}
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SENSOR_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
// only allow one update every 50ms, otherwise updates
// come way too fast
if (lastSensorUpdate == -1 || (curTime - lastSensorUpdate) > 50) {
lastSensorUpdate = curTime;
model.setAccel(values[DATA_X], values[DATA_Y]);
}
}
}
}
Bouncingballmodel.java
package com.stuffthathappens.games;
import java.util.concurrent.atomic.AtomicReference;
import android.os.Vibrator;
/**
* This data model tracks the width and height of the playing field along
* with the current position of a ball.
*/
public class BouncingBallModel {
// the ball speed is meters / second. When we draw to the screen,
// 1 pixel represents 1 meter. That ends up too slow, so multiply
// by this number. Bigger numbers speeds things up.
private final float pixelsPerMeter = 10;
private final int ballRadius;
// these are public, so make sure you synchronize on LOCK
// when reading these. I made them public since you need to
// get both X and Y in pairs, and this is more efficient than
// getter methods. With two getters, you'd still need to
// synchronize.
public float ballPixelX, ballPixelY;
private int pixelWidth, pixelHeight;
// values are in meters/second
private float velocityX, velocityY;
// typical values range from -10...10, but could be higher or lower if
// the user moves the phone rapidly
private float accelX, accelY;
/**
* When the ball hits an edge, multiply the velocity by the rebound.
* A value of 1.0 means the ball bounces with 100% efficiency. Lower
* numbers simulate balls that don't bounce very much.
*/
private static final float rebound = 0.8f;
// if the ball bounces and the velocity is less than this constant,
// stop bouncing.
private static final float STOP_BOUNCING_VELOCITY = 2f;
private volatile long lastTimeMs = -1;
public final Object LOCK = new Object();
private AtomicReference<Vibrator> vibratorRef =
new AtomicReference<Vibrator>();
public BouncingBallModel(int ballRadius) {
this.ballRadius = ballRadius;
}
public void setAccel(float ax, float ay) {
synchronized (LOCK) {
this.accelX = ax;
this.accelY = ay;
}
}
public void setSize(int width, int height) {
synchronized (LOCK) {
this.pixelWidth = width;
this.pixelHeight = height;
}
}
public int getBallRadius() {
return ballRadius;
}
/**
* Call this to move the ball to a particular location on the screen. This
* resets the velocity to zero, but the acceleration doesn't change so
* the ball should start falling shortly.
*/
public void moveBall(int ballX, int ballY) {
synchronized (LOCK) {
this.ballPixelX = ballX;
this.ballPixelY = ballY;
velocityX = 0;
velocityY = 0;
}
}
public void updatePhysics() {
// copy everything to local vars (hence the 'l' prefix)
float lWidth, lHeight, lBallX, lBallY, lAx, lAy, lVx, lVy;
synchronized (LOCK) {
lWidth = pixelWidth;
lHeight = pixelHeight;
lBallX = ballPixelX;
lBallY = ballPixelY;
lVx = velocityX;
lVy = velocityY;
lAx = accelX;
lAy = -accelY;
}
if (lWidth <= 0 || lHeight <= 0) {
// invalid width and height, nothing to do until the GUI comes up
return;
}
long curTime = System.currentTimeMillis();
if (lastTimeMs < 0) {
lastTimeMs = curTime;
return;
}
long elapsedMs = curTime - lastTimeMs;
lastTimeMs = curTime;
// update the velocity
// (divide by 1000 to convert ms to seconds)
// end result is meters / second
lVx += ((elapsedMs * lAx) / 1000) * pixelsPerMeter;
lVy += ((elapsedMs * lAy) / 1000) * pixelsPerMeter;
// update the position
// (velocity is meters/sec, so divide by 1000 again)
lBallX += ((lVx * elapsedMs) / 1000) * pixelsPerMeter;
lBallY += ((lVy * elapsedMs) / 1000) * pixelsPerMeter;
boolean bouncedX = false;
boolean bouncedY = false;
if (lBallY - ballRadius < 0) {
lBallY = ballRadius;
lVy = -lVy * rebound;
bouncedY = true;
} else if (lBallY + ballRadius > lHeight) {
lBallY = lHeight - ballRadius;
lVy = -lVy * rebound;
bouncedY = true;
}
if (bouncedY && Math.abs(lVy) < STOP_BOUNCING_VELOCITY) {
lVy = 0;
bouncedY = false;
}
if (lBallX - ballRadius < 0) {
lBallX = ballRadius;
lVx = -lVx * rebound;
bouncedX = true;
} else if (lBallX + ballRadius > lWidth) {
lBallX = lWidth - ballRadius;
lVx = -lVx * rebound;
bouncedX = true;
}
if (bouncedX && Math.abs(lVx) < STOP_BOUNCING_VELOCITY) {
lVx = 0;
bouncedX = false;
}
// safely copy local vars back to object fields
synchronized (LOCK) {
ballPixelX = lBallX;
ballPixelY = lBallY;
velocityX = lVx;
velocityY = lVy;
}
if (bouncedX || bouncedY) {
Vibrator v = vibratorRef.get();
if (v != null) {
v.vibrate(20L);
}
}
}
public void setVibrator(Vibrator v) {
vibratorRef.set(v);
}
}
Which view you are using has nothing to do with it ....
At the moment you have only one BouncingBallModel
private final BouncingBallModel model = new BouncingBallModel(BALL_RADIUS);
This is the one you see when you draw something. Now if you want to draw multiple balls, you will need many BouncingBallModel. So either create a BouncingBallModel model2 or make it dynamic using an array.
Then iterate over the array and draw each ball.

Categories

Resources