1
votes

Is there a way to solidify a SCNNode scale and orientation by recalculating its geometry?

Basically I am loading a SCNNode out of a scn file that was converted from a sketchup file that was exported to a DAE file. Because ARKit works in meters and with a different axis orientation I have to set the loaded SCNNode’s scale (to 0.0254) and eulerangle (x -90deg) to correctly show it. This all works fine however thus scaling and rotation messes up some logic down the line because this logic also uses rotation thus overriding the previous one... :(

I think it would be great if I could simply tell the SCNNode to recalculate its geometry based on its current scale, orientation... (basically its transformation matrix) which would result in an SCNNode with transformation matrix the zero matrix....

3

3 Answers

1
votes

If you “simply” want to tell the node to scale its vertices permanently, you will have to write a function to do so as there is no standard option.

That function should read the vertex source of the node’s geometry into an array of vectors, then use GLKMatrix4MultiplyAndProjectVector3 to apply the transformation to each vector, and then create a new SCNGeometry with the new verts as a source.

GLKMatrix4 scalemat = GLKMatrix4MakeScale(aNode.scale.x, aNode.scale.y, aNode.scale.z);

for (HEVertex* vert in toBeTransformedVerts) {
     vert.pos = SCNVector3FromGLKVector3(GLKMatrix4MultiplyAndProjectVector3(scalemat, SCNVector3ToGLKVector3(vert.pos)) );
}

//reset node scale property.
aNode.scale = SCNVector3Make(1.0, 1.0, 1.0);

HEVertex is a class I use to store vertices, the pos property is a SCNVector3. In your case you would have to read the vertex source of the .geometry of the SCNNode into an array of SCNVector3 and loop through those instead.

After transforming the position of each vertex, you need to update the node's geometry. I.e. something like this:

SCNGeometrySource *_vertexSource =
        [SCNGeometrySource geometrySourceWithVertices:_meshVertices count:_vertexCount];

aNode.geometry = [SCNGeometry geometryWithSources:@[_vertexSource, _aNode.geometry.geometrySources[1], _aNode.geometry.geometrySources[2]] elements:@[_aNode.geometry.geometryElements.firstObject]];

That is just a rough example, it updates only the vertex source, and reuses the normal and color geometry sources and the geometryElement, which can differ highly per model.

Very doable, but not nearly as simple as re-exporting the model with the appropriate size.

0
votes

If the node doesn't have any subnodes, you can simply parent your node to an empty node, perform the desired transforms on your node, then call flatten on the empty parent node. Your node's center and orientation will match what was the empty parent node. If your node has a tree of child nodes, you'll need to do this recursively.

0
votes

As an alternative to more complicated approaches you can easily accomplish this with a simple child node...

  1. Create a new node
  2. Move the geometry (and any other node properties you wish) from your original node to the new node
  3. Add the new node as a child of the original node
  4. Apply the scale transform to the child node.

Essentially, after step 3 you should be back exactly where you were before doing anything, except you now have a new child node to apply your scale transforms to, leaving the parent node alone so it can use the identity matrix (or whatever matrix you want.)

A similar approach is to use the existing node as-is...

  1. Create a new parent node
  2. Add it to the same parent as your existing node
  3. Move your existing node from the old parent to this new parent
  4. Copy the transform from your existing child node to the new parent node
  5. Replace the transform on the child node with the scale transform only

Choosing between the two really depends on how much stuff you have on your node that you want to move as well as what transforms it originally had on it. (i.e. if you scale a node, it may be out of place from its original position relative to the model.) You choose which of the above you wish to do based on that information.

That said, if it's just the geometry alone and scaling it in-place works, go with the first. If there are lots of node properties, go with the second. Point being, you're adding a new node specifically to apply the scaling to your existing node, no direct geometry modifications needed.