4

I have this app that just works in landscape.

I have an object on scenekit. That object is at a certain position, specified by:

 SCNNode * buttonRef = [scene.rootNode childNodeWithName:@"buttonRef" recursively:YES];
 SCNVector3 buttonRefPosition = [buttonRef position];

Now I need to convert that SCNVector3 to mainView 2D coordinates, where

 mainView = (SCNView *)self.view;

The scene is using orthogonal camera, so no perspective. The camera is the default camera. No rotation, no translation. Camera's orthographicScale is 5.4.

How do I convert that?

I have found other answers on SO about the reverse question, that is converting from CGPoint to SCNVector3 but doesn't sound obvious to me how to do the reverse, from SCNVector3 to CGPoint.

If the SCNView is a subclass of UIView, than (0,0) is the upper left point and in the case of the iPhone 6 landscape, (667, 375) is the lower right point.

This button of mine is at almost full right and middle of height. In terms of UIKit I would say that this button is at (600, 187) but when I do this:

SCNNode * buttonRef = [scene.rootNode childNodeWithName:@"buttonRef" recursively:YES];
SCNVector3 buttonRefPosition = [buttonRef position];

SCNVector3 projectedPoint = [mainView projectPoint: buttonRefPosition];

I get projectPoint equal to (7.8, 375, 0) (?)

and when I add a 10x10 view to the coordinate (7.8, 375) using UIKit, I get a view at the lower left!!

Like I said the app is operating in landscape only but these coordinate systems are all messed.

Duck
  • 34,902
  • 47
  • 248
  • 470
  • Is the `buttonRef` node geometry a SceneKit object (`SCNSphere`, `SCNBox`, etc) or something created in a 3D modelling tool and loaded as a dae file? All the SceneKit objects have an origin at 0,0,0 (by that I mean a sphere's centre is located at 0,0,0), it may be that the model you're using has geometry that is not centred about 0. A quick way to check would be to add a small `SCNSphere` as a child of your buttonRef node and confirm it appears where you think the origin of your geometry is. FWIW `projectPoint` returns expected 2D coords in my app. – lock May 31 '16 at 05:12
  • Your comment makes sense. To test that I did this `[scnView projectPoint:SCNVector3Zero];` or in other words I am projecting 0,0,0. The projection gives me (0, 375, 0). How can 0,0,0 on scenekit – that in my case is what is visible in the exact center of the camera be (0,375,0) or in fact (0,375) on UIKit? ... that is a point on the right down? – Duck May 31 '16 at 12:46
  • Good test, I added that into my project, even switched to a orthographic projection and still got a 2D point in the middle of my view. Is it possible that your SCNView is much larger than the screen size? What does printing out your SCNView's frame/bounds give (in didLayoutView)? If your view was twice as wide as the screen, with the left half out of view, then that would explain the numbers you're getting. – lock Jun 01 '16 at 02:16
  • that gives me (0,0,667,375) as expected. But anyway, like I said scenekit coordinate 0,0,0 that should be (333,187) is being reported back as (0,375) that is a total nonsense. – Duck Jun 01 '16 at 03:43
  • Agreed, somethings not right and I'm out of ideas. – lock Jun 01 '16 at 03:57
  • I will try to create a stripped version of that project and upload somewhere, so you can investigate if you want. Thanks for your effort. – Duck Jun 01 '16 at 04:04
  • Yeah, I'll have a quick look at it. Worth doing even if it only ends up being used in a bug report to Apple. – lock Jun 01 '16 at 04:07

2 Answers2

4

Converting model-space points to screen-space points is called projecting (because it's applying the camera's projection transform to map from a 3D space into a 2D one). The renderer (via the GPU) does it many, many times per frame to get the points in your scene onto the screen.

There's also a convenience method for when you need to do it yourself: projectPoint:

Naeem
  • 789
  • 1
  • 10
  • 23
rickster
  • 124,678
  • 26
  • 272
  • 326
  • I have added more info. – Duck May 30 '16 at 15:25
  • What's the opposite of this? Going from Screen into the scene based (I guess) on camera project. Should I start another question for this? – Confused Sep 24 '16 at 21:47
  • 1
    Or just look at the link. The same protocol that has `project` might also have something that sounds like an opposite... – rickster Sep 25 '16 at 03:21
3

Quite old question, but unfortunately @ricksters answer was no real help for me, as projectPoint: keeps delivering messed up view coordinates in my case too (the same problem as @SpaceDog described)!

However, I found, that in my case, the problem was, that I had applied a transform to my root node (turned the world upside down ;-)). This obviously lead to the wrong results from projectPoint:...

As I couldn't remove the transform, I had to adjust my input to projectPoint: accordingly.

SCNVector3  viewStartPosition = ...; // Some place in the scene
SCNVector3  viewStartPositionRel = SCNVector3FromSCNVector4(SCNMatrix4MultV(self.scene.rootNode.worldTransform, SCNVector4FromSCNVector3(viewStartPosition)));
// Vector from UISceneView 0/0 top/left to start point
SCNVector3  viewStartPositionInViewCoords = [self.sceneView projectPoint:viewStartPositionRel];

Unfortunately, Apple delivers no matrix mathematics for SCNMatrix4 matrices, so the used (really easy) matrix handling methods are also 'home made':

/*
 SCNVector4FromSCNVector3

 */
SCNVector4 SCNVector4FromSCNVector3(SCNVector3 pV) {
    return SCNVector4Make(pV.x, pV.y, pV.z, 0.0);
}

/*
 SCNVector3FromSCNVector4

 */
SCNVector3 SCNVector3FromSCNVector4(SCNVector4 pV) {
    return SCNVector3Make(pV.x, pV.y, pV.z);
}

/*
 SCNMatrix4MultV

 */
SCNVector4 SCNMatrix4MultV(SCNMatrix4 pM, SCNVector4 pV) {
    SCNVector4  r = {
        pM.m11 * pV.x + pM.m12 * pV.y + pM.m13 * pV.z + pM.m14 * pV.w,
        pM.m21 * pV.x + pM.m22 * pV.y + pM.m23 * pV.z + pM.m24 * pV.w,
        pM.m31 * pV.x + pM.m32 * pV.y + pM.m33 * pV.z + pM.m34 * pV.w,
        pM.m41 * pV.x + pM.m42 * pV.y + pM.m43 * pV.z + pM.m44 * pV.w
    };
    return r;
}

I've got no idea, if this is a bug in SceneKit, or if maybe it is forbidden to apply transforms to the rootNode... Anyway: Problem solved :-)

LaborEtArs
  • 1,938
  • 23
  • 27
  • "Cast from '[Float]' to unrelated type 'SCNVector4' always fails" I get this. – Roshan Bade Aug 14 '19 at 11:20
  • `func SCNMatrix4MultV(_ pM: SCNMatrix4, _ pV: SCNVector4) -> SCNVector4 { let r = [pM.m11 * pV.x + pM.m12 * pV.y + pM.m13 * pV.z + pM.m14 * pV.w, pM.m21 * pV.x + pM.m22 * pV.y + pM.m23 * pV.z + pM.m24 * pV.w, pM.m31 * pV.x + pM.m32 * pV.y + pM.m33 * pV.z + pM.m34 * pV.w, pM.m41 * pV.x + pM.m42 * pV.y + pM.m43 * pV.z + pM.m44 * pV.w] as? SCNVector4 return r! }` For this function i get "Cast from '[Float]' to unrelated type 'SCNVector4' always fails" and app crashes in return as nil. – Roshan Bade Aug 16 '19 at 03:45
  • I‘m not that fluent in Swift, but I‘d guess, that the []-term doesn’t give a SCNVector4 but a Float, which is then forced to an SCNVector4... Try to use the SCNVector4 init(x,y,z,w) method instead. – LaborEtArs Aug 17 '19 at 05:44
  • In swift you can write it like this ```func SCNMatrix4MultV(_ pM: SCNMatrix4, _ pV: SCNVector4) -> SCNVector4 { let r = SCNVector4(x: pM.m11 * pV.x + pM.m12 * pV.y + pM.m13 * pV.z + pM.m14 * pV.w, y: pM.m21 * pV.x + pM.m22 * pV.y + pM.m23 * pV.z + pM.m24 * pV.w, z: pM.m31 * pV.x + pM.m32 * pV.y + pM.m33 * pV.z + pM.m34 * pV.w, w: pM.m41 * pV.x + pM.m42 * pV.y + pM.m43 * pV.z + pM.m44 * pV.w) return r }``` – Raja Kishan Nov 19 '21 at 15:23