Voxel Array creates a voxel that's not part of the scene

56 views Asked by At

When changing the divisions argument in MDLVoxelArray() I sometimes get a stray voxel that is unrelated to the underlying Nodes/Geometries. Why is this?

Setup

Consider this Playground code to build a mesh (SCNGeometry)

import UIKit
import SceneKit
import PlaygroundSupport
import SceneKit.ModelIO

let sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 800, height: 800))
let scene = SCNScene()

sceneView.backgroundColor = .black
sceneView.scene = scene
sceneView.allowsCameraControl = true

PlaygroundPage.current.liveView = sceneView

var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 10)
scene.rootNode.addChildNode(cameraNode)

var ambientLight = SCNNode()
ambientLight.light = SCNLight()
ambientLight.light?.type = .ambient
cameraNode.addChildNode(ambientLight)

let vertices: [SCNVector3] = [
    // back square
    SCNVector3(-1, -1,  0),
    SCNVector3( 1, -1,  0),
    SCNVector3( 1,  1,  0),
    SCNVector3(-1,  1,  0),
    
    // top triangle
    SCNVector3(-1, 1, 0),
    SCNVector3(-1, 1, 1),
    SCNVector3(1,  1, 0),
    
    // left triangle
    SCNVector3(-1, 1, 0),
    SCNVector3(-1, 1, 1),
    SCNVector3(-1, 0, 0),
    
    // detached-right triangle
    SCNVector3(1.1, -1.1, 0), // deliberately an extra .1 so the divisions aren't regular
    SCNVector3(1.5, -1.1, 0),
    SCNVector3(1.5, 0.7, 0),
    
    // detached front triangle
    SCNVector3(-0.5, -0.5, 2),
    SCNVector3(0.5, -0.5, 2.5),
    SCNVector3(0.0, 0.0, 2.2)
    
]

let vertexSource = SCNGeometrySource(vertices: vertices)

// Faces
let indices: [Int32] = [
    0,1,2,
    0,2,3,
    4,5,6,
    7,8,9,
    10,11,12,
    13,14,15
]
let indexElement = SCNGeometryElement(
    indices: indices,
    primitiveType: .triangles
)

// Normals
let normals: [SCNVector3] = [
    SCNVector3(0, 0, 1)
]
let normalSource = SCNGeometrySource(normals: normals)

// Colors
let colours: [SCNVector3] = [
    SCNVector3(1, 0, 0),
    SCNVector3(0, 1, 0),
    SCNVector3(0, 0, 1),
    
    SCNVector3(1, 0, 0),
    SCNVector3(0, 0, 1),
    SCNVector3(0, 1, 0),
    
    SCNVector3(1, 0, 1),
    SCNVector3(0, 1, 1),
    SCNVector3(0, 0, 1),
    
    SCNVector3(1, 0, 1),
    SCNVector3(0, 1, 1),
    SCNVector3(0, 0, 1),
    
    SCNVector3(1, 0, 1),
    SCNVector3(0, 1, 1),
    SCNVector3(0, 0, 1),
]

let colourData = Data(
    bytes: colours,
    count: MemoryLayout<SCNVector3>.size * colours.count
)

let colourSource = SCNGeometrySource(
    data: colourData,
    semantic: .color,
    vectorCount: colours.count,
    usesFloatComponents: true,
    componentsPerVector: 3,
    bytesPerComponent: MemoryLayout<Float>.size,
    dataOffset: 0,
    dataStride: MemoryLayout<SCNVector3>.size
)


let meshGeometry = SCNGeometry(
    sources: [vertexSource, normalSource, colourSource],
    elements: [indexElement]
)

let meshMaterial = SCNMaterial()
meshMaterial.diffuse.contents = UIColor.white
meshMaterial.isDoubleSided = true
meshGeometry.materials = [meshMaterial]

let meshNode = SCNNode(geometry: meshGeometry)
meshNode.position = SCNVector3(0, 0, 0)
meshNode.name = "meshNode"
sceneView.scene?.rootNode.addChildNode(meshNode)

enter image description here

enter image description here

Voxel Example

We can create a VoxelArray and place a sphere at each voxel position

let divisions: Int32 = 8  // when it's 9 there's a stray voxel?

let asset = MDLAsset(scnScene: scene)
let voxelArray = MDLVoxelArray(asset: asset, divisions: divisions, patchRadius: 0.0)

if let voxelData = voxelArray.voxelIndices() {

    voxelData.withUnsafeBytes { voxelBytes in
        let voxels = voxelBytes.bindMemory(to: MDLVoxelIndex.self).baseAddress!
        let count = voxelData.count / MemoryLayout<MDLVoxelIndex>.size
        for i in 0..<count {
            
                
            let position = voxelArray.spatialLocation(ofIndex: voxels[i])
            let vec3Position = SCNVector3Make(SCNFloat(position.x) , SCNFloat(position.y), SCNFloat(position.z) )
                
            let sphere = SCNSphere(radius: 0.05)
            sphere.firstMaterial?.diffuse.contents = UIColor.orange
            let sphereNode = SCNNode(geometry: sphere)
            sphereNode.position = vec3Position
            scene.rootNode.addChildNode(sphereNode)
        }
    }
}

enter image description here enter image description here

Issue

However, when I set the number of divisions to 9, I get a stray voxel. Why is this voxel generated?

let divisions: Int32 = 9

enter image description here

0

There are 0 answers