Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SceneKit – Custom geometry does not show up

I should see 2 yellow triangles, but I see nothing.

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)
        ]

        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.yellowColor()
        mat.doubleSided = true
        geo.materials = [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)


let camera = SCNCamera()
camera.zFar = 10000
self.camera = SCNNode()
self.camera.camera = camera
self.camera.position = SCNVector3(x: -20, y: 15, z: 30)
let constraint = SCNLookAtConstraint(target: terrain)
constraint.gimbalLockEnabled = true
self.camera.constraints = [constraint]

sceneView.scene?.rootNode.addChildNode(self.camera)

I get other nodes with non-custom geometry which I see. What's wrong?

like image 574
Stéphane de Luca Avatar asked Jan 20 '16 10:01

Stéphane de Luca


2 Answers

Hal Mueller is quite correct in that the indices involved must be a specified type, but it should be noted that this functionality has changed significantly in recent versions of the Swift language. Notably, SCNGeometryElement(indices:, primitiveType:) now functions perfectly well in Swift 4 and I would advise against using CInt which did not work for me. Instead use one of the standard integer types that conforms to the FixedWidthInteger protocol, i.e. Int32. If you know there's a maximum number of vertices involved in your mesh, use the smallest bit size you can that will encompass all of them.

Example:

    let vertices = [
        SCNVector3(x: 5, y: 4, z: 0),
        SCNVector3(x: -5 , y: 4, z: 0),
        SCNVector3(x: -5, y: -5, z: 0),
        SCNVector3(x: 5, y: -5, z: 0)
    ]

    let allPrimitives: [Int32] = [0, 1, 2, 0, 2, 3]
    let vertexSource = SCNGeometrySource(vertices: vertices)
    let element = SCNGeometryElement(indices: allPrimitives, primitiveType: .triangles)
    let geometry = SCNGeometry(sources: [vertexSource], elements: [element])

    SCNNode(geometry: geometry)

What's Happening Here?

First we create an array of vertices describing points in three-dimensional space. The allPrimitives array describes how those vertices link up. Each element is an index from the vertices array. Since we're using triangles, these should be considered in groups of three, one for each corner. For simplicity's sake I've done a simple flat square here. We then create a geometry source with the semantic type of vertices using the original array of all vertices, and a geometry element using the allPrimitives array, also informing it that they are triangles so it knows to group them in threes. These can then be used to create the SCNGeometry object with which we initialise our SCNNode.

An easy way to think about it is that the vertex source exists only to list all the vertices in the object. The geometry element exists only to describe how those vertices are linked up. It's the SCNGeometry that combines these two objects together to create the final physical representation.

like image 90
Ash Avatar answered Nov 13 '22 04:11

Ash


Note: see Ash's answer, which is a much better approach for modern Swift than this one.

Your index array has the wrong size element. It's being inferred as [Int]. You need [CInt].

I broke out your elements setup into:

    let indices = [0, 2, 3, 0, 1, 2] // [Int]
    print(sizeof(Int))               // 8
    print(sizeof(CInt))              // 4
    let elements = [
        SCNGeometryElement(indices: indices, primitiveType: .Triangles)
    ]

To get the indices to be packed like the expected C array, declare the type explicitly:

    let indices: [CInt] = [0, 2, 3, 0, 1, 2]

Custom SceneKit Geometry in Swift on iOS not working but equivalent Objective C code does goes into more detail, but it's written against Swift 1, so you'll have to do some translation.

SCNGeometryElement(indices:, primitiveType:) doesn't appear to be documented anywhere, although it does appear in the headers.

like image 39
Hal Mueller Avatar answered Nov 13 '22 05:11

Hal Mueller