首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >将模型添加到场景中 - 在您的环境中显示3D内容

将模型添加到场景中 - 在您的环境中显示3D内容

作者头像
iOSDevLog
发布2019-06-17 14:43:36
5.4K0
发布2019-06-17 14:43:36
举报
文章被收录于专栏:iOSDevLogiOSDevLog

在最后几节中,我们能够检测到一个平面并显示一个焦点方块,以帮助我们为模型指定一个位置。我们也熟悉了热门测试和世界变换。现在,我们拥有显示虚拟对象所需的所有工具。在本教程中,我们将学习如何检索模型并使用按钮的触发器将其呈现在场景中。一旦显示,我们将隐藏焦点方块。

下载

要学习本教程,您需要Xcode 9或更高版本,以及Focus Square的最终Xcode项目。您可以下载本节的最终Xcode项目,以帮助您与自己的进度进行比较。

基本视图

Main.Storyboard中,我们已经提到ARSCNView默认放在视图控制器的顶部。但是,如果没有UIView作为基础,则仅限于您可以在用户界面上执行的操作。为了能够正确添加我们的按钮,我们必须删除当前的 ARSCNView并首先从对象库添加UIView作为底层。接下来,选择相同的ARKit SceneKit View并将其放回UIView之上。调整大小以填充整个视图控制器。

约束

然后,单击Storyboard编辑器左下角的第四个图标,将新约束添加到场景视图中。定义约束以确保您的用户界面适应不同的屏幕尺寸或设备方向。设置为0顶部底部。确保它们都被约束到视图而不是安全区域,然后单击Add Constraints安全区域是凹口下方和主页指示器上方的边距,通常是屏幕的可见部分。此外,请确保未选中“ 限制到边距”

如果被限制在安全区域而不是超级视图,这就是看起来的样子,显然,这看起来并不好看。

横屏约束安全区

重新 Outlet

请记住,一个IBOutletsceneView链接到ARSCNView?因为我们删除了旧的ARSCNView,所以它打破了这个Outlet。我们需要重新考虑新的。为此,请打开“ 助理”编辑器,该图标看起来像两个交织在一起的圆圈。现在,我们并排放置两个分屏,非常适合连接。在右侧,我们有ViewController.swift,在那里我们可以找到该出口的声明。单击并拖动左侧的圆圈,它应该是第15行,然后释放到ARSCNView上。现在,关闭助理编辑。

添加按钮

我们想在视图中添加一个按钮,用作在场景中添加模型的触发器。从对象库中,将UIButton拖动到场景视图的顶部。在“ 属性”检查器中,删除“ 按钮”标题并将图像设置为“ 按钮/添加”

约束到底部20但这次是在安全区域,并取消选中Constrain到边距。然后,将鼠标悬停在左侧的“ 对齐”图标上,并在“容器”中选中“水平”以在屏幕中水平居中。

添加按钮功能

我们刚刚在屏幕上添加了按钮,但它根本没有做任何事情。当我们触摸它时,让按钮执行某些操作。现在,打开Assistant编辑器并控制将故事板中的按钮拖到ViewController类。代码中的顺序并不重要,因为我们稍后会移动此函数。原因是我们不能在扩展类中执行此操作。将Connection更改为Action,将其命名为addObjectButtonTapped。保持原样。完成后,关闭“ 助理”编辑器

@IBAction func addObjectButtonTapped(_ sender: Any) {
    print("Add button tapped")
}

让我们运行应用程序来查看我们的新按钮。但在此之前,评论一些印刷品陈述是明智的。转到updateFocusSquare()并注释掉这些代码行。

// print("Focus square hits a plane")

// print("Focus square does not hit a plane")

对象添加文件

让我们创建另一个swift文件,以便在场景中添加模型。右键单击视图控制器+ ARSCNViewDelegate.swift并选择新建文件...。然后,选择Swift File,单击Next。称之为ViewController + ObjectAddition,然后是Create

导入套件(Kits)

与往常一样,用以下框架替换Foundation。然后,向ViewController添加扩展。

import UIKit
import SceneKit
import ARKit

extension ViewController {}

检索模型

在扩展内部,创建一个新函数来检索我们选择的模型是一个很好的主动。此函数仅在此文件中使用,因此我们将采用fileprivate。将有一个String类型的参数,它将有两个名称。在函数外部使用的那个被命名,而在函数内使用的是名称。它将返回一个可选的SCNNode

fileprivate func getModel(named name: String) -> SCNNode? {}

与飞船场景类似,我们将使用我们指定的名称调用场景。然后,检索该场景SketchUp的父节点。我们递归设置为false以返回具有该名称的直接子节点。如果为true,它将解析所有节点,直到找到它为止。我们知道SketchUp是场景中唯一的节点,所以在我们的情况下,真实的不准确。之后,我们将变量名称分配给模型的名称。最后,此函数将在调用时返回模型。

let scene = SCNScene(named: "art.scnassets/\(name)/\(name).scn")!
guard let model = scene.rootNode.childNode(withName: "SketchUp", recursively: false) else {return nil}
model.name = name
return model

可选:PIVOT POINT FIX

如果您需要将模型的轴心点修改为所有3轴的中心,那么您可以在此处执行此操作。您可以使用以下公式。别客气。

let min = model.boundingBox.min
let max = model.boundingBox.max

model.pivot = SCNMatrix4MakeTranslation(
    min.x + (max.x - min.x) / 2,
    min.y + (max.y - min.y) / 2,
    min.z + (max.z - min.z) / 2)

显示模型

我们刚刚完成了这个功能,现在,我们准备在点击按钮时在场景中显示我们的模型。让我们转到ViewController.swift并剪切动作函数addObjectButtonTapped并将其粘贴到这里以将其全部放在一个地方。

我们首先确保焦点方块首先存在,因为它只在检测到表面时才出现在屏幕上。

guard focusSquare != nil else {return}

我们选择展示的模型是iPhoneX。因此,我们将使用getModel函数检索该模型。如果由于某种原因它失败了,我们将打印一条消息给我们。然后,让我们用一个小消息将它添加到场景中。

let modelName = "iPhoneX"
guard let model = getModel(named: modelName) else {
    print("Unable to load \(modelName) from file")
    return
}

sceneView.scene.rootNode.addChildNode(model)
print("\(modelName) added successfully")

运行应用程序。您将意识到该设备不仅站起来而且漂浮在空中。当然,我们已经在场景中添加了我们的模型,我们还没有把它放在表面上。所以,让我们这样做。

命中测试

显然,我们将再次使用命中测试,方法与之前相同。

let hitTest = sceneView.hitTest(screenCenter, types: .existingPlaneUsingExtent)
guard let worldTransformColumn3 = hitTest.first?.worldTransform.columns.3 else {return}
model.position = SCNVector3(worldTransformColumn3.x, worldTransformColumn3.y, worldTransformColumn3.z)

翻转设备

要将电话平放在桌子上,请打开iPhoneX.scn。在“ 节点”检查器中,将x Euler Angle重置为0

让我们再试一次。现在,我们的设备看起来更像是在房间里。

缩放模型

如果您选择了其他型号,您可能已经注意到尺寸不合适。因此,我们将扩展它们中的每一个。我们在iPhoneX的场景编辑器中完成了它。现在,我们在这里撤消它并代之以编码。让我们为所有边界将比例放回到1

回到ViewController + ObjectAddition并在getModel函数中,我们首先为比例声明一个变量,然后根据模型设置不同的值。在我们的情况下,使用[switch]控制流来匹配我们设置的许多条件是完美的。switch语句必须是详尽的,这就是为什么有一个默认情况来涵盖所有其他方案。

var scale: CGFloat

switch name {
    case "iPhoneX":         scale = 0.025
    case "iPhone6s":        scale = 0.025
    case "iPhone7":         scale = 0.0001
    case "iPhone8":         scale = 0.000008
    case "iPhone8Plus":     scale = 0.000008
    case "iPad4":           scale = 0.0006
    case "MacBookPro13":    scale = 0.0029
    case "iMacPro":         scale = 0.0245
    case "AppleWatch":      scale = 0.0000038
    default:                scale = 1
}

在返回之前将模型缩放到我们之前分配的值。

model.scale = SCNVector3(scale, scale, scale)

场景模型

知道我们在场景中有多少模型会很高兴。在ViewController.swift中,将一个新的类变量声明为一个节点数组,我们将其初始化为空。

var modelsInTheScene: Array<SCNNode> = []

返回ViewController + ObjectAddition.swift,并在addObjectButtonTapped操作方法的末尾您添加的每个模型追加到数组modelsInTheScene中。然后,打印该数组的计数

modelsInTheScene.append(model)
print("Currently have \(modelsInTheScene.count) model(s) in the scene")

我们如何运行应用程序并坚果?

焦点方块隐藏/显示选项

当我们在屏幕上显示模型时,我们仍然看到焦点方块干扰了我们漂亮的模型。如果我们在安置后隐藏它,你怎么说?

FocusSquare类中,让我们创建一个函数来为焦点方块的表示设置动画。将隐藏和显示两种情况,因此隐藏值是布尔值。然后我们声明一个SCNAction用于淡入淡出,淡出用于隐藏和淡入显示。这些行动将运行根据是否隐藏是真还是假,一前一后。为此目的使用序列

func setHidden(to hidden: Bool) {
    var fadeTo: SCNAction
        
    if hidden {
        fadeTo = .fadeOut(duration: 0.5)
    } else {
        fadeTo = .fadeIn(duration: 0.5)
    }
        
    let actions = [fadeTo, .run({ (focusSquare: SCNNode) in
        focusSquare.isHidden = hidden
    })]
    runAction(.sequence(actions))
}

视图观点

下一步将有点棘手。如果我们看到模型,我们希望隐藏焦点方块,对吧?但是,如果我们在屏幕上看不到任何内容呢?我们再次需要它来选择下一个位置。我们在屏幕上看到的是不断变化的,所以我们需要在updateFocusSquare()中实现它。在那里,让我们将pointOfView设置为场景视图的视角。

guard let pointOfView = sceneView.pointOfView else {return}

然后,让我们将firstVisibleModel的定义作为场景中的第一个模型。我们正在使用第一个返回满足条件的第一个元素的方法。如果节点从视角可见,它将返回true或false 。

let firstVisibleModel = modelsInTheScene.first { (node) -> Bool in
    return sceneView.isNode(node, insideFrustumOf: pointOfView)
}

隐藏/显示焦点方块

现在,如果第一个模型是可见的而不是零,则模型将在视图中可见。请记住,如果显示模型,我们将隐藏焦点方块,反之亦然。如果这两个因子的值不相等,我们将改变焦点平方的isHidden值。

let modelsAreVisible = firstVisibleModel != nil

if modelsAreVisible != focusSquareLocal.isHidden {
    focusSquareLocal.setHidden(to: modelsAreVisible)
}

实际上,这一切都令人困惑。我们实际上没有选择,因为节点具有isHidden的属性,并且不显示一个for。好吧,不是我所知道的。

那么,让我们来看看这两个场景。如果modelsAreVisible为true且focusSquareLocal.isHidden为false,则表示两者都可见,然后使setHidden为true(与modelsAreVisible值相同)以隐藏焦点方块。另一方面,如果modelsAreVisible为false且focusSquareLocal.isHidden为true,则两者都无处可见,然后setHidden为false以显示焦点方块。听起来很合乎逻辑。有了它,让我们最后一次运行应用程序。

结论

经过漫长的旅程,我们终于将我们的模型添加到我们的环境中,好像它们属于它。我们在本节中也学到了其他有用的概念。我们在故事板中定制了我们的视图,并在代码中播放动画。在下一课中,我们将使用虚拟对象本身。敬请关注。

原文: https://designcode.io/arkit-adding-models

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.02.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 下载
  • 基本视图
    • 约束
    • 重新 Outlet
    • 添加按钮
    • 添加按钮功能
    • 对象添加文件
    • 导入套件(Kits)
    • 检索模型
      • 可选:PIVOT POINT FIX
      • 显示模型
        • 命中测试
          • 翻转设备
          • 缩放模型
          • 场景模型
          • 焦点方块隐藏/显示选项
          • 视图观点
            • 隐藏/显示焦点方块
            • 结论
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档