SCNGeometry NOT using Data - swift

I can’t find any examples online showing the proper creation of triangles for SCNGeometry that don't use the NSData/Data API. I’m trying to do this but nothing renders:
func
testTriangles()
-> SCNNode
{
// Vertices…
var vertices = [SCNVector3]()
vertices.append(SCNVector3(x: 0.0, y: 0.0, z: 0.0))
vertices.append(SCNVector3(x: 0.0, y: 0.0, z: 50.0))
vertices.append(SCNVector3(x: 50.0, y: 0.0, z: 50.0))
let vertexSource = SCNGeometrySource(vertices: vertices)
var normals = [SCNVector3]()
normals.append(SCNVector3(x: 0.0, y: 1.0, z: 0.0))
normals.append(SCNVector3(x: 0.0, y: 1.0, z: 0.0))
normals.append(SCNVector3(x: 0.0, y: 1.0, z: 0.0))
let normalSource = SCNGeometrySource(normals: normals)
// Indices…
var indices = [0, 1, 2]
let triangles = SCNGeometryElement(indices: indices, primitiveType: .triangles)
let geom = SCNGeometry(sources: [vertexSource, normalSource], elements: [triangles])
let node = SCNNode()
node.geometry = geom
let mat = SCNMaterial()
mat.diffuse.contents = NSColor.green
geom.materials = [mat]
return node
}

Take care with Int32 for the indices
Will not work:
let indices = [0,1,2]
Works:
let indices: [Int32] = [0,1,2]

Related

Why are the shades of black in a custom SCNGeometry lighter than in an SCNSphere

In this scene
the column of spheres on the left are created using SCNSphere()
the column of 'circles' on the right are created using SCNGeometry() using .point primitives
there is only an ambient light source
all geometries are using a .constant lighting model
each pair of spheres uses the same RGB values to define the colour.
Why are the shades of black for the last two pairs lighter for the circles than their equivalient sphere?
Full playground code to reproduce this images:
Creating the scene
import UIKit
import SceneKit
import PlaygroundSupport
// Constants I'm using for the darkest grey colour
let B_RED: CGFloat = 0.05
let B_GREEN: CGFloat = 0.05
let B_BLUE: CGFloat = 0.05
let B_ALPHA: CGFloat = 1.0
let BLACK_COLOUR = UIColor(red: B_RED, green: B_GREEN, blue: B_BLUE, alpha: B_ALPHA)
let scene: SCNScene = {
let s = SCNScene()
return s
}()
let sceneView: SCNView = {
let v = SCNView(frame: CGRect(x: 0, y: 0, width: 600, height: 800))
v.scene = scene
v.backgroundColor = UIColor(white: 0.66, alpha: 1.0)
v.allowsCameraControl = true
v.debugOptions = [SCNDebugOptions.showLightInfluences]
v.backgroundColor
return v
}()
let ambientLigntNode: SCNNode = {
let n = SCNNode()
n.light = SCNLight()
n.light!.type = SCNLight.LightType.ambient
n.light!.color = UIColor(white: 1, alpha: 1.0)
return n
}()
PlaygroundPage.current.liveView = sceneView
// a camera
var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.simdPosition = simd_float3(0,0,8)
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(ambientLigntNode)
The column of spheres
// ----------------------------------------------------
// White Sphere
let whiteSphere = SCNSphere(radius: 0.3)
let whiteMaterial = SCNMaterial()
whiteMaterial.diffuse.contents = simd_float3(1,1,1)
whiteMaterial.lightingModel = .constant
whiteSphere.materials = [whiteMaterial]
let whiteSphereNode = SCNNode(geometry: whiteSphere)
whiteSphereNode.simdPosition = simd_float3(-1,2,0)
scene.rootNode.addChildNode(whiteSphereNode)
// ----------------------------------------------------
// Black Sphere
let blackSphere = SCNSphere(radius: 0.3)
let blackMaterial = SCNMaterial()
blackMaterial.diffuse.contents = BLACK_COLOUR
blackMaterial.lightingModel = .constant
blackSphere.materials = [blackMaterial]
let blackSphereNode = SCNNode(geometry: blackSphere)
blackSphereNode.simdPosition = simd_float3(-1,-2,0)
scene.rootNode.addChildNode(blackSphereNode)
// ----------------------------------------------------
// Red Sphere
let redSphere = SCNSphere(radius: 0.3)
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor(
red: 1.0,
green: 0.0,
blue: 0.0,
alpha: 1.0
)
redMaterial.lightingModel = .constant
redSphere.materials = [redMaterial]
let redSphereNode = SCNNode(geometry: redSphere)
redSphereNode.simdPosition = simd_float3(-1, 1, 0)
scene.rootNode.addChildNode(redSphereNode)
// ----------------------------------------------------
// Green Sphere
let greenSphere = SCNSphere(radius: 0.3)
let greenMaterial = SCNMaterial()
greenMaterial.diffuse.contents = UIColor(
red: 0.0,
green: 1.0,
blue: 0.0,
alpha: 1.0
)
greenMaterial.lightingModel = .constant
greenSphere.materials = [greenMaterial]
let greenSphereNode = SCNNode(geometry: greenSphere)
greenSphereNode.simdPosition = simd_float3(-1, 0, 0)
scene.rootNode.addChildNode(greenSphereNode)
// ----------------------------------------------------
// Blue Sphere
let blueSphere = SCNSphere(radius: 0.3)
let blueMaterial = SCNMaterial()
blueMaterial.diffuse.contents = UIColor(
red: 0.0,
green: 0.0,
blue: 1.0,
alpha: 1.0
)
blueMaterial.lightingModel = .constant
blueSphere.materials = [blueMaterial]
let blueSphereNode = SCNNode(geometry: blueSphere)
blueSphereNode.simdPosition = simd_float3(-1, -1, 0)
scene.rootNode.addChildNode(blueSphereNode)
// ----------------------------------------------------
// Grey Sphere
let greySphere = SCNSphere(radius: 0.3)
let greyMaterial = SCNMaterial()
greyMaterial.diffuse.contents = UIColor(
red: 0.5,
green: 0.5,
blue: 0.5,
alpha: 1.0
)
greyMaterial.lightingModel = .constant
greySphere.materials = [greyMaterial]
let greySphereNode = SCNNode(geometry: greySphere)
greySphereNode.simdPosition = simd_float3(-1, -3, 0)
scene.rootNode.addChildNode(greySphereNode)
Column of circles
// ----------------------------------------------------
// Custom SCNGeometry using vertex data
struct Vertex {
let x: Float
let y: Float
let z: Float
let r: Float
let g: Float
let b: Float
let a: Float
}
let vertices: [Vertex] = [
Vertex(x: 0.0, y: 2.0, z: 0.0, r: 1.0, g: 1.0, b: 1.0, a: 1.0), // white
Vertex(x: 0.0, y: 1.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0, a: 1.0), // red
Vertex(x: 0.0, y: 0.0, z: 0.0, r: 0.0, g: 1.0, b: 0.0, a: 1.0), // green
Vertex(x: 0.0, y: -1.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0, a: 1.0), // blue
Vertex(x: 0.0, y: -3.0, z: 0.0, r: 0.5, g: 0.5, b: 0.5, a: 1.0), // rgb
Vertex(
x: 0.0, y: -2.0, z: 0.0,
r: Float(B_RED), g: Float(B_GREEN), b: Float(B_BLUE), a: Float(B_ALPHA)
)
]
let vertexData = Data(
bytes: vertices,
count: MemoryLayout<Vertex>.size * vertices.count
)
let positionSource = SCNGeometrySource(
data: vertexData,
semantic: SCNGeometrySource.Semantic.vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<Vertex>.size
)
let colourSource = SCNGeometrySource(
data: vertexData,
semantic: SCNGeometrySource.Semantic.color,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 4,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: MemoryLayout<Float>.size * 3,
dataStride: MemoryLayout<Vertex>.size
)
let elements = SCNGeometryElement(
data: nil,
primitiveType: .point,
primitiveCount: vertices.count,
bytesPerIndex: MemoryLayout<Int>.size
)
elements.pointSize = 100
elements.minimumPointScreenSpaceRadius = 100
elements.maximumPointScreenSpaceRadius = 100
let spheres = SCNGeometry(
sources: [positionSource, colourSource],
elements: [elements]
)
let sphereNode = SCNNode(geometry: spheres)
let sphereMaterial = SCNMaterial()
sphereMaterial.lightingModel = .constant
spheres.materials = [sphereMaterial]
sphereNode.simdPosition = simd_float3(0,0,0)
scene.rootNode.addChildNode(sphereNode)
Updated based on accepted answer. Converting from sRGB to LinearRGB gives the same result
func srgbToLinear(x: Float) -> Float {
if x <= 0.04045 {
return x / 12.92;
}
return powf((x + 0.055) / 1.055, 2.4)
}
let vertices: [Vertex] = [
Vertex(x: 0.0, y: 2.0, z: 0.0, r: 1.0, g: 1.0, b: 1.0, a: 1.0), // white
Vertex(x: 0.0, y: 1.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0, a: 1.0), // red
Vertex(x: 0.0, y: 0.0, z: 0.0, r: 0.0, g: 1.0, b: 0.0, a: 1.0), // green
Vertex(x: 0.0, y: -1.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0, a: 1.0), // blue
Vertex(x: 0.0, y: -3.0, z: 0.0, r: srgbToLinear(x: 0.5), g: srgbToLinear(x: 0.5), b: srgbToLinear(x: 0.5), a: 1.0), // rgb
Vertex(
x: 0.0, y: -2.0, z: 0.0,
r: srgbToLinear(x: Float(B_RED)),
g: srgbToLinear(x: Float(B_GREEN)),
b: srgbToLinear(x: Float(B_GREEN)),
a: 1.0
)
]
This would be the case if the two types of graphic are generated in different color spaces. For example, if gray for the spheres is interpreted as being in the sRGB as the color space, and the circle are interpreted as generic RGB space then you would see a different in color values.
Consider the following playground code:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
import CoreGraphics
class MyViewController : UIViewController {
override func loadView() {
let view = CustomView()
view.backgroundColor = UIColor.darkGray
self.view = view
}
}
class CustomView : UIView {
override func draw(_ rect: CGRect) {
if let cgContext = UIGraphicsGetCurrentContext() {
cgContext.saveGState()
let gray : [CGFloat] = [0.5, 0.5, 0.5, 1.0]
let srgbGray = CGColor(
colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, components: gray)!
cgContext.setFillColor(srgbGray)
cgContext.fillEllipse(in: CGRect(x:10, y:20, width:72, height: 72))
let genericGray = CGColor(colorSpace: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, components: gray)!
cgContext.setFillColor(genericGray)
cgContext.fillEllipse(in: CGRect(x:110, y:20, width:72, height: 72))
cgContext.restoreGState()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
It produces an effect almost identical to the one you are seeing. I suspect that the circles are drawn assuming a generic color space and the spheres are drawn assuming sRGB.

physically based lighting on custom SCNGeometry Node

Question
How do you define the material on a custom geometry from vertex data, so that it renders the same as 'typical' SCNNodes?
Details
In this scene there are
A directional light
A red sphere using physicallybased lighting model
A blue sphere using physicallybased lighting model
A custom SCNGeometry using vertex data, using a physicallybased lighting model
The red and blue spheres render as I would expect. The two points / spheres in the custom geometry are black.
Why?
Here is the playgrond code:
Setting the scene
import UIKit
import SceneKit
import PlaygroundSupport
// create a scene view with an empty scene
var sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
var scene = SCNScene()
sceneView.scene = scene
sceneView.backgroundColor = UIColor(white: 0.75, alpha: 1.0)
sceneView.allowsCameraControl = true
PlaygroundPage.current.liveView = sceneView
let directionalLightNode: SCNNode = {
let n = SCNNode()
n.light = SCNLight()
n.light!.type = SCNLight.LightType.directional
n.light!.color = UIColor(white: 0.75, alpha: 1.0)
return n
}()
directionalLightNode.simdPosition = simd_float3(0,5,0) // Above the scene
directionalLightNode.simdOrientation = simd_quatf(angle: -90 * Float.pi / 180.0, axis: simd_float3(1,0,0)) // pointing down
scene.rootNode.addChildNode(directionalLightNode)
// a camera
var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.simdPosition = simd_float3(0,0,5)
scene.rootNode.addChildNode(cameraNode)
Adding the blue and red spheres
// ----------------------------------------------------
// Example creating SCNSphere Nodes directly
// Sphere 1
let sphere1 = SCNSphere(radius: 0.3)
let sphere1Material = SCNMaterial()
sphere1Material.diffuse.contents = UIColor.red
sphere1Material.lightingModel = .physicallyBased
sphere1.materials = [sphere1Material]
let sphere1Node = SCNNode(geometry: sphere1)
sphere1Node.simdPosition = simd_float3(-2,0,0)
// Sphere2
let sphere2 = SCNSphere(radius: 0.3)
let sphere2Material = SCNMaterial()
sphere2Material.diffuse.contents = UIColor.blue
sphere2Material.lightingModel = .physicallyBased
sphere2.materials = [sphere2Material]
let sphere2Node = SCNNode(geometry: sphere2)
sphere2Node.simdPosition = simd_float3(-1,0,0)
scene.rootNode.addChildNode(sphere1Node)
scene.rootNode.addChildNode(sphere2Node)
Adding the custom SCNGeometry
// ----------------------------------------------------
// Example creating SCNGeometry using vertex data
struct Vertex {
let x: Float
let y: Float
let z: Float
let r: Float
let g: Float
let b: Float
}
let vertices: [Vertex] = [
Vertex(x: 0.0, y: 0.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0),
Vertex(x: 1.0, y: 0.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0)
]
let vertexData = Data(
bytes: vertices,
count: MemoryLayout<Vertex>.size * vertices.count
)
let positionSource = SCNGeometrySource(
data: vertexData,
semantic: SCNGeometrySource.Semantic.vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<Vertex>.size
)
let colorSource = SCNGeometrySource(
data: vertexData,
semantic: SCNGeometrySource.Semantic.color,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: MemoryLayout<Float>.size * 3,
dataStride: MemoryLayout<Vertex>.size
)
let elements = SCNGeometryElement(
data: nil,
primitiveType: .point,
primitiveCount: vertices.count,
bytesPerIndex: MemoryLayout<Int>.size
)
elements.pointSize = 100
elements.minimumPointScreenSpaceRadius = 100
elements.maximumPointScreenSpaceRadius = 100
let spheres = SCNGeometry(sources: [positionSource, colorSource], elements: [elements])
let sphereNode = SCNNode(geometry: spheres)
let sphereMaterial = SCNMaterial()
sphereMaterial.lightingModel = .physicallyBased
spheres.materials = [sphereMaterial]
sphereNode.simdPosition = simd_float3(0,0,0)
scene.rootNode.addChildNode(sphereNode)
Some Exploration
Adding normals now shows the colours, but in all directions (i.e, there's no shadow).
And I've added a black SCNSphere() and a 3rd point to my VertexData, both using the same RGB values, but the black in the VertexData object appears too 'light'
let vertices: [Vertex] = [
Vertex(x: 0.0, y: 0.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0),
Vertex(x: 1.0, y: 0.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0),
Vertex(x: 0.0, y: 1.0, z: 0.0, r: 0.07, g: 0.11, b: 0.12)
]
let vertexData = Data(
bytes: vertices,
count: MemoryLayout<Vertex>.size * vertices.count
)
let normals = Array(repeating: SCNVector3(1,1,1), count: vertices.count)
let normalSource = SCNGeometrySource(normals: normals)
///
///
let spheres = SCNGeometry(
sources: [
positionSource,
normalSource,
colorSource
],
elements: [elements]
)
According to the documentation, making a custom geometry takes 3 steps.
Create a SCNGeometrySource that contains the 3D shape's vertices.
Create a SCNGeometryElement that contains an array of indices, showing how the vertices connect.
Combine the SCNGeometrySource source and SCNGeometryElement into a SCNGeometry.
Let's start from step 1. You want your custom geometry to be a 3D shape, right? You only have 2 vertices, though.
let vertices: [Vertex] = [ /// what's `r`, `g`, `b` for btw?
Vertex(x: 0.0, y: 0.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0),
Vertex(x: 1.0, y: 0.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0)
]
This will form a line...
A common way of making 3D shapes is from triangles. Let's add 2 more vertices to make a pyramid.
let vertices: [Vertex] = [
Vertex(x: 0.0, y: 0.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0), /// vertex 0
Vertex(x: 1.0, y: 0.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0), /// vertex 1
Vertex(x: 1.0, y: 0.0, z: -0.5, r: 0.0, g: 0.0, b: 1.0), /// vertex 2
Vertex(x: 0.0, y: 1.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0), /// vertex 3
]
Now, we need to convert the vertices into something that SceneKit can handle. In your current code, you convert vertices into Data, then use the init(data:semantic:vectorCount:usesFloatComponents:componentsPerVector:bytesPerComponent:dataOffset:dataStride:) initializer.
let vertexData = Data(
bytes: vertices,
count: MemoryLayout<Vertex>.size * vertices.count
)
let positionSource = SCNGeometrySource(
data: vertexData,
semantic: SCNGeometrySource.Semantic.vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<Vertex>.size
)
This is very advanced and complicated. It's way easier with init(vertices:).
let verticesConverted = vertices.map { SCNVector3($0.x, $0.y, $0.z) } /// convert to `[SCNVector3]`
let positionSource = SCNGeometrySource(vertices: verticesConverted)
Now that you've got the SCNGeometrySource, it's time for step 2 — connecting the vertices via SCNGeometryElement. In your current code, you use init(data:primitiveType:primitiveCount:bytesPerIndex:), then pass in nil...
let elements = SCNGeometryElement(
data: nil,
primitiveType: .point,
primitiveCount: vertices.count,
bytesPerIndex: MemoryLayout<Int>.size
)
If the data itself is nil, how will SceneKit know how to connect your vertices? But anyway, there's once again an easier initializer: init(indices:primitiveType:). This takes in an array of FixedWidthInteger, each representing a ​vertex back in your positionSource.
So how is each vertex represented by a FixedWidthInteger? Well, remember how you passed in verticesConverted, an array of SCNVector3, to positionSource? SceneKit sees each FixedWidthInteger as an index and uses it access verticesConverted.
Since indices are always integers and positive, UInt16 should do fine (it conforms to FixedWidthInteger).
/// pairs of 3 indices, each representing a vertex
let indices: [UInt16] = [
​0, 1, 3, /// front triangle
​1, 2, 3, /// right triangle
​2, 0, 3, /// back triangle
​3, 0, 2, /// left triangle
​0, 2, 1 /// bottom triangle
]
let element = SCNGeometryElement(indices: indices, primitiveType: .triangles)
The order here is very specific. By default, SceneKit only renders the front face of triangles, and in order to distinguish between the front and back, it relies on your ordering. The basic rule is: counterclockwise means front.
So to refer to the first triangle, you could say:
​0, 1, 3
1, 3, 0
3, 0, 1
All are fine. Finally, step 3 is super simple. Just combine the SCNGeometrySource and SCNGeometryElement.
let geometry = SCNGeometry(sources: [positionSource], elements: [element])
And that's it! Now that both your SCNGeometrySource and SCNGeometryElement are set up correctly, lightingModel will work properly.
/// add some color
let material = SCNMaterial()
material.diffuse.contents = UIColor.orange
material.lightingModel = .physicallyBased
geometry.materials = [material]
/// add the node
let node = SCNNode(geometry: geometry)
scene.rootNode.addChildNode(node)
Notes:
I noticed that you were trying to use 2 SCNGeometrySources. The second one was to add color with SCNGeometrySource.Semantic.color, right? The simpler initializer that I used, init(vertices:), defaults to .vertex. If you want per-vertex color or something, you'll probably need to go back to init(data:semantic:vectorCount:usesFloatComponents:componentsPerVector:bytesPerComponent:dataOffset:dataStride:).
Try sceneView.autoenablesDefaultLighting = true for some better lighting
Full demo playground here
Edit: Single Vertex Sphere?
You shouldn't be using a single point to make a sphere. If you're going to do...
elements.pointSize = 100
elements.minimumPointScreenSpaceRadius = 100
elements.maximumPointScreenSpaceRadius = 100
... then a 2D Circle is going to be the best you can get.
That's because, according to the pointSize documentation:
SceneKit can render each point as a small 2D surface that always faces the camera. By applying a texture or custom shader to that surface, you can efficiently render many small objects at once.
Since what's rendered is really just a circle that rotates to face you, .physicallyBased lighting won't work (.constant will, but that's it). It's better to make your sphere with many small triangles, like the pyramid in the above answer. This is also what Apple does with their built in geometry, including SCNSphere.
let sphere = SCNSphere(radius: 1)
let sphereMaterial = SCNMaterial()
sphereMaterial.diffuse.contents = UIColor.purple
sphereMaterial.fillMode = .lines /// add this to see the triangles
sphereMaterial.lightingModel = .physicallyBased
sphere.materials = [sphereMaterial]
let sphereNode = SCNNode(geometry: sphere)
scene.rootNode.addChildNode(sphereNode)

swift SceneKit Sphere deformation after changing position

I have two spheres on the sceneView
let earthSphere = SCNSphere(radius: 5.0)
earthSphere.firstMaterial?.diffuse.contents = UIColor.green
let objNode1 = SCNNode(geometry: earthSphere)
objNode1.position = SCNVector3(x: 0, y: 0, z: 0)
let moonSphere = SCNSphere(radius: 0.5)
moonSphere.firstMaterial?.diffuse.contents = UIColor.red
let objNode2 = SCNNode(geometry: moonSphere)
objNode2.position = SCNVector3(x: 3, y: 3, z: 0)
but moonSphere is deforming image. How to fix that ? It is possible to do that earthSphere and moonSphere look same ? without deformation
etc: cameraNode set to position SCNVector3(x: 0, y: 0, z: 7)

SceneKit custom geometry produces “double not supported” / “invalid vertex format” runtime error

I don't understand what's wrong with the following code:
class Terrain {
private class func createGeometry () -> SCNGeometry {
let sources = [
SCNGeometrySource(vertices:[
SCNVector3(x: -1.0, y: -1.0, z: 0.0),
SCNVector3(x: -1.0, y: 1.0, z: 0.0),
SCNVector3(x: 1.0, y: 1.0, z: 0.0),
SCNVector3(x: 1.0, y: -1.0, z: 0.0)], count:4),
SCNGeometrySource(normals:[
SCNVector3(x: 0.0, y: 0.0, z: -1.0),
SCNVector3(x: 0.0, y: 0.0, z: -1.0),
SCNVector3(x: 0.0, y: 0.0, z: -1.0),
SCNVector3(x: 0.0, y: 0.0, z: -1.0)], count:4),
SCNGeometrySource(textureCoordinates:[
CGPoint(x: 0.0, y: 0.0),
CGPoint(x: 0.0, y: 1.0),
CGPoint(x: 1.0, y: 1.0),
CGPoint(x: 1.0, y: 0.0)], count:4)
]
let elements = [
SCNGeometryElement(indices: [0, 2, 3, 0, 1, 2], primitiveType: .Triangles)
]
let geo = SCNGeometry(sources:sources, elements:elements)
let mat = SCNMaterial()
mat.diffuse.contents = UIColor.redColor()
mat.doubleSided = true
geo.materials = [mat, mat]
return geo
}
class func createNode () -> SCNNode {
let node = SCNNode(geometry: createGeometry())
node.name = "Terrain"
node.position = SCNVector3()
return node
}
}
I use it as follows:
let terrain = Terrain.createNode()
sceneView.scene?.rootNode.addChildNode(terrain)
But get:
2016-01-19 22:21:17.600 SceneKit: error, C3DRendererContextSetupResidentMeshSourceAtLocation - double not supported
2016-01-19 22:21:17.601 SceneKit: error, C3DSourceAccessorToVertexFormat - invalid vertex format
/BuildRoot/Library/Caches/com.apple.xbs/Sources/Metal/Metal-55.2.6.1/Framework/MTLVertexDescriptor.mm:761: failed assertion `Unused buffer at index 18.'
The issue is that the geometry is expecting float components but you’re giving it doubles—CGPoint’s components are CGFloat values, which are typedef’d to double on 64-bit systems. Unfortunately, the SCNGeometrySource …textureCoordinates: initializer insists on CGPoints, so you can’t use that; the workaround I found was to create an NSData wrapping an array of SIMD float vectors, then use the much longer data:semantic:etc: initializer to consume the data. Something like this should do the trick:
let coordinates = [float2(0, 0), float2(0, 1), float2(1, 1), float2(1, 0)]
let coordinateData = NSData(bytes:coordinates, length:4 * sizeof(float2))
let coordinateSource = SCNGeometrySource(data: coordinateData,
semantic: SCNGeometrySourceSemanticTexcoord,
vectorCount: 4,
floatComponents: true,
componentsPerVector: 2,
bytesPerComponent: sizeof(Float),
dataOffset: 0,
dataStride: sizeof(float2))

SceneKit Swift - PhysicsBody not precise

Hello,
I'm making a game with SceneKit in Swift.
In this game I have first cube1 that is on a second cube2 (that is the ground).
PROBLEM 1
The cube1's PhysicsBodyType is Kinematic, while the second is Static.
I used Kinematic for the first cube because when I use Dynamic, the PhysicsBody is not precise as you can see on the image :
While when I use Kinematic, the PhysicsBody is right :
Do you know why the PhysicsBody is not precise ?
PROBLEM 2
I want to apply vertical force (y) to the cube1 but it is impossible to do with Kinematic type, so I tried to use SCNAction.moveBy but the movement is not smooth.
Have you a solution for this ?
CODE
CUBE 1 :
let cubeGeometry = SCNBox(width: cubeSize, height: cubeSize, length: cubeSize, chamferRadius: 0.0)
cubeGeometry.firstMaterial?.diffuse.contents = UIColor(netHex: 0xF1C40F)
cubeNode = SCNNode(geometry: cubeGeometry)
cubeNode.position = SCNVector3(x: 0.0, y: 0.0, z: 0.0)
let cubeShape = SCNPhysicsShape(geometry: cubeGeometry, options: nil)
cubeNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Dynamic, shape: cubeShape)
cubeNode.physicsBody?.angularVelocityFactor = SCNVector3(0.0, 1.0, 0.0)
scene.rootNode.addChildNode(cubeNode)
CUBE 2 :
for index in 0...10 {
let cubeGeometry = SCNBox(width: cubeSize, height: cubeSize, length: cubeSize, chamferRadius: 0.0)
let randomColor = Int(arc4random_uniform(UInt32(colorsArray.count)))
cubeGeometry.firstMaterial?.diffuse.contents = colorsArray[randomColor]
let cubeGround = SCNNode(geometry: cubeGeometry)
cubeGround.position = SCNVector3(x: 0.0, y: -Float(cubeSize), z: Float(index) * -Float(cubeSize))
cubeGround.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Static, shape: SCNPhysicsShape(node: cubeGround, options: nil))
cubeGround.physicsBody?.angularVelocityFactor = SCNVector3(0.0, 0.0, 0.0)
scene.rootNode.addChildNode(cubeGround)
}