Reputation: 1
I have a free 3d skull like in this photo and code in below:
import UIKit
import SceneKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named: "skull.scn")!
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
let scnView = self.view as! SCNView
scnView.scene = scene
scnView.allowsCameraControl = true
scnView.backgroundColor = UIColor.black
let button = UIButton(frame: CGRect(x: 100, y: 600, width: 200, height: 60))
button.setTitle("Open Jaw", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
}
@objc func buttonAction() {
print("Button pressed")
// How can I open jaw here?
}
}
As you can see in code, I want open the jaw via button, since I have all materials in file, how could I use Dflt2 to do this, I want to know how can I get access to Dflt2 for rotation as opening Jaw with a smooth animation or even changing the color, is it possible?
Upvotes: 1
Views: 642
Reputation: 58053
Unlike pro apps, like Autodesk Maya, where through the material you can select the geometry which that material is assigned to, SceneKit doesn't allow you to do that. However, SceneKit, like Maya, has a nodal hierarchical scene structure...
SCNView –> SCNScene -> RootNode
| -> Node_01
| | -> Subnode_A -> Geometry -> Materials
| | -> Subnode_B -> Geometry -> Materials
|
| -> Node_02 -> Geometry -> Materials
|
| -> Node_03 -> Geometry -> Materials
|
| -> Node_04
| | -> Subnode_A
| | -> Subnode_B -> Camera
|
| -> Node_05 -> SpotLight
|
| -> Node_06 -> ParticleSystem
|
| -> Node_07 -> AudioPlayer
...where you can get to desired material only by selecting a specific geometry within a particular node. Such an approach is called node-centric, there's no a material-centric approach, by default. The above abstract diagram shows us the connection of nodes in the scene, where each node has its own transform parameters (translate, rotate and scale).
Thus, you can do what you want only when retrieving a node:
import SceneKit
class GameViewController: UIViewController {
@IBOutlet var sceneView: SCNView!
let scene = SCNScene(named: "art.scnassets/Skull.usdz")!
var lowerJaw: SCNNode? = nil
var counter: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = self.scene
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = .black
guard let skullNode = self.scene.rootNode.childNode(
withName: "Geom", recursively: true)
else { return }
self.lowerJaw = skullNode.childNodes[0].childNode(
withName: "lower_part", recursively: true)
lowerJaw?.childNodes[0].geometry?.firstMaterial?.diffuse.contents =
UIColor.systemBlue
}
@IBAction func pressed(_ sender: UIButton) {
self.counter += 1
let opened = SCNAction.rotateTo(x: .pi/20, y: 0, z: 0, duration: 0.3)
let closed = SCNAction.rotateTo(x: 0, y: 0, z: 0, duration: 0.3)
if (counter % 2 == 0) {
self.lowerJaw?.runAction(closed)
} else {
self.lowerJaw?.runAction(opened)
}
}
}
As you can see, in the Xcode Scene graph
, the "Geom"
node contains all the parts of the skull, and the "lower_part"
subnode, that we're rotating, contains the skull's jaw and lower teeth. So, the most important thing about 3D objects – you need to have separate 3D geometry parts (nodes) in a scene in order to animate them (not a mono-model). The "lower_part"
node is rotated around its pivot point. Each SceneKit's node has its own personal pivot. If you need to move the pivot point to another location use this code.
In SceneKit you can use .usdz
, .scn
, .dae
or .obj
scenes. Xcode's Scene graph
allows you to create, delete, reposition, nest and rename nodes. Nevertheless, the most robust place to build an hierarchy of nodes is 3D authoring app (like Maya or Blender).
However, nothing prevents you from creating a collection of dictionaries like [SCNMaterial: SCNNode].
Abstract example (2 spheres, 2 materials):
import SceneKit
let sceneView = SCNView(frame: .zero)
sceneView.scene = SCNScene()
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.red
let greenMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.green
let nodeA = SCNNode(geometry: SCNSphere(radius: 0.1))
nodeA.position.z = -2.5
nodeA.geometry?.materials[0] = redMaterial
let nodeB = SCNNode(geometry: SCNSphere(radius: 0.1))
nodeB.position.y = 1.73
nodeB.geometry?.materials[0] = greenMaterial
sceneView.scene?.rootNode.addChildNode(nodeA)
sceneView.scene?.rootNode.addChildNode(nodeB)
// Dictionary
var getNode = [redMaterial: nodeA, greenMaterial: nodeB]
getNode[redMaterial]?.position.z // -2.5
getNode[greenMaterial]?.position.y // 1.73
Upvotes: 2