我正在尝试制作一个演示应用程序,我想在其中添加一些选项,例如复制/粘贴所选节点,删除该节点等...
我已经完成删除节点部分的操作。现在,我尝试在缩放和调整节点大小后复制/复制粘贴添加一个节点,但是在平面中复制已编辑的节点没有成功。重复节点将与添加或编辑(缩放或调整大小)节点相同。
谁能告诉我如何在应用程序中复制/粘贴该节点?有没有简单的方法可以做到这一点?
我的主要活动代码如下:
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialog;
import android.support.v7.widget.AppCompatEditText;
import android.support.v7.widget.AppCompatImageView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.Toast;
import com.ar.furniture.R;
import com.ar.furniture.adapter.FurnitureAdapter;
import com.ar.furniture.model.FurnitureModel;
import com.ar.furniture.utils.Logger;
import com.ar.furniture.utils.PhotoUtils;
import com.ar.furniture.utils.PointerDrawable;
import com.ar.furniture.utils.Utils;
import com.google.ar.core.Frame;
import com.google.ar.core.HitResult;
import com.google.ar.core.Plane;
import com.google.ar.core.Trackable;
import com.google.ar.core.TrackingState;
import com.google.ar.sceneform.FrameTime;
import com.google.ar.sceneform.Scene;
import com.google.ar.sceneform.ux.ArFragment;
import java.util.ArrayList;
import java.util.List;
public class FurnitureActivity extends BaseActivity implements
View.OnLongClickListener, // View Long Click Listener
View.OnClickListener, // View Click Listener
DialogInterface.OnDismissListener, // Dialog Dismiss Listener
TextWatcher, // Edit Text TextWatcher
FurnitureAdapter.OnItemClickListener, // Furniture Item Click Listener
Scene.OnUpdateListener { // ARScene Update Listener
//AR Fragment
private ArFragment fragment;
private ImageView ivAddAR, ivClear, ivScreenShot;
//For BottomSheet dialog
private BottomSheetDialog mBottomSheetDialog;
private BottomSheetBehavior behavior;
//Adapter For RecyclerView
private FurnitureAdapter adapter;
//For Pointer Drawable
private PointerDrawable pointer = new PointerDrawable();
//For indicating if ARCore is in tracking state.
private boolean isTracking;
//For indicating the user is looking at a plane.
private boolean isHitting;
//For Avoiding double click on view click
private long mLastClickTime;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_furniture);
init();
}
//region View Click Events
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ivAddAR:
//region To avoid double click evnets
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) {
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
//endregion
//Open Bottom Sheet Dialog
openARBottomSheetDialog();
break;
case R.id.ivClear:
clearNodes(fragment);
//removePreviousAnchors();
break;
case R.id.ivScreenShot:
PhotoUtils.takePhoto(fragment);
break;
default:
break;
}
}
//endregion
//region View Long Click Listener
@Override
public boolean onLongClick(View v) {
String message = "";
switch (v.getId()) {
case R.id.ivAddAR:
message = "Add 3D Image";
break;
case R.id.ivScreenShot:
message = "Take snap shot of view";
break;
case R.id.ivClear:
message = "Clear view";
break;
default:
break;
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
return true;
}
//endregion
//region Furniture Adapter
@Override
public void onClick(View view, FurnitureModel furnitureModel) {
switch (view.getId()) {
case R.id.tvFurnitureName:
case R.id.llFurniture:
case R.id.ivFurniture:
//region To avoid double Click events
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) {
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
//endregion
//Dismiss Bottom Sheet Dialog
if (mBottomSheetDialog != null) {
mBottomSheetDialog.dismiss();
}
//Add 3D Object in ARSceneView
addObject(Uri.parse(/*"furniture_models/" + */furnitureModel.getFurnitureModel() + ".sfb"));
break;
default:
break;
}
}
//endregion
//region Bottom Sheet Dialog Dismiss Event
@Override
public void onDismiss(DialogInterface dialog) {
//Dismiss Bottom Sheet Dialog
dialog.dismiss();
//On Dismiss of bottom sheet dialog null initialize bottom sheet dialog
mBottomSheetDialog = null;
//Hide Keyboard
Utils.hideKeyboard(this);
}
//endregion
//region TextWatcher
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//Filter List
if (adapter != null) {
adapter.getFilter().filter(s.toString());
}
}
@Override
public void afterTextChanged(Editable s) {
}
//endregion
//region ARScene View Update Listener
@Override
public void onUpdate(FrameTime frameTime) {
/***
* To update ARCore View scene view
* First, update the tracking state. If ARCore is not tracking, remove the pointer until tracking is restored.
* Next, if ARCore is tracking, check for the gaze of the user hitting a plane detected by ARCore and enable the pointer accordingly.
*/
boolean trackingChanged = updateTracking();
View contentView = findViewById(android.R.id.content);
if (trackingChanged) {
if (isTracking) {
contentView.getOverlay().add(pointer);
} else {
contentView.getOverlay().remove(pointer);
}
contentView.invalidate();
}
if (isTracking) {
boolean hitTestChanged = updateHitTest();
if (hitTestChanged) {
pointer.setEnabled(isHitting);
contentView.invalidate();
}
}
}
//endregion
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
/***
* Initialize Views and other things for first time
*/
private void init() {
//Initialize ARFragment With Views Id
fragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.sceneFormFragment);
//Initialize Listener to the ARSceneView scene which will get called before processing every frame.
//In this listener we can make ARCore API calls and update the status of pointer.
if (fragment != null) {
//Add Update Listener
fragment.getArSceneView().getScene().addOnUpdateListener(this);
//To change plane color
/*fragment
.getArSceneView()
.getPlaneRenderer()
.getMaterial()
.thenAccept(material -> material.setFloat3(PlaneRenderer.MATERIAL_COLOR, new Color(0.0f, 0.0f, 1.0f, 1.0f)));*/
// Build texture sampler
/*Texture.Sampler sampler = Texture.Sampler.builder()
.setMinFilter(Texture.Sampler.MinFilter.LINEAR)
.setMagFilter(Texture.Sampler.MagFilter.LINEAR)
.setWrapMode(Texture.Sampler.WrapMode.REPEAT)
.build();
// Build texture with sampler
CompletableFuture<Texture> trigrid = Texture.builder()
.setSource(this, R.drawable.image)
.setSampler(sampler)
.build();
// Set plane texture
this.fragment.getArSceneView()
.getPlaneRenderer()
.getMaterial()
.thenAcceptBoth(trigrid, (material, texture) -> {
material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture);
});*/
}
//region Initialize View
ivAddAR = findViewById(R.id.ivAddAR);
ivClear = findViewById(R.id.ivClear);
ivScreenShot = findViewById(R.id.ivScreenShot);
//endregion
//region Set View Click Listener
ivAddAR.setOnClickListener(this);
ivClear.setOnClickListener(this);
ivScreenShot.setOnClickListener(this);
//endregion
//region Set View Long Click Listener
ivAddAR.setOnLongClickListener(this);
ivClear.setOnLongClickListener(this);
ivScreenShot.setOnLongClickListener(this);
//endregion
//List of furniture model to show in list
List<FurnitureModel> alFurnitureModel = new ArrayList<>();
alFurnitureModel.add(new FurnitureModel("Table 1", "table_model_1", R.drawable.table_1));
alFurnitureModel.add(new FurnitureModel("Table 2", "table_model_2", R.drawable.table_2));
alFurnitureModel.add(new FurnitureModel("Table 3", "table_model_3", R.drawable.table_3));
//Initialize Adapter With Furniture list and its item click event
adapter = new FurnitureAdapter(alFurnitureModel);
adapter.setOnItemClickListener(this);
//Initialize Bottom sheet view and its behavior
View bottomSheet = findViewById(R.id.bottom_sheet);
behavior = BottomSheetBehavior.from(bottomSheet);
//Initialize Bottom sheet callback event
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_DRAGGING:
Logger.e("BottomSheetCallback: BottomSheetBehavior.STATE_DRAGGING");
break;
case BottomSheetBehavior.STATE_SETTLING:
Logger.e("BottomSheetCallback: BottomSheetBehavior.STATE_SETTLING");
break;
case BottomSheetBehavior.STATE_EXPANDED:
Logger.e("BottomSheetCallback: BottomSheetBehavior.STATE_EXPANDED");
break;
case BottomSheetBehavior.STATE_COLLAPSED:
Logger.e("BottomSheetCallback: BottomSheetBehavior.STATE_COLLAPSED");
break;
case BottomSheetBehavior.STATE_HIDDEN:
Logger.e("BottomSheetCallback: BottomSheetBehavior.STATE_HIDDEN");
break;
default:
break;
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
Logger.e("BottomSheetCallback: slideOffset: " + slideOffset);
}
});
}
/***
* To open BottomSheet dialog for selecting AR image
*/
private void openARBottomSheetDialog() {
/*if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}*/
if (behavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
} else {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
//Initialize Bottom Sheet Dialog
mBottomSheetDialog = new BottomSheetDialog(this);
mBottomSheetDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
//Initialize View of bottom sheet dialog with its child views
View view = getLayoutInflater().inflate(R.layout.bottom_sheet_furniture, null);
AppCompatImageView ivClose = view.findViewById(R.id.ivClose);
AppCompatEditText edtSearch = view.findViewById(R.id.edtSearch);
RecyclerView rvFurniture = view.findViewById(R.id.rvFurniture);
//Set Recycler view layout manager and adapter
rvFurniture.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
rvFurniture.setAdapter(adapter);
//Initialize Edit text changed listener
edtSearch.addTextChangedListener(this);
//Initialize Close Button Click Event
ivClose.setOnClickListener(v -> {
//Hide Keyboard
Utils.hideKeyboard(FurnitureActivity.this);
//Dismiss Bottom Sheet Dialog
if (mBottomSheetDialog != null) {
mBottomSheetDialog.dismiss();
}
});
//Set Bottom Sheet Content View
mBottomSheetDialog.setContentView(view);
//Show Bottom Sheet Dialog
mBottomSheetDialog.show();
//Set Bottom Sheet Dismiss Listener Event
mBottomSheetDialog.setOnDismissListener(this);
}
/***
* Uses ARCore's camera state and returns.
* @return - true if the tracking state has changed since last call
*/
private boolean updateTracking() {
//Get ARFrame from ARSceneView
Frame frame = fragment.getArSceneView().getArFrame();
boolean wasTracking = isTracking;
isTracking = frame.getCamera().getTrackingState() == TrackingState.TRACKING;
return isTracking != wasTracking;
}
/***
* Uses ARCore to call Frame.hitTest()
* @return - As soon as any hit is detected, the method returns
*/
private boolean updateHitTest() {
Frame frame = fragment.getArSceneView().getArFrame();
android.graphics.Point pt = getScreenCenter();
List<HitResult> hits;
boolean wasHitting = isHitting;
isHitting = false;
if (frame != null) {
hits = frame.hitTest(pt.x, pt.y);
for (HitResult hit : hits) {
Trackable trackable = hit.getTrackable();
if ((trackable instanceof Plane
&& ((Plane) trackable).isPoseInPolygon(hit.getHitPose()))) {
isHitting = true;
break;
}
}
}
/*Vector3 position = gesture.getPosition();
List<HitResult> hitResultList = frame.hitTest(position.x, position.y);
for (int i = 0; i < hitResultList.size(); ++i) {
HitResult hit = (HitResult) hitResultList.get(i);
Trackable trackable = hit.getTrackable();
Pose pose = hit.getHitPose();
Node parent = this.getTransformableNode().getParent();
this.desiredLocalPosition = new Vector3(pose.tx(),parent.getLocalPosition().y, pose.tz());
if (parent != null && this.desiredLocalPosition != null) {
this.desiredLocalPosition = parent.worldToLocalPoint(this.desiredLocalPosition);
}
this.lastArHitResult = hit;
break;
}*/
return wasHitting != isHitting;
}
/***
* To add 3d object on click from gallery layout button
* @param model - 3d model uri
*/
private void addObject(Uri model) {
//Get ARFrame from ARSceneView
Frame frame = fragment.getArSceneView().getArFrame();
//Get center point of screen
android.graphics.Point pt = getScreenCenter();
List<HitResult> hits;
if (frame != null) {
hits = frame.hitTest(pt.x, pt.y);
for (HitResult hit : hits) {
Trackable trackable = hit.getTrackable();
if ((trackable instanceof Plane
&& ((Plane) trackable).isPoseInPolygon(hit.getHitPose()))) {
placeObject(fragment, hit.createAnchor(), model);
break;
}
}
}
}
}
在基本活动中添加了放置对象的代码,如下所示:
/***
* Take the ARCore anchor from the hitTest result and builds the Sceneform nodes.
* It starts asynchronous loading of the 3D model using the ModelRenderable builder.
* Note: Here we are using small models, but larger models could take substantially longer to load.
* @param fragment - ArFragment
* @param anchor - anchor points
* @param model - 3d model uri
*/
public void placeObject(ArFragment fragment, Anchor anchor, Uri model) {
//Code For Run static object from app
ModelRenderable
.builder()
.setSource(fragment.getContext(), model)
.build()
.thenAccept(renderable -> addNodeToScene(fragment, anchor, renderable))
.exceptionally((throwable -> {
AlertDialog.Builder builder = new AlertDialog
.Builder(this)
.setMessage(throwable.getMessage())
.setTitle(getString(R.string.error));
AlertDialog dialog = builder.create();
dialog.show();
return null;
}));
}
/***
* To add two nodes and attaches them to the ArSceneView's scene object.
* 1. AnchorNode: Anchor nodes are positioned based on the pose of an ARCore Anchor.
* As a result, they stay positioned in the sample place relative to the real world.
* 2. TransformableNode: We could use the base class type, Node for the placing the objects,
* but Node does not have the interaction functionality to handle moving, scaling
* and rotating based on user gestures.
* @param fragment - ARFragment
* @param anchor - anchor
* @param renderable - renderable
*/
private void addNodeToScene(ArFragment fragment, Anchor anchor, Renderable renderable) {
//Build Anchor Node using anchor points
AnchorNode anchorNode = new AnchorNode(anchor);
//Build Transformable Node using ArFragment
TransformableNode node = new TransformableNode(fragment.getTransformationSystem());
//Set renderable to transformable node
node.setRenderable(renderable);
// Set the min and max scales of the ScaleController.
// Default min is 0.75, default max is 1.75.
node.getScaleController().setMinScale(0.4f);
node.getScaleController().setMaxScale(2.0f);
// Set the local scale of the node BEFORE setting its parent
node.setLocalScale(new Vector3(0.55f, 0.55f, 0.55f));
//Set parent to anchor node
node.setParent(anchorNode);
//Add anchor node to ArView
fragment.getArSceneView().getScene().addChild(anchorNode);
//Set transformable node to be selectable
node.select();
//Double tap on node.
GestureDetector nodeGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
//For removing added node
node.getParent().removeChild(node);
return true;
//return super.onDoubleTap(e);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
showPopupWindow(fragment, anchorNode, renderable, node);
return true;
//return super.onSingleTapUp(e);
}
});
//For Setting double click on node
node.setOnTouchListener((hitTestResult, motionEvent) -> {
nodeGestureDetector.onTouchEvent(motionEvent);
return true;
//return false;
});
}
答案 0 :(得分:0)
创建了anchor和anchorNode之后,必须设置其父级:
Anchor anchor = hitResult.createAnchor();
AnchorNode anchorNode = new AnchorNode(anchor);
anchorNode.setParent(arFragment.getArSceneView().getScene());
TransformableNode transformableNode = new TransformableNode(arFragment.getTransformationSystem());
transformableNode.setParent(anchorNode);
transformableNode.setRenderable(heightRenderable);
transformableNode.select();
ScaleController scaleController = transformableNode.getScaleController();
scaleController.setMaxScale(10f);
scaleController.setMinScale(0.01f);
transformableNode.setOnTapListener(this);
arFragment.getArSceneView().getScene().addOnUpdateListener(this);
lastAnchorNode = anchorNode;
查看我的github链接:https://github.com/shubh261096/measurementapp