0

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)
  • How is the original SCNNode created ? Is it triangles or data strips? – Ptit Xav Feb 16 '22 at 06:49
  • Did you cloning using clone() and updating only colors? – Ptit Xav Feb 16 '22 at 06:54
  • @PtitXav The original node comes from an .obj load and I'm cloning it creating another node from the geometry (I updated my question with the code) is there a way to specify a color per each vertex using .clone()? or even without using it? – Rodrigo González Feb 16 '22 at 14:29
  • chat happens here is that you have some v//vn which are used multiple time (4//2 , 6//2) that is why number of vertices is not what you expect : number of vertices you get is only the number of différents v//vn that are present in the obj file (optimisation to use smaller vector array. After for the color, it depends if you want one color per face or per vertice or per (vertice//normal) – Ptit Xav Feb 16 '22 at 15:01
  • I see, in that case, how can I get the array of vertices and normals that can be used to create a new geometry? a non-optimized array? I want to specify a color per each vertice based on the position of it – Rodrigo González Feb 16 '22 at 15:11
  • you must create a color source with a color for each vertices. then use this source for you geometry, the same way you do with vertices and normal source. – Ptit Xav Feb 16 '22 at 16:22
  • But how can I achieve that? if I create a node from the sources, the resulting mesh looks like the 'cloned mesh' picture I posted, basically what I want is to calculate and set per vertex color to my existing mesh that comes from an .obj – Rodrigo González Feb 21 '22 at 14:30

2 Answers2

0

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

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
    
}
Ptit Xav
  • 3,006
  • 2
  • 6
  • 15