SCNGeometry is difficult to instantiate

Hey. I've combed through the seriously weak documentation, looked for examples (none of which are in Swift) I found an Obj-C example and converted it. It doesn't work. It creates an empty hierarchy of SCNNodes. What is going on here? this is the third time, I've written SCNGeometry code from scratch, using examples from different sources, trying to follow the insanely arcane, undocumented, and openGL-ish design pattern. This time, I've taken Obj-C code, and simply converted it, leaving everything as close to the original as possible.


what is going on?


// BKPivotNode is a subclass of SCNNode.



func genTubeTest(aPivot: BKPivotNode)->SCNNode{

var retNode = SCNNode()

if aPivot.children.isEmpty != true{

for aChild in aPivot.childNodes{

if let bChild = aChild as? BKPivotNode {

let sources = [SCNGeometrySource(vertices: [ SCNVector3(x: -1.0, y: -1.0, z: 0),

SCNVector3(x: -1.0, y: 1.0, z: 0),

SCNVector3(x: 1.0, y: 1.0, z: 0),

SCNVector3(x: 1.0, y: -1.0, z: 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 newElement = SCNGeometryElement(data: NSData(bytes: [0, 2, 3,0,1,2], length: sizeof(Int)), primitiveType: .Triangles, primitiveCount: 2, bytesPerIndex: sizeof(Int))

let newGeo = SCNGeometry(sources:sources, elements:[newElement])


let newMat = SCNMaterial()

newMat.diffuse.contents = NSColor.redColor()

newMat.transparency = 0.5

newMat.doubleSided = true

newGeo.firstMaterial = newMat;


let newNode = SCNNode()

newNode.geometry = newGeo

retNode.addChildNode(newNode)


}

}

}

return retNode

}

This line looks like it might have a problem:


let newElement = SCNGeometryElement(data: NSData(bytes: [0, 2, 3,0,1,2], length: sizeof(Int)),
                    primitiveType: .Triangles, primitiveCount: 2, bytesPerIndex: sizeof(Int))


The NSData instance you are creating will only have a length of sizeof(Int), and later the bytesPerIndex: is also sizeof(Int) but I think you expect the data to have 6 index values in it.



If fixing that doesn't fix the problem, it would help to know which example you converting, so that we can see the original obj-c code.

And what does the BKPivotNode instance method children() return? Just the childNodes array?

children() returns a specific list of child nodes. subclasses with specific utlity.


this is what I updated that line to:

let nums : [Int] = [0, 2, 3,0,1,2]

let newElement = SCNGeometryElement(data: NSData(bytes: nums, length: sizeofValue(nums)), primitiveType: .Triangles , primitiveCount: 2, bytesPerIndex: 6)


the results are still negative. the method is called, the geometry object is not instantiated, no errors reported.



original Obj-c method:

- (SCNGeometry *) createGeometry{


NSArray *sources = @[

[SCNGeometrySource geometrySourceWithVertices: (SCNVector3[]){

{.x = -1.0f, .y = -1.0f, .z = 0.0f},

{.x = -1.0f, .y = 1.0f, .z = 0.0f},

{.x = 1.0f, .y = 1.0f, .z = 0.0f},

{.x = 1.0f, .y = -1.0f, .z = 0.0f}

} count:4],

[SCNGeometrySource geometrySourceWithNormals:(SCNVector3[]){

{.x = 0.0f, .y = 0.0f, .z = -1.0f},

{.x = 0.0f, .y = 0.0f, .z = -1.0f},

{.x = 0.0f, .y = 0.0f, .z = -1.0f},

{.x = 0.0f, .y = 0.0f, .z = -1.0f}

} count:4],

[SCNGeometrySource geometrySourceWithTextureCoordinates:(CGPoint[]){

{.x = 0.0f, .y = 0.0f},

{.x = 0.0f, .y = 1.0f},

{.x = 1.0f, .y = 1.0f},

{.x = 1.0f, .y = 0.0f}

} count:4]

];


NSArray *elements = @[

[SCNGeometryElement geometryElementWithData:[NSData dataWithBytes:(short[]){0, 2, 3,0,1,2} length:sizeof(short[6])]

primitiveType:SCNGeometryPrimitiveTypeTriangles

primitiveCount:2

bytesPerIndex:sizeof(short)]];


SCNGeometry *geo = [SCNGeometry geometryWithSources:sources elements:elements];


SCNMaterial *mat = [SCNMaterial material];

mat.diffuse.contents = [NSColor redColor];


geo.firstMaterial = mat;

geo.firstMaterial.doubleSided = YES;


return geo;


}

The bytesPerIndex: parameter wants the size of each of the six index values.


let nums : [Int] = [0, 2, 3, 0, 1, 2]
let newElement = SCNGeometryElement(data: NSData(bytes: nums, length: 6*sizeof(Int)),
    primitiveType: .Triangles , primitiveCount: 2, bytesPerIndex: sizeof(Int))



Even before that change, though, separating out the geometry creation seems to show that part working (in OSX playground in Xcode 7 beta 3):

func testGeometry() -> SCNGeometry
{
    let sources = [SCNGeometrySource(vertices: [ SCNVector3(x: -1.0, y: -1.0, z: 0),
        SCNVector3(x: -1.0, y: 1.0, z: 0),
        SCNVector3(x: 1.0, y: 1.0, z: 0),
        SCNVector3(x: 1.0, y: -1.0, z: 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 nums : [Int] = [0, 2, 3, 0, 1, 2]
    let newElement = SCNGeometryElement(data: NSData(bytes: nums, length: 6*sizeof(Int)),
          primitiveType: .Triangles , primitiveCount: 2, bytesPerIndex: sizeof(Int))
    let newGeo = SCNGeometry(sources:sources, elements:[newElement])

    let newMat = SCNMaterial()
    newMat.diffuse.contents = NSColor.redColor()
    newMat.transparency = 0.5
    newMat.doubleSided = true
    newGeo.firstMaterial = newMat

    return newGeo
}

let x = testGeometry()    // <SCNGeometry: 0x7ffc8170c110>
x.geometryElementCount    // 1
x.firstMaterial            // <SCNMaterial: 0x7ffc81671710


So, if your nodes are empty, the problem must be in this part:

func genTubeTest(aPivot: BKPivotNode) -> SCNNode
{
    var retNode = SCNNode()
    if aPivot.children.isEmpty != true
    {
        for aChild in aPivot.childNodes
        {
            if let bChild = aChild as?  BKPivotNode
            {
                let newGeo = testGeometry()
             
                let newNode = SCNNode()
                newNode.geometry = newGeo
                retNode.addChildNode(newNode)
             
            }
        }
    }
    return retNode
}


How are you testing the output of genTubeTest() ?


Are you sure that the BKPivotNode you pass in has childNodes which are also BKPivotNode instances, and that children isn't returning an empty array?

i get a geometry object of 0x0 when I run that method. it's not instantiated.

xcode 7 b3 as well. not in a playground, however.


as for how I'm testing it, I have 3 test cases.

a single pivot node.

a hierarchy of two pivot nodes

and then a much more complicated setup that adds 3 children to the child pivot in the two pivot hierarchy. All done programatically, all built and tested an awful lot before I started tackling SCNGeometry.


when I test, I step through the function. when i hit the "if let bChild = aChild as? BKPivotNode" line, I follow it down into the brackets, and then onto the test function. everything is getting called. I get a 0x0 geometry object.


I'll try the element tip, see what that does, but newElement as written instantiates.


update: no difference.

I think I'll try messing around with a playground, this might require something with a little lessoverhead to figure out.


thanks.

-td

in the playground the geometry object has an address. I've gone back to the application and taken a closer look, and indeed the geometry in the app has an address. but none of it's properties do. everything is 0x0. This must be why I'm not getting any crashes, but it still amounts to the same thing: SCNGeometry does not instantiate. and those results are reflected in the playground.


the geometry object... renders nothing.

this code renders a nice bright sphere in the playground:

let aPurpleSphere = SCNSphere(radius: 0.05)
let aMat = SCNMaterial()
let con = CGColorCreateGenericRGB(1.0, 0.0, 1.0, 1.0)
aMat.emission.contents = con
aMat.diffuse.contents = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0)
aPurpleSphere.materials = [aMat]

You might want to try posting in the SceneKit section: https://forums.developer.apple.com/community/graphics-and-games/scenekit


Sorry I can't be of more help, but hopefully someone who has more experience with this in ScenKit will step in and be able to help you figure it out.

thnx.

i wasn't aware of a scene kit forum. will go there now.

half tempted to set your post as the "correct" answer.

i will say this:

it seems there are problems with the swift implemenation of the SCNGeometryElement init method. the bytesPerIndex value only allows 1,2,and 4 as values. that is contrary to everything discussed in this thread and everything i could find documentation-wise, which frankly isn't much. APPLE GET ON THE DOCS. WE'RE SUFFERING FROM GUESSWORK HERE!

APPLE GET ON THE DOCS. WE'RE SUFFERING FROM GUESSWORK HERE!

It's a little known fact that you can file bugs against the documentation in exactly the same way you file bugs against the system software. If you want your feedback to be heard by the folks who are responsible for addressing it, that's your best way forward.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

guys why are you sending in four verts for a triangle ?

SCNGeometry is difficult to instantiate
 
 
Q