简体   繁体   中英

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.

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

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);
    }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM