0

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...

Guig
  • 9,891
  • 7
  • 64
  • 126
  • https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSValue_Class/: *"An NSValue object is a simple container for a single C or Objective-C data item. ..."* NSValue cannot hold an array. What are do you need it for? – Martin R Sep 09 '16 at 08:43
  • Pass an array of vectors to a GL shader (`uniform vec4 u_points[7];` in my shader code) It might be possible to pass a vector 28 here instead of 7 vector 4, even though I'm not 100% sure. How would you go about creating an `NSValue` that holds those 28 values? – Guig Sep 09 '16 at 08:46
  • The docs says that NSValue can work with a pointers. Can you not have a pointer with an array? – Guig Sep 09 '16 at 08:47
  • Sure, you can pass a pointer in an NSValue, but it is *your* responsibility to guarantee the lifetime of the pointed-to data. – I am not familiar with GL shaders, so I probably cannot help here. But I would suggest that you add all relevant information about the actual use case to the question itself. – Martin R Sep 09 '16 at 08:49
  • Yes, I was trying to keep the question focused, but I probably needs to add a bit – Guig Sep 09 '16 at 08:52
  • Again, I am not expert for this, but note that `setValue:forKey` does not require an NSValue argument, it takes an AnyObject. So passing an (NS)Array of NSValue might work. – Martin R Sep 09 '16 at 09:10
  • Thanks. I've tried a number of combinations and filled a bug for apple documentation on that. – Guig Sep 09 '16 at 19:40
  • 1
    When you get to dealing with shader-language arrays, it's more relevant that you're passing an arbitrary bag of bytes down to the GPU than what CPU-side data type is in it. I haven't tested myself, but you might be able to try making a (Swift) `Array` of `SCNVector4`s, creating an `NSData` from that, and passing the `NSData` to the shader. – rickster Sep 09 '16 at 20:34

1 Answers1

0

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.

OOPer
  • 47,149
  • 6
  • 107
  • 142