CariusLars / ar_flutter_plugin

Flutter Plugin for AR (Augmented Reality) - Supports ARKit on iOS and ARCore on Android devices
MIT License
317 stars 228 forks source link

Detecting walls and ceilings as planes #215

Open KakTamTebya opened 5 months ago

KakTamTebya commented 5 months ago

Hi, I'm new to flutter and doing university project now. I have a glb models that can be placed lets say on the wall and I want to be able to move them only along the wall after I placed it. I was trying to implement this behaviour, but my code works fine only on floor objects. It detects the floor and applies plane texture to it, and then I can tap and all is great. I need the same behaviour on wall and ceiling objects. But then I try do the same pointing the camera at the wall, it doesn't detect the wall and showing animated guide for long time. At some time animated guide disappears, but plane texture doesn't appear at all and there is no reaction to taps. Hope someone can help me. Thanks to everyone who read this)

UPD: I've tried again just now and it detected the bookcase that stands along the wall, but still not detecting the empty wall and ceiling. I've also tried to change planeDetectionConfig to PlaneDetectionConfig.vertical. Im trying on Android device, if it's important info.

Here is the code:

import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart';
import 'package:vector_math/vector_math_64.dart';
import 'package:ar_furniture/ui/reused_widgets/text_elevated_button.dart';
import 'package:ar_furniture/generated/l10n.dart';

import 'package:ar_flutter_plugin/ar_flutter_plugin.dart';
import 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_session_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_object_manager.dart';
import 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';
import 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';
import 'package:ar_flutter_plugin/datatypes/node_types.dart';
import 'package:ar_flutter_plugin/models/ar_hittest_result.dart';
import 'package:ar_flutter_plugin/models/ar_anchor.dart';
import 'package:ar_flutter_plugin/models/ar_node.dart';

@RoutePage()
class ArPage extends StatefulWidget {

  final String glbUrl;

  const ArPage({
    super.key,
    required this.glbUrl,
  });

  @override
  State<ArPage> createState() => _ArPageState();
}

class _ArPageState extends State<ArPage> {
  late ARSessionManager arSessionManager;
  late ARObjectManager arObjectManager;
  late ARAnchorManager arAnchorManager;
  bool _addModelMutex = false;
  ARNode? _node;
  ARAnchor? _anchor;

  @override
  void dispose() {
    super.dispose();
    arSessionManager.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      body: Stack(
        children: [
          ARView(
            onARViewCreated: onARViewCreated,
            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,
            permissionPromptDescription: S.of(context).permissionPromptDescription,
            permissionPromptButtonText: S.of(context).permissionPromptButtonText,
            permissionPromptParentalRestriction: S.of(context).permissionPromptParentalRestriction,
          ),
          Align(
            alignment: Alignment.topLeft,
            child: Padding(
              padding: const EdgeInsets.only(top: 24).copyWith(left: 4),
              child: IconButton(
                icon: Icon(Icons.arrow_back_sharp, color: theme.primaryColor),
                onPressed: () => context.router.pop(),
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: TextElevatedButton(
              function: onRemove,
              text: S.of(context).removeModel,
            ),
          ),
        ],
      )
    );
  }

  void onARViewCreated(
      ARSessionManager sessionManager,
      ARObjectManager objectManager,
      ARAnchorManager anchorManager,
      ARLocationManager locationManager) {
    arSessionManager = sessionManager;
    arObjectManager = objectManager;
    arAnchorManager = anchorManager;
    arSessionManager.onInitialize(
      showFeaturePoints: false,
      showWorldOrigin: false,
      handlePans: true,
      handleRotation: true,
    );
    arObjectManager.onInitialize();

    arSessionManager.onPlaneOrPointTap = onPlaneOrPointTapped;
  }

  Future<void> onRemove() async {
    if (_anchor != null) {
      arAnchorManager.removeAnchor(_anchor!);
    }
    _anchor = null;
  }

  Future<void> onPlaneOrPointTapped(
      List<ARHitTestResult> hitTestResults) async {
    if (_node != null && _anchor != null){
      return;
    }
    var singleHitTestResult = hitTestResults.firstWhereOrNull(
      (hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);
    if (singleHitTestResult == null || _addModelMutex) {
      return;
    }
    _addModelMutex = true;
    var newAnchor =
      ARPlaneAnchor(transformation: singleHitTestResult.worldTransform);
    bool didAddAnchor = await arAnchorManager.addAnchor(newAnchor) ?? false;
    if (!didAddAnchor) {
      arSessionManager.onError(S.current.arError);
    }
    _anchor = newAnchor;
    var newNode = ARNode(
      type: NodeType.webGLB,
      uri: widget.glbUrl,
      position: Vector3(0.0, 0.0, 0.0),
    );
    bool didAddNodeToAnchor = await arObjectManager.addNode(
      newNode, planeAnchor: newAnchor) ?? false;
    if (!didAddNodeToAnchor){
      arSessionManager.onError(S.current.arError);
      _anchor = null;
      arAnchorManager.removeAnchor(newAnchor);
    }
    _node = newNode;
    _addModelMutex = false;
  }
}