Math Moving Grid in Fast Mode with SceneKit

I am a mathematician who wants to program a geometric game.

I have precise coordinates and mathematical formulas, from several cells that I need to display, and their unit normals.

I only need one texture (colored reflective metal) per mesh.

I need the user to move the pieces, i.e. changed the grid coordinates again with a simple math formula.

So I don't need to import 3D files, but I can figure it out.

Imagine a Rubik's cube. The coordinates of the cube are calculated, and the cubes are rotated by the user. I have a program running in Mathematica.

I find it very difficult right now to spend sleepless days trying to figure out how to display the computed mesh in SceneKit - with each vertex and normal animation separately.

ANY working example of, say, a single triangle with computed coordinates (not the provided shape) rendered with animated coordinates using SceneKit would be EXTREMELY appreciated.

I looked more and it seems that individual grid points may not move in SceneKit. I like with SceneKit (as opposed to OpenGL) the function with which you can get objects under the user's finger. Can OpenGL and SceneKit be mixed in a project?

I could take from there ...

+3


source to share


1 answer


Animating vertex positions individually is, in general, a difficult problem. But there are good ways to approach her in SceneKit.

The GPU really wants the vertex data to be loaded in one block before it starts rendering the frame. This means that if you are constantly calculating new vertex positions / normals / etc on the CPU, you have the problem of bringing all this data closer to the GPU every time, even if only part of it changes.

Since you are already describing your surface mathematically, you have a good place to do this work on the GPU itself. If each vertex position is a function of some variable, you can write that function in a shader and find a way to pass the input variable to the vertex.

There are several options that you might consider:

  • Shader modifiers. Start with dummy geometry with the desired topology (the number of vertices and how they are connected as polygons). Pass your input variable as an additional texture, and in the shader modifier code (for the geometry entry point), find the texture, execute your function and set the vertex position with the result.

  • Metal shaders. Create a geometry source backed by a metal buffer , and then at render time, queue a compute shader that writes vertex data to that buffer according to your function. (There's skeleton code for the part of what's in the link.)


Update. From your comments, it sounds like you might be in an easier position.

If you have geometry made up of pieces that are static in relation to themselves and move relative to each other - like Rubik's cube - computing vertices during rendering is overkill. Instead, you can load the static parts of your geometry onto the GPU once and use transforms to position them relative to each other.



In SceneKit, it is possible to create separate nodes, each with its own (static) geometry for each part, and then sets a transform node (or position / orientation / scale) to move the nodes in relation to each other.To move multiple nodes at the same time, use a hierarchy node - make some of them children from another node. If someone needs to move together at one point and another subset needs to move later, you can change the hierarchy.

Here is a concrete example of a Rubik's cube idea. First, creating some cubes:

// convenience for creating solid color materials
func materialWithColor(color: NSColor) -> SCNMaterial {
    let mat = SCNMaterial()
    mat.diffuse.contents = color
    mat.specular.contents = NSColor.whiteColor()
    return mat
}

// create and arrange a 3x3x3 array of cubelets
var cubelets: [SCNNode] = []
for x in -1...1 {
    for y in -1...1 {
        for z in -1...1 {
            let box = SCNBox()
            box.chamferRadius = 0.1
            box.materials = [
                materialWithColor(NSColor.greenColor()),
                materialWithColor(NSColor.redColor()),
                materialWithColor(NSColor.blueColor()),
                materialWithColor(NSColor.orangeColor()),
                materialWithColor(NSColor.whiteColor()),
                materialWithColor(NSColor.yellowColor()),
            ]
            let node = SCNNode(geometry: box)
            node.position = SCNVector3(x: CGFloat(x), y: CGFloat(y), z: CGFloat(z))
            scene.rootNode.addChildNode(node)
            cubelets += [node]
        }
    }
}

      

Next, the process of performing rotation. This is one particular rotation, but you can generalize this to a function that performs any transformation on any subset of cubes:

// create a temporary node for the rotation
let rotateNode = SCNNode()
scene.rootNode.addChildNode(rotateNode)

// grab the set of cubelets whose position is along the right face of the puzzle,
// and add them to the rotation node
let rightCubelets = cubelets.filter { node in 
    return abs(node.position.x - 1) < 0.001 
}
rightCubelets.map { rotateNode.addChildNode($0) }

// animate a rotation
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(2)
rotateNode.eulerAngles.x += CGFloat(M_PI_2)
SCNTransaction.setCompletionBlock {
    // after animating, remove the cubelets from the rotation node,
    // and re-add them to the parent node with their transforms altered
    rotateNode.enumerateChildNodesUsingBlock { cubelet, _ in
        cubelet.transform = cubelet.worldTransform
        cubelet.removeFromParentNode()
        scene.rootNode.addChildNode(cubelet)
    }
    rotateNode.removeFromParentNode()
}
SCNTransaction.commit()

      

The magic part is in the process of cleaning up after the animation. The cubes start out as children of the scene root node, and we temporarily rearrange them to a different node so that we can transform them together. By putting them back in the root node children again, we set each local transform

to it's own worldTransform

so that it retains the effect of the temporary transform node.

You can then repeat this process to grab any set of nodes in the (new) set of world space positions and use another temporary node to transform them.

I'm not sure how similar a Rubik cube is as your problem is, but it looks like you can probably generalize a solution from something like this.

+6


source







All Articles