2

我正在尝试将此功能https://github.com/heinrisch/faceid-to-stl集成到我的反应本机应用程序中。repo 是用 swift 编写的,我基本上想将 ViewController 类显示到我的 react native 应用程序中,但我不知道该怎么做。想先尝试一下,然后最终我想集成 StandardCyborg SDK https://standardcyborg.com/而不是为 React Native 应用程序进行 3D 面部扫描。

我已经尝试了多篇关于桥接本机模块和功能的帖子,并且我已经能够做到这一点。但我认为 ViewControllers 有点不同,因为它们不是 UI 组件?还是他们?对桥接本机模块非常陌生,不熟悉 swift。任何帮助深表感谢。

视图控制器.m

#import <Foundation/Foundation.h>

#import <React/RCTBridgeModule.h>
#import <React/RCTViewManager.h>
#import <React/RCTEventEmitter.h>

@interface RCT_EXTERN_MODULE(Mask, NSObject)
RCT_EXTERN_METHOD(init)
RCT_EXTERN_METHOD(init?)
RCT_EXTERN_METHOD(update)
@end

@interface RCT_EXTERN_MODULE(ViewController, NSObject)
RCT_EXTERN_METHOD(viewDidLoad)
RCT_EXTERN_METHOD(renderer)
RCT_EXTERN_METHOD(renderer)
RCT_EXTERN_METHOD(startSession)
RCT_EXTERN_METHOD(capture)
RCT_EXTERN_METHOD(presentShareSheet)
RCT_EXTERN_METHOD(createSTL)
RCT_EXTERN_METHOD(generateURL)
@end

氧桥接Header.h


#import <React/RCTBridgeModule.h>


AppDelegate.m

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"oxygen"
                                            initialProperties:nil];

  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

@end

AppDelegate.h


#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>

@property (nonatomic, strong) UIWindow *window;

@end

ViewController.swift

@objc(ViewController)
class ViewController: UIViewController, ARSCNViewDelegate {

  @objc @IBOutlet var sceneView: ARSCNView!
  @objc private var isPresentingShareSheet = false
  @objc private var mask: Mask?
  @objc private var lastFaceAnchor: ARFaceAnchor?

  @objc
  static func requiresMainQueueSetup() -> Bool {
    return true
  }

  @objc
  override func viewDidLoad() {
    print("ViewController loaded...")
    super.viewDidLoad()

    sceneView.delegate = self
    startSession()

    let button = UIButton(type: .custom)
    button.setTitle("Capture", for: .normal)
    button.tintColor = .black
    button.titleLabel?.textColor = .black
    button.backgroundColor = .lightGray
    button.addTarget(self, action: #selector(capture), for: .touchUpInside)
    button.layer.cornerRadius = 12
    view.addSubview(button)

    button.translatesAutoresizingMaskIntoConstraints = false
    let constraints: [NSLayoutConstraint] = [
      button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40),
      button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
      button.heightAnchor.constraint(equalToConstant: 55)
    ]
    view.addConstraints(constraints)
  }

  @objc
  func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    //        let device = sceneView.device!
    //        let maskGeometry = ARSCNFaceGeometry(device: device)!
    //        mask = Mask(geometry: maskGeometry)
    let device = MTLCreateSystemDefaultDevice()
    let maskGeometry = ARSCNFaceGeometry(device: device!)!
    mask = Mask(geometry: maskGeometry)
    node.addChildNode(mask!)

  }

  @objc
  func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard let faceAnchor = anchor as? ARFaceAnchor else { return }
    mask?.update(withFaceAnchor: faceAnchor)

    if let faceAnchor = anchor as? ARFaceAnchor {
      lastFaceAnchor = faceAnchor
    }
  }

  @objc
  private func startSession() {
    sceneView.scene.rootNode.childNodes.forEach {
      $0.removeFromParentNode()
    }

    let configuration = ARFaceTrackingConfiguration()
    configuration.isLightEstimationEnabled = true
    sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
  }

  @objc
  private func capture() {
    guard let faceAnchor = lastFaceAnchor else {
      return
    }

    let data = createSTL(from: faceAnchor)
    let url = generateURL(for: data)
    presentShareSheet(with: url)
  }

  @objc
  private func createSTL(from faceAnchor: ARFaceAnchor) -> Data {

    let mapped = faceAnchor.geometry.triangleIndices.map { i in
      return faceAnchor.geometry.vertices[Int(i)]
    }

    var out: [String] = ["solid face"]
    mapped.enumerated().forEach { i, vertex in
      if i % 3 == 0 {
        out.append("facet normal 0 0 0")
        out.append("\touter loop")
      }

      out.append("\t\tvertex \(vertex.x) \(vertex.y) \(vertex.z)")

      if i % 3 == 2 {
        out.append("\tendloop")
      }
    }

    out.append("endsolid face")

    let file = out.joined(separator: "\n")
    let data = file.data(using: .ascii)!
    return data
  }

  @objc
  private func generateURL(for data: Data) -> URL {
    let url = URL(fileURLWithPath: NSTemporaryDirectory() + "face.stl")
    try! data.write(to: url)
    return url
  }

  @objc
  private func presentShareSheet(with url: URL) {
    self.isPresentingShareSheet = true
    DispatchQueue.main.async {
      let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
      activityViewController.completionWithItemsHandler = { _, _, _, _ in
        self.isPresentingShareSheet = false
      }
      self.present(activityViewController, animated: true, completion: nil)
    }
  }
}
4

0 回答 0