What is an example of creating custom nodes with vertices in swift SceneKit?

This area is hardly registered on the internet and it would be great to see a working example of Swift 3, say a custom drawn cube with hand-crafted SCNvector3. Objective-C does this, but Swift does not. This may not be the usual form of the question, but I know it will help a lot. If I missed somewhere, please specify.

The documentation is not very helpful

scngeometrysource, etc.

thank

+3


source to share


1 answer


Custom geometry is built from a set of vertices and normals.

Tops

In this context, a vertex is the point at which two or more lines intersect. For a cube, the vertices are the corners shown in the following figure.

enter image description here

We build the geometry by creating the faces of the cube using a set of triangles, two triangles on the face. Our first triangle is defined by vertices 0, 2 and 3, as shown in the figure below, and the second triangle is defined by vertices 0, 1, and 2. It is important to note that each triangle has a front and a back. The side of a triangle is defined by the order of the vertices, with the front side in counterclockwise order. For our cube, the front will always be outside the cube.

If the center of the cube is the origin, the six vertices that define one of the faces of the cube can be defined with

let vertices:[SCNVector3] = [
        SCNVector3(x:-1, y:-1, z:1),    // 0
        SCNVector3(x:1, y:1, z:1),      // 2
        SCNVector3(x:-1, y:1, z:1)      // 3

        SCNVector3(x:-1, y:-1, z:1),    // 0
        SCNVector3(x:1, y:-1, z:1),     // 1
        SCNVector3(x:1, y:1, z:1)       // 2
]

      

and we create a vertex source at

let vertexSource = SCNGeometrySource(vertices: vertices)

      

At this point, we have a vertex source that can be used to draw the face of the cube; however SceneKit doesn't know how the triangle should react to lights in the scene. To properly reflect light, we need to provide our geometry with at least one normal vector for each vertex.

normals

The normal is a vector that indicates the orientation of the vertex, which affects the reflection of light from the corresponding triangle. In this case, the normal vectors for the six vertices of the triangle are the same; they all point in the positive z direction (i.e. x = 0, y = 0, and z = 1); see the red arrows in the picture below.

enter image description here

The normals are determined by the formula



let normals:[SCNVector3] = [
        SCNVector3(x:0, y:0, z:1),      // 0
        SCNVector3(x:0, y:0, z:1),      // 2
        SCNVector3(x:0, y:0, z:1),      // 3

        SCNVector3(x:0, y:0, z:1),      // 0
        SCNVector3(x:0, y:0, z:1),      // 1
        SCNVector3(x:0, y:0, z:1)       // 2
]

      

and the source is determined

let normalSource = SCNGeometrySource(vertices: normals)

      

We now have the sources (vertices and normals) needed to build the bounded geometry, i.e. one face of the cube (two triangles). The final part is to create an array of indices in vertices and normal arrays. In this case, the indices are sequential since the vertices are in the order in which they are used.

let indices:[Int32] = [0, 1, 2, 3, 4, 5]

      

From the indices, we create a geometry element. The setup is a little more complicated because it SCNGeometryElement

requires it NSData

as a parameter.

let pointer = UnsafeRawPointer(indices)
let indexData = NSData(bytes: pointer, length: MemoryLayout<Int32>.size * indices.count)

let element = SCNGeometryElement(data: indexData as Data, primitiveType: .triangles, primitiveCount: indices.count, bytesPerIndex: MemoryLayout<Int32>.size)

      

We can now create custom geometry with

let geometry = SCNGeometry(sources: [vertexSource, normalSource], elements: [element])

      

and finally create a node and assign custom geometry to your property geometry

let node = SCNNode()
node.geometry = geometry

scene.rootNode.addChildNode(node)

      

Now let's expand the vertices and normals, including all the faces of the cube:

    // The vertices
    let v0 = SCNVector3(x:-1, y:-1, z:1)
    let v1 = SCNVector3(x:1, y:-1, z:1)
    let v2 = SCNVector3(x:1, y:1, z:1)
    let v3 = SCNVector3(x:-1, y:1, z:1)

    let v4 = SCNVector3(x:-1, y:-1, z:-1)
    let v5 = SCNVector3(x:1, y:-1, z:-1)
    let v6 = SCNVector3(x:-1, y:1, z:-1)
    let v7 = SCNVector3(x:1, y:1, z:-1)

    // All the cube faces
    let vertices:[SCNVector3] = [
        // Front face
        v0, v2, v3,
        v0, v1, v2,

        // Right face
        v1, v7, v2,
        v1, v5, v7,

        // Back
        v5, v6, v7,
        v5, v4, v6,

        // Left
        v4, v3, v6,
        v4, v0, v3,

        // Top
        v3, v7, v6,
        v3, v2, v7,

        // Bottom
        v1, v4, v5,
        v1, v0, v4
    ]

    let normalsPerFace = 6
    let plusX = SCNVector3(x:1, y:0, z:0)
    let minusX = SCNVector3(x:-1, y:0, z:0)
    let plusZ = SCNVector3(x:0, y:0, z:1)
    let minusZ = SCNVector3(x:0, y:0, z:-1)
    let plusY = SCNVector3(x:0, y:1, z:0)
    let minusY = SCNVector3(x:0, y:-1, z:0)

    // Create an array with the direction of each vertex. Each array element is
    // repeated 6 times with the map function. The resulting array or arrays
    // is then flatten to an array
    let normals:[SCNVector3] = [
        plusZ,
        plusX,
        minusZ,
        minusX,
        plusY,
        minusY
        ].map{[SCNVector3](repeating:$0,count:normalsPerFace)}.flatMap{$0}

    // Create an array of indices [0, 1, 2, ..., N-1]
    let indices = vertices.enumerated().map{Int32($0.0)}

    let vertexSource = SCNGeometrySource(vertices: vertices)

    let normalSource = SCNGeometrySource(normals: normals)

    let pointer = UnsafeRawPointer(indices)
    let indexData = NSData(bytes: pointer, length: MemoryLayout<Int32>.size * indices.count)

    let element = SCNGeometryElement(data: indexData as Data, primitiveType: .triangles, primitiveCount: indices.count, bytesPerIndex: MemoryLayout<Int32>.size)

    let geometry = SCNGeometry(sources: [vertexSource, normalSource], elements: [element])

    // Create a node and assign our custom geometry
    let node = SCNNode()
    node.geometry = geometry

    scene.rootNode.addChildNode(node)

      

+9


source







All Articles