How to use a SKScene as a SCNMaterial? - ios

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)

Related

How to add Shadow on surface plane?

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

SceneKit With Vuforia AR library custom models

I am new in SceneKit, working with swift 3 and latest Vuforia library 6-2-9.
I have a problem with applying textures on custom 3D models.
fileprivate func createDefaultMarkerScene(with view: VuforiaEAGLView) -> SCNScene {
let scene = SCNScene()
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.light?.color = UIColor.lightGray
lightNode.position = SCNVector3(x: 0, y: 0, z: 1000)
scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light?.type = .ambient
ambientLightNode.light?.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
let geoNode = SCNNode()
geoNode.name = "model_model"
let normalsSources = SCNGeometrySource(normals: normalsSCNVector3)
let verticesSources = SCNGeometrySource(vertices: verticesSCNVector3)
let texCoordsSources = SCNGeometrySource(textureCoordinates: texCoordCGPoint)
let indicesElements = SCNGeometryElement(indices: indices, primitiveType: SCNGeometryPrimitiveType.triangles)
geoNode.geometry = SCNGeometry(sources: [verticesSources, normalsSources, texCoordsSources], elements: [indicesElements])
geoNode.position = SCNVector3(x: 0, y: 0, z: 0)
geoNode.scale = SCNVector3(x: 50, y: 50, z: 50)
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "grad_ao.png")
geoNode.geometry?.firstMaterial = material
scene.rootNode.addChildNode(geoNode)
return scene
}
Model is properly rendered, but applied texture is completely messed up. I have tried with rotated image, also uv coordinates all ok [0..1].
Any ideas? Thank you
I have managed to fix an issue.
The key is in "texture.png" file.
I just need to flip the image for 180 degrees with:
material.diffuse.contentsTransform = SCNMatrix4Translate(SCNMatrix4MakeScale(1, -1, 1), 0, 1, 0)

SceneKit: vertically center scaled SCNNode inside parent?

The goal is to scale a SCNNode and vertically center it within its parent.
However, scaling a SCNNode does not affect its bounding box and computing the scaled height does not work. Without an accurate height, how can you vertically center the node inside its parent?
To illustrate the problem with using the scaled height, see the attached file, Tiki.dae. The original height of the asset (as shown by the bounding box) is 324.36. If you set the Y-scale to 0.01, however, the height doesn't become ~3.24. It becomes smaller than 3, which you can prove by fitting it comfortably within a sphere of height 3 (radius of 1.5).
The code below attempts to center a scaled node inside its parent, but it doesn't work.
Note: reference node is the fox/panda reference node from the WWDC 2015 fox demo.
// Create reference node
let referenceNode = SCNReferenceNode(URL: referenceURL)
referenceNode?.load()
// Scale reference node
let scale = Float(3)
referenceNode?.scale = SCNVector3(x: scale, y: scale, z: scale)
// Create geometry for sphere
let sphereGeometry = SCNSphere(radius: (gridSphere.geometry as! SCNSphere).radius)
//sphereGeometry.materials = gridSphere.geometry!.materials
sphereGeometry.firstMaterial!.diffuse.contents = gPurpleColor
// Create sphere to hold reference node, position at same place as <gridSphere>
let liveSphere = SCNNode(geometry: sphereGeometry)
liveSphere.position = gridSphere.position
// Center reference node inside <liveSphere>
var min = SCNVector3Zero
var max = SCNVector3Zero
referenceNode?.getBoundingBoxMin(&min, max: &max)
let referenceNodeHeight = max.y - min.y
referenceNode?.position = SCNVector3(x: 0, y: 0 - referenceNodeHeight, z: 0)
// Add reference node to <liveSphere>
liveSphere.addChildNode(referenceNode!)
// This value never changes no matter the scale value???
print(referenceNodeHeight)
Here's a Playground that adds a cube (in place of your reference node) as a child of the sphere. The cube responds to scaling (see the lines following "// Scale reference node").
//: Playground - noun: a place where people can play
import Cocoa
import SceneKit
import XCPlayground
public class GizmoNode: SCNNode {
required public override init() {
super.init()
let axisLength = CGFloat(3.0)
let offset = CGFloat(axisLength/2.0)
let axisSide = CGFloat(0.2)
let chamferRadius = CGFloat(axisSide)
let xBox = SCNBox(width: axisLength, height: axisSide, length: axisSide, chamferRadius: chamferRadius)
xBox.firstMaterial?.diffuse.contents = NSColor.redColor()
let yBox = SCNBox(width: axisSide, height: axisLength, length: axisSide, chamferRadius: chamferRadius)
yBox.firstMaterial?.diffuse.contents = NSColor.greenColor()
let zBox = SCNBox(width: axisSide, height: axisSide, length: axisLength, chamferRadius: chamferRadius)
zBox.firstMaterial?.diffuse.contents = NSColor.blueColor()
let xNode = SCNNode(geometry: xBox)
let yNode = SCNNode(geometry: yBox)
let zNode = SCNNode(geometry: zBox)
self.addChildNode(xNode)
self.addChildNode(yNode)
self.addChildNode(zNode)
print (xNode.position)
print (yNode.position)
print (zNode.position)
xNode.position.x = offset
yNode.position.y = offset
zNode.position.z = offset
print (xNode.pivot)
print (yNode.pivot)
print (zNode.pivot)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
let scene = SCNScene()
sceneView.scene = scene
sceneView.backgroundColor = NSColor.blackColor()
sceneView.allowsCameraControl = true
let gizmo = GizmoNode()
scene.rootNode.addChildNode(gizmo)
XCPlaygroundPage.currentPage.liveView = sceneView
// Create reference node
let cubeSize = CGFloat(0.5)
let cubeGeometry = SCNBox(width: cubeSize, height: cubeSize, length: cubeSize, chamferRadius: 0.0)
let referenceNodeStandIn = SCNNode(geometry: cubeGeometry)
//referenceNodeStandIn?.load()
let cubeColor = NSColor.whiteColor().colorWithAlphaComponent(0.5)
cubeGeometry.firstMaterial!.diffuse.contents = cubeColor
// Scale reference node
let scale = CGFloat(8)
referenceNodeStandIn.scale = SCNVector3(x: scale, y: scale, z: scale)
// Create geometry for sphere
let sphereRadius = CGFloat(2.0)
let sphereGeometry = SCNSphere(radius: sphereRadius)
//sphereGeometry.materials = gridSphere.geometry!.materials
let gPurpleColor = NSColor.purpleColor().colorWithAlphaComponent(1.0)
sphereGeometry.firstMaterial!.diffuse.contents = gPurpleColor
// Create sphere to hold reference node, position at same place as <gridSphere>
let liveSphere = SCNNode(geometry: sphereGeometry)
//liveSphere.position = gridSphere.position
scene.rootNode.addChildNode(liveSphere)
// Center reference node inside <liveSphere>
var min = SCNVector3Zero
var max = SCNVector3Zero
referenceNodeStandIn.getBoundingBoxMin(&min, max: &max)
print("min: \(min) max: \(max)")
let referenceNodeHeight = max.y - min.y
//referenceNodeStandIn.position = SCNVector3(x: 0, y: 0 - referenceNodeHeight, z: 0)
// Add reference node to <liveSphere>
liveSphere.addChildNode(referenceNodeStandIn)
// This value never changes no matter the scale value because it's in local coordinate space
print("reference node height", referenceNodeHeight)

Adding an SKLabelNode to Scene Kit View

I am building a game using Scene Kit. In order to present the score I wanted to use an SKLabelNode on the screen, however, when I attach it to a SCNNode, it looks very blurry:
Here is the code that I have written to do this, please let me know if there is a better way to go about doing this without having the text be so blurry. Thank you so much!
func initHUD() {
let skScene = SKScene(size: CGSize(width: 100, height: 100))
skScene.backgroundColor = UIColor(white: 0.0, alpha: 0.0)
labelNode = SKLabelNode()
labelNode.fontSize = 20
labelNode.position.y = 50
labelNode.position.x = 50
skScene.addChild(labelNode)
let plane = SCNPlane(width: 1, height: 1)
let material = SCNMaterial()
material.lightingModelName = SCNLightingModelConstant
material.doubleSided = true
material.diffuse.contents = skScene
plane.materials = [material]
hudNode = SCNNode(geometry: plane)
hudNode.name = "HUD"
hudNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: 3.14159265)
hudNode.position = SCNVector3(x:0, y: 1, z: -5)
}
func updateHUD() {
labelNode.text = "\(score)"
}
The typical way to do a HUD for a SceneKit scene is to create a SpriteKit SKScene and set it as the overlaySKScene of your SceneKit view. Then it renders at full resolution and always at the same view-relative size and position.

Scale SCNNode in SceneKit

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:

Resources