SceneKit:从SCNGeometryElement中提取数据

时间:2015-04-10 13:23:44

标签: ios objective-c iphone swift scenekit

我正在使用SceneKit,我遇到了一个问题:

如何从SCNGeometryElement对象中提取数据? 我用这个方法:

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

结果不好:

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

提前致谢。

3 个答案:

答案 0 :(得分:2)

一些注释:

  • 您应该依赖geometryElement.primitiveType而不是硬编码3(除非您确定自己始终处理SCNGeometryPrimitiveTypeTriangles
  • 似乎该范围的位置未考虑geometryElement.bytesPerIndex
  • 您的缓冲区大小为3 * sizeof(int),但大小应为numberOfIndicesPerPrimitive * geometryElement.bytesPerIndex

答案 1 :(得分:0)

正如专家所说,您应该首先确认索引的原始类型和数据类型。 您的代码仅在索引类型为int时有效。

这是一些适合我的代码。我只处理由三角形组成的几何。

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

答案 2 :(得分:0)

由于原始问题有一个 Swift 标签,我将在 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
        }
    }
}
}

这里可能有一点缩进问题,但是是的,它是另一个函数内部的函数。目标与其他答案相同。创建一个缓冲区,定义缓冲区要处理的数字类型,构建 SCNVector3 并返回数组。