如何在Scenekit和UIView中实现重置按钮?

问题描述 投票:0回答:0

向知道所有这些东西是如何工作的人寻求帮助。我找不到任何具体的方法来在线完成这个看似常见的任务,所以我将其发布。这是内幕:

首先是 SwiftUI ModelViewer。这是一个创建 CustomSceneView 以显示 3D 模型的简单视图。有一个重置按钮可以将模型和/或相机重置到其原始位置。你知道,就像任何时候你弄乱了 3D 东西并想回到开始,因为发生了一些奇怪的事情。似乎是一项足够简单的任务,但我一直在努力。用户可以做所有经典的运动,包括放大/缩小和将模型旋转到任何方向/方位。

struct ModelViewer: View {
    @State var scene: SCNScene?
    @State private var camera: Camera?

    var body: some View {
        VStack {
            CustomSceneView(
                scene: self.$scene,
                camera: self.$camera
            )

            Button("Reset") {
                self.camera = .default
            }
            .padding(.horizontal, 20)
            .padding(.vertical, 10)
            .background(Color.gray.opacity(0.25))
            .cornerRadius(12)
            .foregroundColor(.prismBlack)
        }
    }
}

现在这是实际处理所有这些功能的代码:

import SwiftUI
import SceneKit

struct Camera {
    static let `default` = Camera(
        position: SCNVector3(x: 0.805120, y: 0.057330, z: 1.4),
        rotation: SCNVector4(0, 0, 0, 0),
        orientation: SCNVector4(x: 0, y: 0, z: 0, w: 1.0)
    )

    let position: SCNVector3
    let rotation: SCNVector4
    let orientation: SCNVector4

    init(position: SCNVector3, rotation: SCNVector4, orientation: SCNVector4) {
        self.position = position
        self.rotation = rotation
        self.orientation = orientation
    }

    init(from node: SCNNode) {
        self.init(position: node.position, rotation: node.rotation, orientation: node.orientation)
    }
}

struct CustomSceneView: UIViewRepresentable {
    typealias Context = UIViewRepresentableContext<Self>
    typealias UIViewType = SCNView

    @Binding var scene: SCNScene?
    @Binding var camera: Camera?

    func makeUIView(context: Context) -> SCNView {
        return context.coordinator.view
    }

    func updateUIView(_ uiView: UIViewType, context: Context) {
        context.coordinator.set(camera: self.camera)
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, SCNSceneRendererDelegate {
        private let parent: CustomSceneView
        private let camera = SCNCamera()
        private let cameraNode = SCNNode()
        private let contentNode = SCNNode()
        private let scene = SCNScene()

        let view = SCNView()

        init(_ parent: CustomSceneView) {
            self.parent = parent
            super.init()

            self.view.delegate = self
            self.view.pointOfView = self.cameraNode
            self.view.allowsCameraControl = true
            self.view.autoenablesDefaultLighting = true
            self.view.scene = self.scene

            self.camera.name = "Camera"
            self.camera.focalLength = 50
            self.camera.usesOrthographicProjection = true

            self.cameraNode.name = "CameraNode"
            self.cameraNode.camera = self.camera
            self.cameraNode.position = parent.camera?.position ?? Camera.default.position
            self.scene.rootNode.addChildNode(self.cameraNode)

            self.contentNode.name = "ContentNode"
            self.scene.rootNode.addChildNode(self.contentNode)

            guard let scene = parent.scene else { return }
            self.addChildNodes(from: scene)
        }

        private func addChildNodes(from scene: SCNScene) {
            scene.rootNode.childNodes.forEach({ node in
                node.name = "Model"
                node.scale = SCNVector3(x: 1.0, y: 1.0, z: 1.0)
                node.position = SCNVector3(x: 0.8, y: -0.75, z: -0.25)
                node.rotation = SCNVector4(x: 0, y: 0, z: 1, w: .pi / 2)
                self.contentNode.addChildNode(node)
            })
        }

        func set(camera: Camera?) {
            guard let camera else { return }
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 1
            self.view.pointOfView?.position = camera.position
            self.view.pointOfView?.rotation = camera.rotation
            self.view.pointOfView?.orientation = camera.orientation
            SCNTransaction.commit()
        }

        func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) {
            self.parent.camera = nil
        }

        func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {

        }

        func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {

        }
    }
}

值得注意的是,我正在非常精细地完成这一切,因为我必须使用的模型非常多变。虽然不能改变模型,所以相机和节点定位是我唯一的选择。现在,这里的代码大部分都可以工作,您可以单击按钮,模型通常会重置。但是,如果单击按钮时模型处于运动状态,则会触发重置,但最终位置将处于某种随机状态。此外,如果用户放大或缩小,则模型将重置定位,但不会重置缩放。因此,如果任何人有一些关于如何使模型真正重置而不管缩放、移动等的提示或技巧,他们将不胜感激!!

编辑:通过将

self.view.pointOfView?.camera?.fieldOfView = camera.fieldOfView
添加到
set
方法(其中fieldOfView在默认相机中设置了默认值),我可以正确重置缩放。但是,如果模型在按下重置按钮时处于运动状态,它仍然无法正确重置。

swiftui uikit scenekit 3d-modelling
© www.soinside.com 2019 - 2024. All rights reserved.