user2724028
user2724028

Reputation: 604

SceneKit : Extract data from SCNGeometryElement

I am using SceneKit, and I have an issue:

How can I extract the data from a SCNGeometryElement object ? I use this method :

- (void)geometryElements:(SCNNode *)node {
for (int indexElement = 0; indexElement < node.geometry.geometryElementCount; indexElement++) {
    SCNGeometryElement *currentElement = [node.geometry geometryElementAtIndex:indexElement];

    NSLog(@"\n");
    NSLog(@"bytes per index : %d", currentElement.bytesPerIndex);
    NSLog(@"number element : %d", currentElement.primitiveCount);
    NSLog(@"data lenght : %d", currentElement.data.length);

    for (int indexPrimitive = 0; indexPrimitive < currentElement.primitiveCount; indexPrimitive++) {
        int array[3];
        memset(array, 0, 3);

        [currentElement.data getBytes:&array range:NSMakeRange(indexPrimitive * 3, (currentElement.bytesPerIndex * 3))];

        NSLog(@"currentelement : %d %d %d", array[0], array[1], array[3]);
    }
}

The result is not good :

2015-04-10 15:10:25.183 IKTest[1234:244778] currentelement : 14539995     -1068223968 -379286778
2015-04-10 15:10:25.183 IKTest[1234:244778] currentelement : 14737374 -1068223968 -379286778
2015-04-10 15:10:25.183 IKTest[1234:244778] currentelement : 14934753 -1068223968 -379286778

Thanks in advance.

Upvotes: 2

Views: 1129

Answers (3)

Farini
Farini

Reputation: 933

As the original question has a Swift tag, I am posting my solution in Swift 5.

extension SCNGeometryElement {

/// Gets the `Element` vertices
func getVertices() -> [SCNVector3] {
    
    func vectorFromData<UInt: BinaryInteger>(_ float: UInt.Type, index: Int) -> SCNVector3 {
        assert(bytesPerIndex == MemoryLayout<UInt>.size)
        let vectorData = UnsafeMutablePointer<UInt>.allocate(capacity: bytesPerIndex)
        let buffer = UnsafeMutableBufferPointer(start: vectorData, count: primitiveCount)
        let stride = 3 * index
        self.data.copyBytes(to: buffer, from: stride * bytesPerIndex..<(stride * bytesPerIndex) + 3)
        return SCNVector3(
            CGFloat.NativeType(vectorData[0]),
            CGFloat.NativeType(vectorData[1]),
            CGFloat.NativeType(vectorData[2])
        )
    }
    
    let vectors = [SCNVector3](repeating: SCNVector3Zero, count: self.primitiveCount)
    return vectors.indices.map { index -> SCNVector3 in
        switch bytesPerIndex {
            case 2:
                return vectorFromData(Int16.self, index: index)
            case 4:
                return vectorFromData(Int.self, index: index)
            case 8:
                return SCNVector3Zero
            default:
                return SCNVector3Zero
        }
    }
}
}

There might be a bit of an indentation problem here, but yes, it is a function inside another one. The objective is the same as the other answers. Create a buffer, define what types of numbers the buffer is going to handle, build the SCNVector3 and return the array.

Upvotes: 0

ooOlly
ooOlly

Reputation: 2127

As mnuages said, you should confirm primtive type and data type of index first. Your code only work if index type is int.

Here is some code work for me. I only deals that geometry consisted of triangles.

void extractInfoFromGeoElement(NSString* scenePath){
    NSURL *url = [NSURL fileURLWithPath:scenePath];
    SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:nil];
    SCNGeometry *geo = scene.rootNode.childNodes.firstObject.geometry;
    SCNGeometryElement *elem = geo.geometryElements.firstObject;
    NSInteger componentOfPrimitive = (elem.primitiveType == SCNGeometryPrimitiveTypeTriangles) ? 3 : 0;
    if (!componentOfPrimitive) {//TODO: Code deals with triangle primitive only
        return;
    }
    for (int i=0; i<elem.primitiveCount; i++) {
        void *idxsPtr = NULL;
        int stride = 3*i;
        if (elem.bytesPerIndex == 2) {
            short *idxsShort = malloc(sizeof(short)*3);
            idxsPtr = idxsShort;
        }else if (elem.bytesPerIndex == 4){
            int *idxsInt = malloc(sizeof(int)*3);
            idxsPtr = idxsInt;
        }else{
            NSLog(@"unknow index type");
            return;
        }
        [elem.data getBytes:idxsPtr range:NSMakeRange(stride*elem.bytesPerIndex, elem.bytesPerIndex*3)];
        if (elem.bytesPerIndex == 2) {
            NSLog(@"triangle %d : %d, %d, %d\n",i,*(short*)idxsPtr,*((short*)idxsPtr+1),*((short*)idxsPtr+2));
        }else{
            NSLog(@"triangle %d : %d, %d, %d\n",i,*(int*)idxsPtr,*((int*)idxsPtr+1),*((int*)idxsPtr+2));
        }
        //Free
        free(idxsPtr);
    }
}

Upvotes: 0

mnuages
mnuages

Reputation: 13462

a few notes:

  • you should rely on geometryElement.primitiveType instead of hard coding 3 (unless you are sure that you're always dealing with SCNGeometryPrimitiveTypeTriangles)
  • it seems that the range's location does not take geometryElement.bytesPerIndex into account
  • your buffer is of size 3 * sizeof(int) but should be of size numberOfIndicesPerPrimitive * geometryElement.bytesPerIndex

Upvotes: 2

Related Questions