I have the following SCNNode:
let box = SCNBox(width: 10.0, height: 10.0, length: 10.0, chamferRadius: 0)
let boxNode = SCNNode(geometry: box)
boxNode.position = SCNVector3(x: 0, y: 0, z: 0)
If I apply:
boxNode.scale = SCNVector3(x: 0.5, y: 0.5, z: 0.5)
it appears to have no effect on the size of the box. I've checked this with boxNode.getBoundingBoxMin(&v1, max: &v2). It's always the same and appears the same on-screen.
Am I missing something? The docs imply that setting the scale should affect the node's geometry and thus be a different size.
Thanks.
J.
I just tested in a playground and it worked perfectly for me. Perhaps your camera is zooming in to compensate for the smaller box?
import SceneKit
import SpriteKit
import XCPlayground
let sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 800, height: 600))
XCPlaygroundPage.currentPage.liveView = sceneView
var scene = SCNScene()
sceneView.scene = scene
sceneView.backgroundColor = SKColor.greenColor()
sceneView.debugOptions = .ShowWireframe
// default lighting
sceneView.autoenablesDefaultLighting = true
// a camera
var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 15, y: 15, z: 30)
scene.rootNode.addChildNode(cameraNode)
let box = SCNBox(width: 10.0, height: 10.0, length: 10.0, chamferRadius: 0)
let boxNode = SCNNode(geometry: box)
boxNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene.rootNode.addChildNode(boxNode)
let centerConstraint = SCNLookAtConstraint(target: boxNode)
cameraNode.constraints = [centerConstraint]
let sphere = SCNSphere(radius: 4)
let sphereNode = SCNNode(geometry: sphere)
sphereNode.position = SCNVector3(x:15, y:0, z:0)
scene.rootNode.addChildNode(sphereNode)
//boxNode.scale = SCNVector3(x: 0.5, y: 0.5, z: 0.5)
Uncomment the last line and you'll see the box (10 x 10 x 10) switch switch from being larger than the sphere (diameter of 8) to being smaller than the sphere. Here it is after the scaling:
Related
I've started learning SceneKit. And I've tried SCNBox. And it has chamferRadius. But the radius is applied for all the edges.
But I want to achieve something similar to the one on the screenshot below
You can do this by extruding a UIBezierPath:
// rounded rect bezier path
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 1.0, height: 1.0), cornerRadius: 0.1)
path.flatness = 0
// extrude the path
let shape = SCNShape(path: path, extrusionDepth: 0.05)
let mat = SCNMaterial()
mat.diffuse.contents = UIColor(white: 0.9, alpha: 1.0)
shape.materials = [mat]
let shapeNode = SCNNode(geometry: shape)
Result:
Here's a full example (note: I have only glanced at SceneKit, so I used this tutorial as a starting point Introduction to SceneKit):
import UIKit
import SceneKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let sceneView = SCNView(frame: self.view.frame)
self.view.addSubview(sceneView)
let scene = SCNScene()
sceneView.scene = scene
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 3.0, y: 2.0, z: 1.5)
let ambientLight = SCNLight()
ambientLight.type = .ambient
ambientLight.color = UIColor(white: 0.9, alpha: 1.0)
cameraNode.light = ambientLight
let light = SCNLight()
light.type = SCNLight.LightType.spot
light.spotInnerAngle = 30.0
light.spotOuterAngle = 80.0
light.castsShadow = true
let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5)
// rounded rect bezier path
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 1.0, height: 1.0), cornerRadius: 0.1)
path.flatness = 0
// extrude the path
let shape = SCNShape(path: path, extrusionDepth: 0.05)
let mat = SCNMaterial()
mat.diffuse.contents = UIColor(white: 0.9, alpha: 1.0)
shape.materials = [mat]
let shapeNode = SCNNode(geometry: shape)
let planeGeometry = SCNPlane(width: 50.0, height: 50.0)
let planeNode = SCNNode(geometry: planeGeometry)
planeNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0)
planeNode.position = SCNVector3(x: 0, y: 0.0, z: 0)
let floorMaterial = SCNMaterial()
floorMaterial.diffuse.contents = UIColor.lightGray
planeGeometry.materials = [floorMaterial]
scene.rootNode.addChildNode(lightNode)
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(shapeNode)
scene.rootNode.addChildNode(planeNode)
let constraint = SCNLookAtConstraint(target: shapeNode)
constraint.isGimbalLockEnabled = true
cameraNode.constraints = [constraint]
lightNode.constraints = [constraint]
}
}
I am having a SKScene as a SCNMaterial that I want to apply on a SCNSphere. I want to add a point on the surface of the sphere, that should be partially visible even when it is on the other side of the sphere. So I want a double sided texture. But when I use spherematerial.isDoubleSided = true I get a whitish layer on top of my sphere. Not sure why this happens.
Here is my code:
let surfacematerial = SKScene(size: CGSize(width: 800, height: 400))
surfacematerial.backgroundColor = SKColor.blue
self.point.fillColor = SKColor.red
self.point.lineWidth = 0
self.point.position = CGPoint(x: 200, y: 200)
surfacematerial.addChild(point)
let spherematerial = SCNMaterial()
spherematerial.isDoubleSided = true
spherematerial.diffuse.contents = surfacematerial
let sphere = SCNSphere(radius: CGFloat(2))
sphere.materials = [spherematerial]
spherenode = SCNNode(geometry: sphere)
spherenode.position = SCNVector3(x: 0, y: 0, z: 0)
spherenode.opacity = CGFloat(0.9)
I Have one problem, In ARView Display Shadow on the object but did not display on the surface, I have attached try code? It is working Display shadow on the object but does not display on the surface.
Show Image
Code :
var light2 = SCNLight()
var lightNodeb2 = SCNNode()
light2.castsShadow = true
light2.automaticallyAdjustsShadowProjection = true
light2.maximumShadowDistance = 40.0
light2.orthographicScale = 1
light2.type = .directional
light2.shadowMapSize = CGSize(width: 2048, height: 2048)
light2.shadowMode = .forward
light2.shadowSampleCount = 128
light2.shadowRadius = 3
light2.shadowBias = 32
light2.zNear=1;
light2.zFar=1000;
lightNodeb2.light = light2
lightNodeb2.position = SCNVector3(x: -1, y: 10, z: 1)
lightNodeb2.rotation = SCNVector4Make(2, 0, 0, -Float.pi / 3)
self.sceneView.scene.rootNode.addChildNode(lightNodeb2)
I see here 2 possible problems:
Your light's settings are OK. I think the first problem is following: you use programmatic light with "non-programmatic" 3D objects in Scene graph.
Check it. In this code all objects are programmatic:
let sphereNode1 = SCNNode(geometry: SCNSphere(radius: 1))
sphereNode1.position = SCNVector3(x: 0, y: 5, z: 3)
scene.rootNode.addChildNode(sphereNode1)
let sphereNode2 = SCNNode(geometry: SCNSphere(radius: 3))
sphereNode2.position = SCNVector3(x: 0, y: 1, z: 0)
scene.rootNode.addChildNode(sphereNode2)
let boxNode = SCNNode(geometry: SCNBox(width: 20, height: 0.1, length: 20, chamferRadius: 0))
boxNode.position = SCNVector3(x: 0, y: -3, z: 0)
scene.rootNode.addChildNode(boxNode)
let light2 = SCNLight()
let lightNodeb2 = SCNNode()
light2.castsShadow = true
light2.automaticallyAdjustsShadowProjection = true
light2.maximumShadowDistance = 40.0
light2.orthographicScale = 1
light2.type = .directional
light2.shadowMapSize = CGSize(width: 2048, height: 2048)
light2.shadowMode = .deferred // Renders shadows in a postprocessing pass
light2.shadowSampleCount = 128
light2.shadowRadius = 3
light2.shadowBias = 32
light2.zNear = 1
light2.zFar = 1000
lightNodeb2.light = light2
// DIRECTIONAL LIGHT POSITION doesn't matter. Matters only its direction.
// lightNodeb2.position = SCNVector3(x: -1, y: 10, z: 1)
lightNodeb2.rotation = SCNVector4(2, 0, 0, -Float.pi/3)
scene.rootNode.addChildNode(lightNodeb2)
All objects Shadow castings are On by default.
First problem's Solution:
To make your 3D model to become accessible for virtual programmatic lights use the following approach:
func childNode(withName name: String, recursively: Bool) -> SCNNode? {
return SCNNode()
}
let geometryNode = childNode(withName: "art.scnassets/your3Dmodel",
recursively: true)!
scene.rootNode.addChildNode(geometryNode)
Second problem's Solution:
And if you wanna have a hidden plane with a shadow use this code:
hiddenPlaneNode.castsShadow = false
hiddenPlaneNode.geometry?.firstMaterial?.lightingModel = .constant
hiddenPlaneNode.geometry?.firstMaterial?.isDoubleSided = true
hiddenPlaneNode.geometry?.firstMaterial?.readsFromDepthBuffer = true
hiddenPlaneNode.geometry?.firstMaterial?.writesToDepthBuffer = true
hiddenPlaneNode.geometry?.firstMaterial?.colorBufferWriteMask = []
light2.shadowMode = .deferred
I am trying to rotate by scene kit node, however it is not rotating.
I want it to rotate around the y axis. It is a sphere.
let node = SCNNode()
node.geometry = SCNSphere(radius: 1)
node.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "diffuse.png")
node.geometry?.firstMaterial?.specular.contents = UIImage(named: "specular.png")
node.geometry?.firstMaterial?.emission.contents = UIImage(named: "emission.png")
scene.rootNode.addChildNode(node)
let action = SCNAction.rotate(by:360 * CGFloat((Double.pi)/180.0), around: SCNVector3(x: 0, y: 1, z: 0), duration: 8)
let repeatAction = SCNAction.repeatForever(action)
node.runAction(repeatAction)
Are you certain it isn't rotating? I'm not sure what your diffuse, specular and emission images are but maybe because it's a sphere it just doesn't really look like it is rotating because it looks the same from all sides.
Running the same action on a cube in viewDidAppear is working for me, so it could just be that you can't really notice the sphere rotating. I used this code:
let node = SCNNode()
node.geometry = SCNBox (width: 0.5, height: 0.5, length: 0.5, chamferRadius: 0.1)
node.position = SCNVector3Make(0, 0, -1)
sceneView.scene.rootNode.addChildNode(node)
let action = SCNAction.rotate(by:360 * CGFloat((Double.pi)/180.0), around: SCNVector3(x: 0, y: 1, z: 0), duration: 8)
let repeatAction = SCNAction.repeatForever(action)
node.runAction(repeatAction)
The cube is rotating fine with the way that you rotated the SCNNode.
Hope this helps
I have 3D star in spritekit and have it rotating along z axis so it looks like its spinning.
However I cannot get it to spin.
The file is a .dae file which I have imported into my project.
The star itself loads but nothing happens. Any idea anyone?
import SpriteKit
override func didMove(to view: SKView) {
let starScene = SCNScene.init(named: "star.dae")
let star3d = SK3DNode(viewportSize: CGSize(width: 330, height: 330))
star3d.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
star3d.scnScene = starScene
star3d.zPosition = 3
star3d.isPlaying = true
star3d.scnScene = starScene
let camera = SCNCamera()
camera.xFov = 60
camera.yFov = 60
var cameraNode = SCNNode()
cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
let rotationAnimation = CABasicAnimation(keyPath: "rotation")
rotationAnimation.toValue = NSValue(scnVector4: SCNVector4Make(1, 2, 3, .pi * 2))
rotationAnimation.duration = 6
rotationAnimation.repeatCount = FLT_MAX
starScene?.rootNode.addChildNode(cameraNode)
star3d.pointOfView = cameraNode
self.addChild(star3d)
cameraNode.addAnimation(rotationAnimation, forKey: nil)
}
You have correctly set star3d.pointOfView to the camera, but you haven't added cameraNodeto the scene.
starScene.rootNode.addChildNode(cameraNode)
Here's a complete version, in a macOS playground. Swap out the last two lines (current.liveView) to see the raw SceneKit render or the combined SpriteKit render.
import Cocoa
import SceneKit
import SpriteKit
import PlaygroundSupport
let spritekitView = SKView(frame: CGRect(x: 0, y: 0, width: 800, height: 500))
let scenekitView = SCNView(frame: CGRect(x: 0, y: 0, width: 800, height: 500))
let spriteKitScene = SKScene(size: CGSize(width: 480, height: 320))
spritekitView.showsFPS = true
spritekitView.presentScene(spriteKitScene)
PlaygroundSupport.PlaygroundPage.current.liveView = spritekitView
// let starScene = SCNScene.init(named: "star.dae")
let starScene = SCNScene()
starScene.rootNode.addChildNode(SCNNode(geometry: SCNBox(width: 1, height: 2, length: 3, chamferRadius: 0.2)))
let star3d = SK3DNode(viewportSize: CGSize(width: 330, height: 330))
star3d.position = CGPoint(x: spriteKitScene.frame.midX, y: spriteKitScene.frame.midY)
star3d.scnScene = starScene
star3d.isPlaying = true
spriteKitScene.addChild(star3d)
let camera = SCNCamera()
camera.xFov = 60
camera.yFov = 60
var cameraNode = SCNNode()
cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 3, z: 15)
starScene.rootNode.addChildNode(cameraNode)
star3d.pointOfView = cameraNode
scenekitView.scene = starScene
scenekitView.autoenablesDefaultLighting = true
scenekitView.pointOfView = cameraNode
let rotateVector = SCNVector4Make(0, 0, 1, CGFloat(2 * M_PI))
let rotate = SCNAction.repeatForever(SCNAction.rotateBy(x: 0.0, y: 0, z: CGFloat(2 * M_PI), duration: 10.0))
cameraNode.runAction(rotate)
let rotationAnimation = CABasicAnimation(keyPath: "rotation")
rotationAnimation.toValue = NSValue(scnVector4: rotateVector)
rotationAnimation.duration = 6
rotationAnimation.repeatCount = FLT_MAX
//cameraNode.addAnimation(rotationAnimation, forKey: nil)
PlaygroundPage.current.liveView = spritekitView
//PlaygroundPage.current.liveView = scenekitView
Thanks to Hal Mueller.
The following spinning star effect can be achieved with the below code adding a sk3dnode into spritekit.
media0.giphy.com/media/rOhH4D15SlWCc/giphy.gif
If the animation does not run on your device.
In your project .plist file add the following row.
PrefersOpenGL = YES
let starScene = SCNScene.init(named: "Round_CStar_obj.obj.dae")
let star3d = SK3DNode(viewportSize: CGSize(width: 300, height: 300))
star3d.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
star3d.scnScene = starScene
star3d.name = "star3d"
star3d.zPosition = 3
star3d.zRotation = 6
star3d.isPlaying = true
star3d.scnScene = starScene
self.addChild(star3d)
let camera = SCNCamera()
camera.usesOrthographicProjection = true
camera.orthographicScale = 9
camera.zNear = 0
camera.zFar = 200
var cameraNode = SCNNode()
cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 0)
cameraNode.eulerAngles = SCNVector3(x: 0, y: 0, z: 0)
starScene?.rootNode.addChildNode(cameraNode)
star3d.pointOfView = cameraNode
let rotationAnimation = CABasicAnimation(keyPath: "rotation")
rotationAnimation.toValue = NSValue(scnVector4: SCNVector4Make(0, 1, 0, .pi * 2))
rotationAnimation.duration = 2
rotationAnimation.repeatCount = FLT_MAX
cameraNode.addAnimation(rotationAnimation, forKey: nil)