swift - 缩放 SCNNode 及其子节点

标签 swift arkit

我正在制作一个简单的测量应用程序。目前,我将球体作为 SCNNodes 放置在该位置周围,并且节点之间会出现一个标签,显示从节点 1 到节点 2 的直线长度。

这就是标签的创建方式:

func addLabel() {
    let plane = SCNPlane(width: 0.07, height: 0.02)
    plane.cornerRadius = plane.height / 10

    let sks = SKScene(size: CGSize(width: plane.width * 10e3, height: plane.height * 10e3))
    sks.backgroundColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 0.7)

    currentLbl = SKLabelNode(text: "")
    currentLbl.fontSize = 110
    currentLbl.fontName = "Helvetica"
    currentLbl.verticalAlignmentMode = .center
    currentLbl.position = CGPoint(x: sks.frame.midX, y: sks.frame.midY)
    currentLbl.fontColor = .white

    sks.addChild(currentLbl)

    let material = SCNMaterial()
    material.isDoubleSided = true
    material.diffuse.contents = sks
    material.diffuse.contentsTransform = SCNMatrix4Translate(SCNMatrix4MakeScale(1, -1, 1), 0, 1, 0)

    plane.materials = [material]
    let node = SCNNode(geometry: plane)
    node.constraints = [SCNBillboardConstraint()]
    node.position = SCNVector3Make(0, 0, 0)
    let (minBound, maxBound) = node.boundingBox
    node.pivot = SCNMatrix4MakeTranslation( (maxBound.x + minBound.x)/2, minBound.y, 0.02/2)

    lblNodes.append(node)
    currentLblNode = node
    sceneView.scene.rootNode.addChildNode(node)
}

我想将数学方程应用于这些标签节点的比例(在我的更新函数中),以保持几米的可读性。

var myNodes: [SCNNode] = []
let s = getMagicScalingNumber()

假设我按照上面的方法获得了比例因子,并且有一个 SCNNode 数组,如何缩放所有节点及其各自的子节点,以便它们在视觉上保持比例。

如果 SCNTransformConstraint() 是一个选项,我希望有一个如何实现它的示例。


编辑:只是为了澄清,我已经尝试过

currentLblNode.scale = SCNVector3Make(s, s, s)

这似乎不起作用。

最佳答案

我知道这已经很晚了,但是我已经整理了一个示例,应该可以为您指明正确的方向。

在您的示例中,您实际上是在创建一个持有者 SCNNode,其中包含您的标签等。

您可以将它们存储到 [SCNNode] 数组中,然后像这样转换它们的比例:

/// Updates The Contents Of Each Node Added To Our NodesAdded Array
///
/// - Parameters:
///   - nodes: [SCNNode]
///   - pointOfView: SCNNode
func updateScaleFromCameraForNodes(_ nodes: [SCNNode], fromPointOfView pointOfView: SCNNode){

    nodes.forEach { (node) in

        //1. Get The Current Position Of The Node
        let positionOfNode = SCNVector3ToGLKVector3(node.worldPosition)

        //2. Get The Current Position Of The Camera
        let positionOfCamera = SCNVector3ToGLKVector3(pointOfView.worldPosition)

        //3. Calculate The Distance From The Node To The Camera
        let distanceBetweenNodeAndCamera = GLKVector3Distance(positionOfNode, positionOfCamera)

        //4. Animate Their Scaling & Set Their Scale Based On Their Distance From The Camera
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.5
        switch distanceBetweenNodeAndCamera {
        case 0 ... 0.5:
            node.simdScale = simd_float3(0.25, 0.25, 0.25)
        case 0.5 ... 1:
            node.simdScale = simd_float3(0.5, 0.5, 0.5)
        case 1 ... 1.5:
            node.simdScale = simd_float3(1, 1, 1)
        case 1.5 ... 2:
            node.simdScale = simd_float3(1.5, 1.5, 1.5)
        case 2 ... 2.5:
            node.simdScale = simd_float3(2, 2, 2)
        case 2.5 ... 3:
            node.simdScale = simd_float3(2.5, 2.5, 2.5)
        default:
            print("Default")
        }
        SCNTransaction.commit()
    }

}

这里我只是设置“随机”值来说明根据与相机的距离而定的比例概念。

为了更好地结合上下文,我整理了一个小演示:

//--------------------------
// MARK: - ARSCNViewDelegate
//--------------------------

extension ViewController: ARSCNViewDelegate{

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

        if !nodesAdded.isEmpty, let currentCameraPosition = self.sceneView.pointOfView {
            updateScaleFromCameraForNodes(nodesAdded, fromPointOfView: currentCameraPosition)
        }

    }
}

class ViewController: UIViewController {

     @IBOutlet var sceneView: ARSCNView!

     var nodesAdded = [SCNNode]()

    //-----------------------
    // MARK: - View LifeCycle
    //-----------------------

    override func viewDidLoad() {
        super.viewDidLoad()

        //1. Generate Our Three Box Nodes
        generateBoxNodes()

        //2. Run The Session
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
        sceneView.delegate = self

    }

    /// Generates Three SCNNodes With An SCNBox Geometry & Places Them In A Holder Node
    func generateBoxNodes(){

        //1. Create An SCNNode To Hold All Of Our Content
        let holderNode = SCNNode()

        //2. Create An Array Of Colours For Each Face
        let colours: [UIColor] = [.red, .green, .blue, .purple, .cyan, .black]

        //3. Create An SCNNode Wih An SCNBox Geometry
        let boxNode = SCNNode()
        let boxGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.01)
        boxNode.geometry = boxGeometry

        //4. Create A Different Material For Each Face
        var materials = [SCNMaterial]()

        for i in 0..<5{
            let faceMaterial = SCNMaterial()
            faceMaterial.diffuse.contents = colours[i]
            materials.append(faceMaterial)
        }

        //5. Set The Geometries Materials
        boxNode.geometry?.materials = materials

        //6. Create Two More Nodes By Cloning The First One
        let secondBox = boxNode.flattenedClone()
        let thirdBox = boxNode.flattenedClone()

        //7. Position Them In A Line & Add To The Scene
        boxNode.position = SCNVector3(-0.2, 0, 0)
        secondBox.position = SCNVector3(0, 0, 0)
        thirdBox.position = SCNVector3(0.2, 0, 0)

        holderNode.addChildNode(boxNode)
        holderNode.addChildNode(secondBox)
        holderNode.addChildNode(thirdBox)
        holderNode.position = SCNVector3(0, 0, -1)
        self.sceneView.scene.rootNode.addChildNode(holderNode)

        nodesAdded.append(holderNode)
    }


    /// Updates The Contents Of Each Node Added To Our NodesAdded Array
    ///
    /// - Parameters:
    ///   - nodes: [SCNNode]
    ///   - pointOfView: SCNNode
    func updateScaleFromCameraForNodes(_ nodes: [SCNNode], fromPointOfView pointOfView: SCNNode){

        nodes.forEach { (node) in

            //1. Get The Current Position Of The Node
            let positionOfNode = SCNVector3ToGLKVector3(node.worldPosition)

            //2. Get The Current Position Of The Camera
            let positionOfCamera = SCNVector3ToGLKVector3(pointOfView.worldPosition)

            //3. Calculate The Distance From The Node To The Camera
            let distanceBetweenNodeAndCamera = GLKVector3Distance(positionOfNode, positionOfCamera)

            //4. Animate Their Scaling & Set Their Scale Based On Their Distance From The Camera
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 0.5
            switch distanceBetweenNodeAndCamera {
            case 0 ... 0.5:
                node.simdScale = simd_float3(0.25, 0.25, 0.25)
            case 0.5 ... 1:
                node.simdScale = simd_float3(0.5, 0.5, 0.5)
            case 1 ... 1.5:
                node.simdScale = simd_float3(1, 1, 1)
            case 1.5 ... 2:
                node.simdScale = simd_float3(1.5, 1.5, 1.5)
            case 2 ... 2.5:
                node.simdScale = simd_float3(2, 2, 2)
            case 2.5 ... 3:
                node.simdScale = simd_float3(2.5, 2.5, 2.5)
            default:
                print("Default")
            }
            SCNTransaction.commit()
        }

    }


    override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) }

    override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) }

    override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() }

}

希望对你有帮助...

关于swift - 缩放 SCNNode 及其子节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50999792/

相关文章:

swift - 如何展开UIView并在展开时 move 其他组件

ios - 圆角半径仅适用于 View 的左上角和左下角

xcode - 我可以将USDZ转换为诸如STL的实体网格吗

ios - ARKit 对象随机摆动

swift - 在 Swift 应用程序中从 Excel 文档中读取数据

swift - 如何更改游戏场景中文本标签的文本

swift - CVPixelBuffer – 如何以每秒 60 帧的速度捕获每第三帧?

ios - 在 ARKIT 中为 3D 对象添加阴影

computer-vision - 带有 ARkit 和 CoreML 的视觉框架

ios - 如何在 ResearchKit 中配置具有多个 ORKAudioSteps 的 ORKOrderedTask?