如何 3d 旋转 arkit 文本 (Swift4)
how to 3d rotate arkit text (Swift4)
我下面的代码获取文本并将 3d 文本放置在参数现实中。问题是我无法旋转文本。我可以在二维方向上移动它。我想移动和旋转文本。我不知道该怎么做。我还没有看到任何关于这个问题的在线教程。目前这是一个 swift 4 问题。
import UIKit
import SceneKit
import ARKit
class TextNode: SCNNode{
var textGeometry: SCNText!
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
super.init()
textGeometry = SCNText(string: text , extrusionDepth: depth)
textGeometry.font = UIFont(name: font, size: textSize)
textGeometry.flatness = 0
textGeometry.firstMaterial?.diffuse.contents = colour
self.geometry = textGeometry
self.scale = SCNVector3(0.01, 0.01 , 0.01)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
extension ViewController: UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let textEntered = textField.text,
let textRange = Range(range, in: textEntered) {
let updatedText = textEntered.replacingCharacters(in: textRange, with: string)
print("User Has Entered \(updatedText)")
DispatchQueue.main.async {
self.textNode.textGeometry.string = updatedText
}
}
return true
}
}
class ViewController: UIViewController {
/////
var name: String?
@IBOutlet var theBar: UITextField!
@IBOutlet var augmentedRealityView: ARSCNView!
//2. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//3. Create Our Session
let augmentedRealitySession = ARSession()
//4. Create A Variable To Store The Current Nodes Rotation Around It's Y-Axis
var currentAngleY: Float = 0.0
var isRotating = false
var currentNode: SCNNode?
/////
var textNode: TextNode!
fileprivate func judo() {
augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = SCNVector3(0, 0, -1.5)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
augmentedRealityView.session.pause()
}
@IBAction func changeTextColour(){
let snapShot = self.augmentedRealityView.snapshot()
UIImageWriteToSavedPhotosAlbum(snapShot, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
print("Error Saving ARKit Scene \(error)")
} else {
print("ARKit Scene Successfully Saved")
}}
@objc func scaleCurrentNode(_ gesture: UIPinchGestureRecognizer) {
if !isRotating, let selectedNode = currentNode{
if gesture.state == .changed {
let pinchScaleX: CGFloat = gesture.scale * CGFloat((selectedNode.scale.x))
let pinchScaleY: CGFloat = gesture.scale * CGFloat((selectedNode.scale.y))
let pinchScaleZ: CGFloat = gesture.scale * CGFloat((selectedNode.scale.z))
selectedNode.scale = SCNVector3Make(Float(pinchScaleX), Float(pinchScaleY), Float(pinchScaleZ))
gesture.scale = 1
}
if gesture.state == .ended {}
}
}
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let selectedNode = currentNode{
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed{
isRotating = true
selectedNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The CurrentNode
if(gesture.state == .ended) {
currentAngleY = selectedNode.eulerAngles.y
isRotating = false
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
augmentedRealityView.automaticallyUpdatesLighting = true
theBar.delegate = self as? UITextFieldDelegate
textNode = TextNode(text: theBar.text!, colour: .white)
augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = SCNVector3(0, 0, -1.5)
augmentedRealityView.automaticallyUpdatesLighting = true
//1. Run The ARSession
augmentedRealityView.session = augmentedRealitySession
augmentedRealitySession.run(configuration, options: [.resetTracking, .removeExistingAnchors])
//2. Add A UIPinchGestureRecognizer So We Can Scale Our TextNode
let scaleGesture = UIPinchGestureRecognizer(target: self, action: #selector(scaleCurrentNode(_:)))
self.view.addGestureRecognizer(scaleGesture)
//3. Add A Tap Gesture Recogizer So We Can Place Our TextNode
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(placeOrAssignNode(_:)))
self.view.addGestureRecognizer(tapGesture)
//4. Add A Rotation Gesture Recogizer So We Can Rotate Our TextNode
let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotateNode(_:)))
self.view.addGestureRecognizer(rotateGesture)
}
@objc func placeOrAssignNode(_ gesture: UITapGestureRecognizer){
//1. Get The Current Location Of The Tap
let currentTouchLocation = gesture.location(in: self.augmentedRealityView)
//2. If We Hit An SCNNode Set It As The Current Node So We Can Interact With It
if let nodeHitTest = self.augmentedRealityView.hitTest(currentTouchLocation, options: nil).first?.node{
currentNode = nodeHitTest
return
}
//3. Do An ARHitTest For Features Points So We Can Place An SCNNode
if let hitTest = self.augmentedRealityView.hitTest(currentTouchLocation, types: .featurePoint).first {
//4. Get The World Transform
let hitTestPosition = hitTest.worldTransform.columns.3
//5. Add The TestNode At The Desired Position
createTextFromPosition(SCNVector3(hitTestPosition.x, hitTestPosition.y, hitTestPosition.z))
return
}
}
func createTextFromPosition(_ position: SCNVector3){
let textNode = SCNNode()
//1. Create The Text Geometry With String & Depth Parameters
let textGeometry = SCNText(string: theBar.text! , extrusionDepth: 1)
//2. Set The Font With Our Set Font & Size
textGeometry.font = UIFont(name: "Helvatica", size: 1)
//3. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0
//4. Set The Colour Of The Text
textGeometry.firstMaterial?.diffuse.contents = UIColor.white
//5. Set The Text's Material
textNode.geometry = textGeometry
//6. Set The Pivot At The Center
let min = textNode.boundingBox.min
let max = textNode.boundingBox.max
textNode.pivot = SCNMatrix4MakeTranslation(
min.x + (max.x - min.x)/2,
min.y + (max.y - min.y)/2,
min.z + (max.z - min.z)/2
)
//7. Scale The Text So We Can Actually See It!
textNode.scale = SCNVector3(0.005, 0.005 , 0.005)
//8. Add It To The Hierachy & Position It
self.augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = position
//9. Set It As The Current Node
currentNode = textNode
}
func changeColour(_ value: Int){
if value == 0{
textNode.textGeometry.firstMaterial?.diffuse.contents = UIColor.lightGray
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let name = UserDefaults.standard.value(forKey: "name") as? String{
theBar.text = name
}
theBar.delegate = self as? UITextFieldDelegate
if name == String(1) {
textNode = TextNode(text: theBar.text!, colour: .red)
augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = SCNVector3(0, 0, -1.5)
}
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
augmentedRealityView.session.run(configuration)
}
}
//////////////////////////////////////
SCNNode 对象有一个旋转 属性 您可以利用。我没看到你在代码中的什么地方设置它。
https://developer.apple.com/documentation/scenekit/scnnode/1408034-rotation
// Sample code: Rotates node to the `heading`
public func nodeRotation(heading: Double) -> SCNVector4 {
return SCNVector4(0, 1, 0, Double.pi - ((heading * (Double.pi/180)) - Double.pi))
}
textNode.rotation = nodeRotation(heading: 45.0)
我下面的代码获取文本并将 3d 文本放置在参数现实中。问题是我无法旋转文本。我可以在二维方向上移动它。我想移动和旋转文本。我不知道该怎么做。我还没有看到任何关于这个问题的在线教程。目前这是一个 swift 4 问题。
import UIKit
import SceneKit
import ARKit
class TextNode: SCNNode{
var textGeometry: SCNText!
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
super.init()
textGeometry = SCNText(string: text , extrusionDepth: depth)
textGeometry.font = UIFont(name: font, size: textSize)
textGeometry.flatness = 0
textGeometry.firstMaterial?.diffuse.contents = colour
self.geometry = textGeometry
self.scale = SCNVector3(0.01, 0.01 , 0.01)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
extension ViewController: UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let textEntered = textField.text,
let textRange = Range(range, in: textEntered) {
let updatedText = textEntered.replacingCharacters(in: textRange, with: string)
print("User Has Entered \(updatedText)")
DispatchQueue.main.async {
self.textNode.textGeometry.string = updatedText
}
}
return true
}
}
class ViewController: UIViewController {
/////
var name: String?
@IBOutlet var theBar: UITextField!
@IBOutlet var augmentedRealityView: ARSCNView!
//2. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//3. Create Our Session
let augmentedRealitySession = ARSession()
//4. Create A Variable To Store The Current Nodes Rotation Around It's Y-Axis
var currentAngleY: Float = 0.0
var isRotating = false
var currentNode: SCNNode?
/////
var textNode: TextNode!
fileprivate func judo() {
augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = SCNVector3(0, 0, -1.5)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
augmentedRealityView.session.pause()
}
@IBAction func changeTextColour(){
let snapShot = self.augmentedRealityView.snapshot()
UIImageWriteToSavedPhotosAlbum(snapShot, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
print("Error Saving ARKit Scene \(error)")
} else {
print("ARKit Scene Successfully Saved")
}}
@objc func scaleCurrentNode(_ gesture: UIPinchGestureRecognizer) {
if !isRotating, let selectedNode = currentNode{
if gesture.state == .changed {
let pinchScaleX: CGFloat = gesture.scale * CGFloat((selectedNode.scale.x))
let pinchScaleY: CGFloat = gesture.scale * CGFloat((selectedNode.scale.y))
let pinchScaleZ: CGFloat = gesture.scale * CGFloat((selectedNode.scale.z))
selectedNode.scale = SCNVector3Make(Float(pinchScaleX), Float(pinchScaleY), Float(pinchScaleZ))
gesture.scale = 1
}
if gesture.state == .ended {}
}
}
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let selectedNode = currentNode{
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed{
isRotating = true
selectedNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The CurrentNode
if(gesture.state == .ended) {
currentAngleY = selectedNode.eulerAngles.y
isRotating = false
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
augmentedRealityView.automaticallyUpdatesLighting = true
theBar.delegate = self as? UITextFieldDelegate
textNode = TextNode(text: theBar.text!, colour: .white)
augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = SCNVector3(0, 0, -1.5)
augmentedRealityView.automaticallyUpdatesLighting = true
//1. Run The ARSession
augmentedRealityView.session = augmentedRealitySession
augmentedRealitySession.run(configuration, options: [.resetTracking, .removeExistingAnchors])
//2. Add A UIPinchGestureRecognizer So We Can Scale Our TextNode
let scaleGesture = UIPinchGestureRecognizer(target: self, action: #selector(scaleCurrentNode(_:)))
self.view.addGestureRecognizer(scaleGesture)
//3. Add A Tap Gesture Recogizer So We Can Place Our TextNode
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(placeOrAssignNode(_:)))
self.view.addGestureRecognizer(tapGesture)
//4. Add A Rotation Gesture Recogizer So We Can Rotate Our TextNode
let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotateNode(_:)))
self.view.addGestureRecognizer(rotateGesture)
}
@objc func placeOrAssignNode(_ gesture: UITapGestureRecognizer){
//1. Get The Current Location Of The Tap
let currentTouchLocation = gesture.location(in: self.augmentedRealityView)
//2. If We Hit An SCNNode Set It As The Current Node So We Can Interact With It
if let nodeHitTest = self.augmentedRealityView.hitTest(currentTouchLocation, options: nil).first?.node{
currentNode = nodeHitTest
return
}
//3. Do An ARHitTest For Features Points So We Can Place An SCNNode
if let hitTest = self.augmentedRealityView.hitTest(currentTouchLocation, types: .featurePoint).first {
//4. Get The World Transform
let hitTestPosition = hitTest.worldTransform.columns.3
//5. Add The TestNode At The Desired Position
createTextFromPosition(SCNVector3(hitTestPosition.x, hitTestPosition.y, hitTestPosition.z))
return
}
}
func createTextFromPosition(_ position: SCNVector3){
let textNode = SCNNode()
//1. Create The Text Geometry With String & Depth Parameters
let textGeometry = SCNText(string: theBar.text! , extrusionDepth: 1)
//2. Set The Font With Our Set Font & Size
textGeometry.font = UIFont(name: "Helvatica", size: 1)
//3. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0
//4. Set The Colour Of The Text
textGeometry.firstMaterial?.diffuse.contents = UIColor.white
//5. Set The Text's Material
textNode.geometry = textGeometry
//6. Set The Pivot At The Center
let min = textNode.boundingBox.min
let max = textNode.boundingBox.max
textNode.pivot = SCNMatrix4MakeTranslation(
min.x + (max.x - min.x)/2,
min.y + (max.y - min.y)/2,
min.z + (max.z - min.z)/2
)
//7. Scale The Text So We Can Actually See It!
textNode.scale = SCNVector3(0.005, 0.005 , 0.005)
//8. Add It To The Hierachy & Position It
self.augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = position
//9. Set It As The Current Node
currentNode = textNode
}
func changeColour(_ value: Int){
if value == 0{
textNode.textGeometry.firstMaterial?.diffuse.contents = UIColor.lightGray
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let name = UserDefaults.standard.value(forKey: "name") as? String{
theBar.text = name
}
theBar.delegate = self as? UITextFieldDelegate
if name == String(1) {
textNode = TextNode(text: theBar.text!, colour: .red)
augmentedRealityView.scene.rootNode.addChildNode(textNode)
textNode.position = SCNVector3(0, 0, -1.5)
}
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
augmentedRealityView.session.run(configuration)
}
}
//////////////////////////////////////
SCNNode 对象有一个旋转 属性 您可以利用。我没看到你在代码中的什么地方设置它。
https://developer.apple.com/documentation/scenekit/scnnode/1408034-rotation
// Sample code: Rotates node to the `heading`
public func nodeRotation(heading: Double) -> SCNVector4 {
return SCNVector4(0, 1, 0, Double.pi - ((heading * (Double.pi/180)) - Double.pi))
}
textNode.rotation = nodeRotation(heading: 45.0)