Search code examples
scenekitarkit

Recalculate SCNNode geometry based on transformation


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


Solution

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