Wrong vertex data when getting sources from SCNNode geometry

296 Views Asked by At

I'm using the code below inside a SCNGeometry extension to get the vertex data, and a similar one for .normals, my objective is to create a cloned mesh from the original node to colorize each vertex, but when I try to create another node with those vertices and normals, the resulting mesh has some triangles messed up, I have a small mesh to test and this is what I got, Do any of you have some guidance on what I could be doing wrong?

In the example, this function is getting me an array of 50 vertices, while the mesh actually has 18 faces, hence the result should be an array of 54 vertices, am I right?

Original mesh Cloned mesh

Extension:

func extGetVertices() -> [SCNVector3]? {
    let sources = self.sources(for: .vertex)
    guard let source = sources.first else{return nil}
    let stride = source.dataStride / source.bytesPerComponent
    let offset = source.dataOffset / source.bytesPerComponent
    let vectorCount = source.vectorCount
    return source.data.withUnsafeBytes { (buffer : UnsafePointer<Float>) -> [SCNVector3] in
        var result = Array<SCNVector3>()
        for i in 0...vectorCount - 1 {
            let start = i * stride + offset
            result.append(SCNVector3(buffer[start], buffer[start+ 1], buffer[start+ 2]))
        }
        return result
    }
}

obj:

#
# object obj_12853878
#
v 1097 957 36
v 779.26361083984375 992 0
v 707.26361083984375 590.5828857421875 91
v 1076 334.41595458984375 0
v 748.26361083984375 326.41595458984375 0
v 732.01263427734375 22.33051872253417968 0
v 1110.4652099609375 639.2049560546875 0
v 335.71615600585937504 680.5828857421875 39
v 314.88912963867187504 369.207000732421875 9.023892608628001E-14
v 350.4644775390625 926.65570068359375 -33
v 36 358.41595458984375 0
v 0 0 -33
v 0 680.5828857421875 -27
v 0 957 19
v 335.71615600585937504 22 0
v 1076 0 30
# 16 vertices
vn -0.08388713002204895 0.23459480702877044 0.9684669375419616
vn 0 0 1
vn 0.24344816803932188 -0.28190669417381288 0.92804181575775152
vn -0.1642393171787262 -0.11176854372024536 0.98006796836853024
vn -0.11669350415468216 0.28533965349197388 0.9512959122657776
vn 0.00356122362427413 -0.0920381024479866 0.99574911594390864
vn -0.19254806637763976 0.06056648120284081 0.9794166088104248
vn 0.13100945949554444 -0.1627427488565445 0.97793215513229376
vn -0.0974447876214981 -0.0058451765216887 0.9952237606048584
vn -0.03258795291185379 -0.3300407826900482 0.9434040188789368
vn 0.23050078749656676 -0.09988142549991608 0.967932403087616
vn -0.07897967845201492 0.233848974108696 0.96905976533889776
vn 0.00482096569612622 -0.1245955303311348 0.99219590425491328
vn -0.18483471870422364 0.28617173433303832 0.940181851387024
vn -0.08079835772514343 0.08905769884586334 0.99274384975433344
vn 1.8364935581471252E-16 -2.4888339972415545E-16 1
# 16 vertex normals
g obj_12853878
s 1
f 1//1 2//1 3//1
f 4//2 5//2 6//2
f 7//3 3//3 5//3
f 3//4 8//4 9//4
f 2//5 10//5 8//5
f 9//6 11//6 12//6
f 8//7 13//7 11//7
f 10//8 14//8 13//8
f 15//9 9//9 12//9
f 5//10 3//10 9//10
f 7//11 1//11 3//11
f 2//12 8//12 3//12
f 8//13 11//13 9//13
f 10//14 13//14 8//14
f 4//15 6//15 16//15
f 7//2 5//2 4//2
f 5//2 15//2 6//2
f 5//16 9//16 15//16
#18 polygons

Clone code:

guard let let vertices = node.geometry?.extGetVertices() else {return nil}
guard let let normals = node.geometry?.extGetNormals() else {return nil}
guard let let indices = (0..<vertices.count).indices.map {Int32($0)}

let vertexSource = SCNGeometrySource(vertices: vertices)
let indexElement = SCNGeometryElement(indices: indices, primitiveType: SCNGeometryPrimitiveType.triangles)
let normalSource = SCNGeometrySource(normals: normals)
let voxelGeometry = SCNGeometry(sources: [vertexSource, normalSource], elements: [indexElement])
let voxelMaterial = SCNMaterial()
voxelMaterial.diffuse.contents = UIColor.white
voxelGeometry.materials = [voxelMaterial]
let clonedNode = SCNNode(geometry: voxelGeometry)
2

There are 2 best solutions below

0
On

Ok, I found my original problem, I was creating the array of indexes as a series of consecutive numbers, but they could be obtained from the original mesh too

func extGetIndices() -> [Int32]? {
    guard let dataCount = self.elements.first?.data.count else { return nil }
    let faces = self.elements.first?.data.withUnsafeBytes {(ptr: UnsafeRawBufferPointer) -> [Int32] in
        guard let boundPtr = ptr.baseAddress?.assumingMemoryBound(to: Int32.self) else {return []}
        let buffer = UnsafeBufferPointer(start: boundPtr, count: dataCount / 4)
        return Array<Int32>(buffer)
    }
    return faces
}

so, the updated code for cloning the node is:

guard let let vertices = node.geometry?.extGetVertices() else {return nil}
guard let let normals = node.geometry?.extGetNormals() else {return nil}
guard let let indices = node.geometry?.extGetIndices() else {return nil}

let vertexSource = SCNGeometrySource(vertices: vertices)
let indexElement = SCNGeometryElement(indices: indices, primitiveType: SCNGeometryPrimitiveType.triangles)
let normalSource = SCNGeometrySource(normals: normals)
let voxelGeometry = SCNGeometry(sources: [vertexSource, normalSource], elements: [indexElement])
let voxelMaterial = SCNMaterial()
voxelMaterial.diffuse.contents = UIColor.white
voxelGeometry.materials = [voxelMaterial]
let clonedNode = SCNNode(geometry: voxelGeometry)

Additionally, the node could be colored having a SCNGeometrySource of color type and adding colors for each vertex:

let colors = getRandomColors(vertices.count)
let colorData = NSData(bytes: colors , length: MemoryLayout<SCNVector3>.stride * colors.count)
let colorSource = SCNGeometrySource(data: colorData as Data, semantic: .color, vectorCount: colors.count, usesFloatComponents: true, componentsPerVector: 3, bytesPerComponent: MemoryLayout<Float>.size, dataOffset: 0, dataStride: MemoryLayout<SCNVector3>.stride)
let voxelGeometry = SCNGeometry(sources: [vertexSource, normalSource, colorSource ], elements: [indexElement])


//random colors function
func getRandomColors(count: Int) -> [SCNVector3] {
    var colors: [SCNVector3] = []
    for _ in 0..<count {
        let red = Double.random(in: 0...1)
        let green = Double.random(in: 0...1)
        let blue = Double.random(in: 0...1)
        colors.append(SCNVector3(red, green, blue))
    }
    return colors
}
0
On

Here an example :

func cloneNode(node: SCNNode) -> SCNNode? {
    guard let vertices = node.geometry?.extGetVertices() else {return nil}
    guard let normals = node.geometry?.extGetNormals() else {return nil}
    let indices = (0..<vertices.count).indices.map {Int32($0)}

    // initialise color source
    struct RGBColor {
        let r,g,b : Float
    }

    var colors : [RGBColor] = []
    
    // to have contiguous memory space
    colors.reserveCapacity(vertices.count)

    // convert to color geometry source
    let colorsAsData = NSData(bytes: colors, length: MemoryLayout<RGBColor>.size * colors.count) as Data
    // fill colors arrays
    let colorSource = SCNGeometrySource(data: colorsAsData,
                                        semantic: .color,
                                        vectorCount: colors.count,
                                        usesFloatComponents: true,
                                        componentsPerVector: 3,
                                        bytesPerComponent: MemoryLayout<Float>.size,
                                        dataOffset: MemoryLayout.offset(of: \RGBColor.r)!,
                                        dataStride: MemoryLayout<RGBColor>.stride)

    
    let vertexSource = SCNGeometrySource(vertices: vertices)
    let indexElement = SCNGeometryElement(indices: indices, primitiveType: SCNGeometryPrimitiveType.triangles)
    let normalSource = SCNGeometrySource(normals: normals)
    // Add the colors source in the list
    let voxelGeometry = SCNGeometry(sources: [vertexSource, normalSource, colorSource], elements: [indexElement])
    let voxelMaterial = SCNMaterial()
    voxelMaterial.diffuse.contents = UIColor.white
    voxelGeometry.materials = [voxelMaterial]
    let clonedNode = SCNNode(geometry: voxelGeometry)
    
    return clonedNode
    
}