向知道所有这些东西是如何工作的人寻求帮助。我找不到任何具体的方法来在线完成这个看似常见的任务,所以我将其发布。这是内幕:
首先是 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在默认相机中设置了默认值),我可以正确重置缩放。但是,如果模型在按下重置按钮时处于运动状态,它仍然无法正确重置。