Scene Kit View with moving Object - swift

How can I show a moving 3D object in my Simple view Application using scene kit view with swift 4.
For example bee.glb file in windows, I am using these four files:
body.bmp
body.jpg
volkeswagon-vw-beetle.mtl
volkeswagon-vw-beetle.obj
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController {
#IBOutlet weak var sceneView: SCNView!
override func viewDidLoad() {
super.viewDidLoad()
// 1: Load .obj file
let scene = SCNScene(named: "volkeswagon-vw-beetle.obj")
// 2: Add camera node
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
// 3: Place camera
cameraNode.position = SCNVector3(x: 0, y: 10, z: 35)
// 4: Set camera on scene
scene!.rootNode.addChildNode(cameraNode)
// 5: Adding light to scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 35)
scene?.rootNode.addChildNode(lightNode)
// 6: Creating and adding ambien light to scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light?.type = .ambient
ambientLightNode.light?.color = UIColor.darkGray
scene?.rootNode.addChildNode(ambientLightNode)
// Allow user to manipulate camera
sceneView.allowsCameraControl = true
// Show FPS logs and timming
sceneView.showsStatistics = true
// Set background color
sceneView.backgroundColor = UIColor.white
// Allow user translate image
sceneView.cameraControlConfiguration.allowsTranslation = false
// Set scene settings
sceneView.scene = scene
}
}
The output is:
"3DVideo[6430:105708] [SceneKit] Error: Physically based lighting model is not supported by the OpenGL renderer, using Phong instead (3)
2019-09-02 03:29:13.297536-0700 3DVideo[6430:105708] [SceneKit] Error: Physically based lighting model is not supported by the OpenGL renderer, using Phong instead (2)
2019-09-02 03:29:13.308381-0700 3DVideo[6430:105708] [SceneKit] Error: Physically based lighting model is not supported by the OpenGL renderer, using Phong instead"

i resolve this issue by Just change my file extension from .obj to .dae.Because obj file has no any animation

Related

swift SceneKit object is empty

im trying to show a earth model on screen, loaded model from here (tried and other same models), but id didn't show on screen
[let scene = SCNScene(named: "tierra-1.obj")
if scene == nil {
print("nuuuul")
return
}
// 2: Add camera node
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
// 3: Place camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 0)
// 4: Set camera on scene
scene?.rootNode.addChildNode(cameraNode)
// 5: Adding light to scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene?.rootNode.addChildNode(lightNode)
// 6: Creating and adding ambien light to scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light?.type = .ambient
ambientLightNode.light?.color = UIColor.darkGray
scene?.rootNode.addChildNode(ambientLightNode)
// If you don't want to fix manually the lights
sceneView.autoenablesDefaultLighting = true
// Allow user to manipulate camera
sceneView.allowsCameraControl = true
// Show FPS logs and timming
sceneView.showsStatistics = true
// Set background color
sceneView.backgroundColor = UIColor.white
// Allow user translate image
sceneView.cameraControlConfiguration.allowsTranslation = false
// Set scene settings
sceneView.scene = scene][2]
but after loading I have empty screen (example), how to fix that?
I copied and ran your code using a custom obj, I got it loaded and in the viewport.
I loaded in an obj, using a node, and not trying to load the obj as the scene itself.
I noticed that your camera is at world space 0,0,0. that would most likely be inside the mesh, which would appear see through as materials are defaulted with backface culling on.
I assign the material to be blue, just to make it easier to spot incase the asset is very small. I would suggest selecting the asset in XCode, and you can see the bounding box size. This will help get a better idea for how far away you need to move the camera to get it to see the mesh.
import ModelIO
import SceneKit
import SceneKit.ModelIO
import PlaygroundSupport
let scene = SCNScene()
let sceneView = SCNView(frame: CGRect(x:0, y:0, width:800, height:800))
// Set scene settings
sceneView.scene = scene
// 2: Add camera node
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
// 3: Place camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 10)
// 4: Set camera on scene
scene.rootNode.addChildNode(cameraNode)
// Material
let blueMaterial = SCNMaterial()
blueMaterial.diffuse.contents = UIColor.blue
// Accessing the Playground resource folder
let path = Bundle.main.path(forResource:"randomAsset", ofType: "obj")
let asset = MDLAsset(url: URL(string: path!)!)
let object = asset.object(at: 0)
let objNode = SCNNode(mdlObject: object)
objNode.geometry?.materials = [blueMaterial]
scene.rootNode.addChildNode(objNode)
// Allow user to manipulate camera
sceneView.allowsCameraControl = true
// Show FPS logs and timming
//sceneView.showsStatistics = true
// Set background color
sceneView.backgroundColor = UIColor.gray
// Allow user translate image
sceneView.cameraControlConfiguration.allowsTranslation = true
PlaygroundPage.current.setLiveView(sceneView)
I dont think SceneKit supporst loading an Obj as a scene. They currently only allow types that have the ability to export scene data eg.camera,lights. https://developer.apple.com/documentation/scenekit/scnscenesource
If you want to load the obj file you will have to import it into your scene.
This post should help https://forums.developer.apple.com/thread/3979

SceneKit's instance property "autoenablesDefaultLighting" doesn't work

I tried to turn on and off default lighting in SCNView via .autoenablesDefaultLighting instance property but in doesn't work (Neither in UI nor programmatically).
I need all objects to be black when there's no light.
How to turn default lighting off?
Here's a code:
import SceneKit
import QuartzCore
class GameViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scnView = SCNView(frame: NSRect(x: 0,
y: 0,
width: 450,
height: 300))
view.addSubview(scnView)
scnView.autoenablesDefaultLighting = false // DOESN'T WORK
scnView.allowsCameraControl = true
scnView.backgroundColor = NSColor.blue
let scene = SCNScene()
scnView.scene = scene
let sphereGeo = SCNSphere(radius: 2)
sphereGeo.segmentCount = 4
sphereGeo.materials.first?.diffuse.contents = NSColor.lightGray
let sphereNode = SCNNode(geometry: sphereGeo)
sphereNode.name = "Sphere Node"
scene.rootNode.addChildNode(sphereNode)
}
}
It seems it's working only when I'm using Physically Based Rendering shading model.
let material = SCNMaterial()
material.lightingModel = SCNMaterial.LightingModel.physicallyBased
sceneView.autoenablesDefaultLighting = false
If I use .physicallyBased type property for shading my models the lighting works as supposed.

SceneKit: sceneView projectPoint with multiple cameras

If a scene contains multiple cameras, which camera does the projectPoint method use to project points from 3D to screen-space? If this is defined by the pointOfView property, then how come when I update the position of the pointOfView a given 3D point is still projected to the same 2D point?
Since SCNCamera belongs to SCNView, just set the PoV via the "pointOfView" instance property of a View to a required camera node.
let cameraNode001 = SCNNode()
cameraNode001.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode001)
cameraNode001.position = SCNVector3(x: 0, y: 0, z: 15)
let cameraNode002 = SCNNode()
cameraNode002.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode002)
cameraNode002.position = SCNVector3(x: 10, y: 10, z: 30)
let sceneView = self.view as! SCNView
sceneView.scene = scene
sceneView.pointOfView = cameraNode001
then you can change PoV:
sceneView.pointOfView = cameraNode002

Orbit Scenekit camera around specific node

I'm having a difficult time trying to figure out how to get my SceneKit camera to orbit around a specific node in my game.
If I have a single node (a ship) and a camera in my scene everything works fine. If I add an additional node (a planet) the cameras pivot point appears to change from my ship to a space between my ship and planet.
Things I've tried:
Setting a lookat constraint on my camera (set to the ship)
Settingcamera position to my ship (it will move but the pivot point
still seems to be between the two objects)
Changing the cameras pivot point
example:
class TestSceneViewController: UIViewController, SCNSceneRendererDelegate {
var scnView: SCNView = SCNView()
var scnScene: SCNScene!
var cameraNode: SCNNode!
var ship: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setupScene()
setupCamera()
...
func setupView() {
// scnView = self.view as! SCNView
// retrieve the SCNView
scnView = SCNView(frame: view.frame)
scnView.showsStatistics = true
view.addSubview(scnView)
scnView.allowsCameraControl = true
scnView.defaultCameraController.interactionMode = .orbitTurntable
scnView.defaultCameraController.inertiaEnabled = true
scnView.delegate = self
scnView.isPlaying = true
scnView.loops = true
}
func setupScene () {
scnScene = SCNScene()
scnView.scene = scnScene
let ships = SCNScene(named: "art.scnassets/simpleshuttle3.scn")
ship = ships!.rootNode.childNode(withName: "ship", recursively: true)
ship?.position = SCNVector3(x: 0, y: 0, z: 0)
scnScene.rootNode.addChildNode(ship!)
let planets = SCNScene(named: "art.scnassets/sphere.scn")!
if let planet = planets.rootNode.childNode(withName: "Ball", recursively: true){
planet.position = SCNVector3(x: 0, y: 0, z: 40)
scnScene.rootNode.addChildNode(planet)
}
}
func setupCamera() {
cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: ship.position.x, y: ship.position.y, z: 80)
cameraNode.camera?.motionBlurIntensity = 1.0
cameraNode.camera?.automaticallyAdjustsZRange = true
scnScene.rootNode.addChildNode(cameraNode)
}
You are enabling the manual camera control.
scnView.allowsCameraControl = true
If you want to use e.g. SCNLookAtConstraint you have to disable that. Otherwise you have a conflict. The camera is not supposed to point at a certain position while simultaneously being rotated by the user.
If you want to stay with the default camera controller you can create an additional SCNNode as the parent for your planet and the ships. This parent node can now be moved so that the pivot point is at your desired position.

Using SceneKit for hitTesting not returning a hit with SCNNode

The documentation in XCode clearly states that hitTesting a geometry in SceneKit can be done with SCNRender, SCNView or the SCNNode themselves when one plans to test a 3D line segment. I have a use for SCNScene with its nodes without a renderer or a view, therefore I am planning to use SCNNode hitTesting. I create a SCNScene, put a SCNNode in it and test a simple ray that goes through, but I always get an empty hitList and I don't understand why:
import Swift
import SceneKit
let boxGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0)
let boxNode = SCNNode(geometry: boxGeometry)
var scene = SCNScene()
scene.rootNode.addChildNode(boxNode)
let from = SCNVector3(x: 0, y: -2, z: 0)
let to = SCNVector3(x: 0, y: 2 , z: 0)
var hits = scene.rootNode.hitTestWithSegmentFromPoint(from, toPoint: to, options:nil) // this is always empty
if hits != nil {
if hits!.count > 0 {
var hit = (hits!.first as! SCNHitTestResult).node as SCNNode
}
}
I have tried passing various forms of options but nothing changes.
SCNHitTestFirstFoundOnlyKey: yes or no does not change anything
SCNHitTestSortResultsKey: yes or no does not change anything
SCNHitTestClipToZRangeKey: invalid for SCNNode
SCNHitTestBackFaceCullingKey: yes or no does not change anything
SCNHitTestBoundingBoxOnlyKey: yes or no does not change anything
SCNHitTestRootNodeKey: rootNOde of scene or boxNode does not change
anything
SCNHitTestIgnoreHiddenNodesKey: yes or no does not change anything
What am I doing wrong?
I have found the answer, which is either a bug or a feature: using SCNScene and its nodes SCNNode for 3D hitTesting, in particular the method: "hitTestWithSegmentFromPoint(toPoint:options:)" does not return a hit unless the scene is included in an SCNView. It appears it cannot be used offscreen. My guess is yours for why this is the case, although I can imagine it has something to do with performing some of these quite expensive calculations on the graphics card.
I have tested this using the GameView SCNScene starter project. The critical line is self.gameView!.scene = scene
override func awakeFromNib(){
let scene = SCNScene()
let boxGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
let boxNode = SCNNode(geometry: boxGeometry)
boxNode.position=SCNVector3(x: 0, y: 0, z: 0)
scene.rootNode.addChildNode(boxNode)
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = SCNLightTypeOmni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = NSColor.darkGrayColor()
scene.rootNode.addChildNode(ambientLightNode)
// set the scene to the view
// uncomment this to fail
self.gameView!.scene = scene
// allows the user to manipulate the camera
self.gameView!.allowsCameraControl = true
// show statistics such as fps and timing information
self.gameView!.showsStatistics = true
// configure the view
self.gameView!.backgroundColor = NSColor.blackColor()
let hitList = scene.rootNode.hitTestWithSegmentFromPoint(SCNVector3(x:-10,y:0,z:0), toPoint: SCNVector3(x:10,y:0,z:0), options:[SCNHitTestBackFaceCullingKey:false, SCNHitTestSortResultsKey:true, SCNHitTestIgnoreHiddenNodesKey:false])
if hitList?.count > 0 {
println("Hit found: \n\n\( hitList![0] )") // assign self.gameView!.scene = scene to reach this point.
} else {
println("No hit") // uncomment self.gameView!.scene = scene to reach this point.
}
}
I've also had trouble with hitTestWithSegmentFromPoint.
I was calling it in viewDidLoad() and it returned a 0 elements array, though I was sure there was a hit.
Calling it in viewDidAppear() (or later) solved my problem.