ARKit, ScenceKit. How to render several SCNNode?
У меня есть моя routeFinishNode
var routeFinishNode: SCNNode? = nil {
didSet {
oldValue?.removeFromParentNode()
if let node = routeFinishNode {
sceneView.scene.rootNode.addChildNode(node)
}
}
}
Также у меня есть массив маршрутов
private var routes: [MKRoute] = []
Передо мной стоит задача отобразить метки на сцене с использование кастомной вьюшки и лейбла для отображения дистанции до объекта.
Моя вьюшка и лейбел:
var routeFinishView: UIView? = nil {
didSet {
oldValue?.removeFromSuperview()
if let routeFinishView = routeFinishView {
view.addSubview(routeFinishView)
}
}
}
var routeDistanceLabel: UILabel? = nil {
didSet {
oldValue?.removeFromSuperview()
if let label = routeDistanceLabel {
view.addSubview(label)
}
}
Что я делаю: я прохожусь по массиву маршрутов и инициализирую ноду, вьюшку и лейбел:
func showRouteFinishNodes() {
routes.forEach { (route) in
let routePointsCount = route.polyline.pointCount
let routePoints = route.polyline.points()
var geoRoute: [CLLocationCoordinate2D] = []
for pointIndex in 0..<routePointsCount {
let point: MKMapPoint = routePoints[pointIndex]
geoRoute.append(MKCoordinateForMapPoint(point))
}
let route = geoRoute
.map { engine.convert(coordinate: $0) }
.compactMap { $0 }
.map { CGPoint(position: $0) }
guard route.count == geoRoute.count else {
return
}
guard let routeFinishPoint = route.last else { return }
let node = SCNNode()
node.position = routeFinishPoint.positionIn3D
nodesArray.append(node)
}
nodesArray.forEach {
print($0.position)
routeFinishNode = $0
sceneView.scene.rootNode.addChildNode(routeFinishNode ?? SCNNode())
print(sceneView.scene.rootNode.childNodes.count)
routeFinishHint = makeFinishNodeHint()
routeFinishView = makeFinishNodeView()
routeDistanceLabel = makeDistanceLabel()
}
}
Для отображения и рендеринга вьюшки и лейбла я использую делегат renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval)
func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) {
guard let routeFinishNode = routeFinishNode else { return }
print(routeFinishNode.position)
guard let parent = routeFinishNode.parent else { return }
guard let pointOfView = renderer.pointOfView else { return }
let bounds = UIScreen.main.bounds
let positionInWorld = routeFinishNode.worldPosition
let positionInPOV = parent.convertPosition(routeFinishNode.position, to: pointOfView)
let projection = sceneView.projectPoint(positionInWorld)
let projectionPoint = CGPoint(x: CGFloat(projection.x), y: CGFloat(projection.y))
let annotationPositionInRFN = SCNVector3Make(0.0, 1.0, 0.0) // in Route finish node coord. system
let annotationPositionInWorld = routeFinishNode.convertPosition(annotationPositionInRFN, to: nil)
let annotationProjection = sceneView.projectPoint(annotationPositionInWorld)
let annotationProjectionPoint = CGPoint(x: CGFloat(annotationProjection.x), y: CGFloat(annotationProjection.y))
let rotationAngle = Vector2.y.angle(with: (Vector2(annotationProjectionPoint) - Vector2(projectionPoint)))
let screenMidToProjectionLine = CGLine(point1: bounds.mid, point2: projectionPoint)
let intersection = screenMidToProjectionLine.intersection(withRect: bounds)
let povWorldPosition: Vector3 = Vector3(pointOfView.worldPosition)
let routeFinishWorldPosition: Vector3 = Vector3(positionInWorld)
let distanceToFinishNode = (povWorldPosition - routeFinishWorldPosition).length
DispatchQueue.main.async { [weak self] in
guard let slf = self else { return }
guard let routeFinishHint = slf.routeFinishHint else { return }
guard let routeFinishView = slf.routeFinishView else { return }
guard let routeDistanceLabel = slf.routeDistanceLabel else { return }
let placemarkSize = ViewController.finishPlacemarkSize(
forDistance: CGFloat(distanceToFinishNode),
closeDistance: 10.0,
farDistance: 25.0,
closeDistanceSize: 100.0,
farDistanceSize: 50.0
)
let distance = floor(distanceToFinishNode)
let point: CGPoint = intersection ?? projectionPoint
let isInFront = positionInPOV.z < 0
let isProjectionInScreenBounds: Bool = intersection == nil
if slf.routeUISwitch.isOn {
routeFinishHint.isHidden = (isInFront && intersection == nil)
routeFinishView.isHidden = !routeFinishHint.isHidden
routeDistanceLabel.isHidden = routeFinishView.isHidden
} else {
routeFinishHint.isHidden = true
routeFinishView.isHidden = true
routeDistanceLabel.isHidden = true
}
if isInFront {
routeFinishHint.center = point
} else {
if isProjectionInScreenBounds {
routeFinishHint.center = CGPoint(
x: reflect(point.x, of: bounds.mid.x),
y: bounds.height
)
} else {
routeFinishHint.center = CGPoint(
x: reflect(point.x, of: bounds.mid.x),
y: reflect(point.y, of: bounds.mid.y)
)
}
}
routeFinishView.center = projectionPoint
routeFinishView.bounds.size = CGSize(width: placemarkSize, height: placemarkSize)
routeFinishView.layer.cornerRadius = placemarkSize / 2
let distanceString = "\(distance) м"
let distanceAttrStr = ViewController.distanceText(forString: distanceString)
routeDistanceLabel.attributedText = distanceAttrStr
routeDistanceLabel.center = projectionPoint
let size = distanceAttrStr.boundingSize(width: .greatestFiniteMagnitude)
routeDistanceLabel.bounds.size = size
routeDistanceLabel.transform = CGAffineTransform(rotationAngle: CGFloat(rotationAngle - .pi))
}
}
Моя проблема заключается в том, что из всех созданных нод на сцене рендериться только одна - последняя из массива. Как сделать так, чтобы рендерилась каждая нода ?
Как работает сейчас:
Как нужно чтобы работало:

