Swift - Array of SCNVector4 to NSValue?

651 views Asked by At

How can I convert an array of vector to an NSValue? There is a convenience initiation for a single SCNVector4 for instance, but how can I use that for an array?

Like:

let v1 = SCNVector4(x: 0.0, y: 0.0, z: 0.0, w: 0.0)
let v2 = SCNVector4(x: 1.0, y: 0.0, z: 0.0, w: 0.0)

let value = NSValue(scnVector4: v1) // that works
let arrayValue = NSValue(scnVectors4: [v1, v2]) // that obviously doesnt work :( What will work?

=== context ===

I want to pass points to a shader, using a shader modifier (within SceneKit). The code in the shader is:

uniform vec4 u_points[7];

I'm trying to pass the points to the shader doing:

material.setValue(points, forKey: "u_points") // points is a [SCNVector4] with length 7

This doesn't work. If I had a single vector (uniform vec4 u_point;), I can do:

material.setValue(NSValue(vec4: points[0]), forKey: "u_point")

and that works. Similarly, if the shader is using an array of length 1 (ie uniform vec4 u_points[1];), the previous binding would also work. So it sounds like if I can pass a vector of length 4x7=28 instead of an array, I should be good

==== tries ====

1/ NSArray of NSNumber

As mentioned in the comments, setValue can takes anything in input, so I tried with an NSArray of NSNumber:

let points = [v1, v2]
let a = points.map({ return [$0.x, $0.y, $0.z] })
.flatMap({ return $0 })
  .map({ return NSNumber(value: $0) }) // a is [NSNumber] of length 2x4 = 8

material.setValue(NSArray(array: a), forKey: "u_points")

but that doesn't work

2/ NSArray of SCNVector3: didn't work

3/ NSArray of NSValue of SCNVector3: didn't work

4/ NSArray of NSArray of NSNumber: didn't work

It'd be great if Apple had some kind of good documentation...

1

There are 1 answers

0
OOPer On

I'm not sure this would solve your issue, but NSValue can contain some fixed sized array.

let v1 = SCNVector4(x: 0.0, y: 0.0, z: 0.0, w: 0.0)
let v2 = SCNVector4(x: 1.0, y: 0.0, z: 0.0, w: 0.0)

let value = NSValue(scnVector4: v1) // that works
let arrayValue = NSValue(bytes: [v1, v2], objCType: "[2{SCNVector4=ffff}]")

Check the reference of NSValue class, and Type Encodings.

init(bytes:objCType:)

Declaration

init(bytes value: UnsafeRawPointer,  
     objCType type: UnsafePointer<Int8>)

Parameters - value | A pointer to data to be stored in the new value object.

type | The Objective-C type of value, as provided by the @encode() compiler directive. Do not hard-code this parameter as a C string.

You may need to hard code the type, as @encode() directive is not available in Swift.