从scenekit中提取顶点

时间:2013-06-22 12:02:29

标签: collada scenekit

我在理解scenekit几何方面遇到了问题。

我有Blender的默认立方体,我导出为collada(DAE),可以将它带入scenekit ......一切都很好。

现在我想看到立方体的顶点。在DAE中,我可以看到以下“Cube-mesh-positions-array”,

“1 1 -1 1 -1 -1 -1 -0.9999998 -1 -0.9999997 1 -1 1 0.9999995 1 0.9999994 -1.000001 1 -1 -0.9999997 1 -1 1 1”

现在我想在scenekit中做的是使用以下内容返回顶点:

SCNGeometrySource *vertexBuffer = [[cubeNode.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex] objectAtIndex:0];

如果我处理vertexBuffer(我尝试了很多查看数据的方法),它似乎不正确。

有人可以解释“SCNGeometrySourceSemanticVertex”给我的内容,以及如何正确提取顶点数据?我想看到的是:

X = "float"
Y = "float"
Z = "float"

此外,我正在调查以下类/方法,这看起来很有前途(这里有一些好的数据值),但是来自gmpe的数据显示为空,是否有人能够解释“SCNGeometryElement”的数据属性包含什么?

SCNGeometryElement *gmpe = [theCurrentNode.geometry geometryElementAtIndex:0];

谢谢,非常感谢,

d

8 个答案:

答案 0 :(得分:25)

几何源

当你调用geometrySourcesForSemantic:时,你会得到一组SCNGeometrySource个对象,在你的情况下,给定的语义是顶点数据的来源。)

此数据可能已经以多种不同方式进行编码,多个来源可以使用具有不同strideoffset的相同数据。源本身有一堆属性,您可以解码data,例如

  • dataStride
  • dataOffset
  • vectorCount
  • componentsPerVector
  • bytesPerComponent

您可以使用这些组合来确定要读取data的哪些部分并从中创建顶点。

解码

步幅告诉你应该步进多少字节到达下一个向量,偏移量告诉你在到达该向量的相关数据段之前应该偏离该向量的起始偏移多少字节。您应该为每个向量读取的字节数是componentsPerVector * bytesPerComponent

读出单个几何源的所有顶点的代码看起来像这样

// Get the vertex sources
NSArray *vertexSources = [geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];

// Get the first source
SCNGeometrySource *vertexSource = vertexSources[0]; // TODO: Parse all the sources

NSInteger stride = vertexSource.dataStride; // in bytes
NSInteger offset = vertexSource.dataOffset; // in bytes

NSInteger componentsPerVector = vertexSource.componentsPerVector;
NSInteger bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
NSInteger vectorCount = vertexSource.vectorCount;

SCNVector3 vertices[vectorCount]; // A new array for vertices

// for each vector, read the bytes
for (NSInteger i=0; i<vectorCount; i++) {

    // Assuming that bytes per component is 4 (a float)
    // If it was 8 then it would be a double (aka CGFloat)
    float vectorData[componentsPerVector];

    // The range of bytes for this vector
    NSRange byteRange = NSMakeRange(i*stride + offset, // Start at current stride + offset
                                    bytesPerVector);   // and read the lenght of one vector

    // Read into the vector data buffer
    [vertexSource.data getBytes:&vectorData range:byteRange];

    // At this point you can read the data from the float array
    float x = vectorData[0];
    float y = vectorData[1];
    float z = vectorData[2];

    // ... Maybe even save it as an SCNVector3 for later use ...
    vertices[i] = SCNVector3Make(x, y, z);

    // ... or just log it 
    NSLog(@"x:%f, y:%f, z:%f", x, y, z);
}

几何元素

这将为您提供所有顶点,但不会告诉您如何使用它们来构造几何体。为此,您需要几何元素来管理顶点的索引。

您可以从geometryElementCount属性获取几何体的几何元素数。然后,您可以使用geometryElementAtIndex:获取不同的元素。

元素可以告诉您顶点是使用单个三角形还是三角形条带。它还告诉您每个索引的字节数(索引可能是int s或short s,这对于解码其data是必需的。

答案 1 :(得分:4)

Swift版本

Objective-C版本与此基本相同。

let planeSources = _planeNode?.geometry?.geometrySourcesForSemantic(SCNGeometrySourceSemanticVertex)
if let planeSource = planeSources?.first {
    let stride = planeSource.dataStride
    let offset = planeSource.dataOffset
    let componentsPerVector = planeSource.componentsPerVector
    let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent

    let vectors = [SCNVector3](count: planeSource.vectorCount, repeatedValue: SCNVector3Zero)
    let vertices = vectors.enumerate().map({
        (index: Int, element: SCNVector3) -> SCNVector3 in
        var vectorData = [Float](count: componentsPerVector, repeatedValue: 0)
        let byteRange = NSMakeRange(index * stride + offset, bytesPerVector)
        planeSource.data.getBytes(&vectorData, range: byteRange)
        return SCNVector3Make(vectorData[0], vectorData[1], vectorData[2])
    })

    // You have your vertices, now what?
}

答案 2 :(得分:2)

使用swift 3.1,您可以更快,更短的方式从SCNGeometry中提取顶点:

func vertices(node:SCNNode) -> [SCNVector3] {
    let vertexSources = node.geometry?.getGeometrySources(for: SCNGeometrySource.Semantic.vertex)
    if let vertexSource = vertexSources?.first {
        let count = vertexSource.data.count / MemoryLayout<SCNVector3>.size
        return vertexSource.data.withUnsafeBytes {
            [SCNVector3](UnsafeBufferPointer<SCNVector3>(start: $0, count: count))
        }
    }
    return []
}

... 今天我已经注意到在osx上这不会正常工作。发生这种情况是因为在iOS SCNVector3上使用Float和osx CGFloat进行构建(只有苹果很好,所以很简单,所以很痛苦)。所以我不得不调整osx的代码,但这不会像在iOS上一样快。

func vertices() -> [SCNVector3] {

        let vertexSources = sources(for: SCNGeometrySource.Semantic.vertex)


        if let vertexSource = vertexSources.first {

        let count = vertexSource.vectorCount * 3
        let values = vertexSource.data.withUnsafeBytes {
            [Float](UnsafeBufferPointer<Float>(start: $0, count: count))
        }

        var vectors = [SCNVector3]()
        for i in 0..<vertexSource.vectorCount {
            let offset = i * 3
            vectors.append(SCNVector3Make(
                CGFloat(values[offset]),
                CGFloat(values[offset + 1]),
                CGFloat(values[offset + 2])
            ))
        }

        return vectors
    }
    return []
}

答案 3 :(得分:2)

如果数据不连续(向量大小不等于步幅),这是一种扩展方法,这是从DAE文件加载几何时的情况。它还不使用copyByte函数。

extension  SCNGeometry{


    /**
     Get the vertices (3d points coordinates) of the geometry.

     - returns: An array of SCNVector3 containing the vertices of the geometry.
     */
    func vertices() -> [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
                let x = buffer[start]
                let y = buffer[start + 1]
                let z = buffer[start + 2]
                result.append(SCNVector3(x, y, z))
            }
            return result
        }
    }
}

答案 4 :(得分:1)

Swift 3版本:

    // `plane` is some kind of `SCNGeometry`

    let planeSources = plane.geometry.sources(for: SCNGeometrySource.Semantic.vertex)
    if let planeSource = planeSources.first {
        let stride = planeSource.dataStride
        let offset = planeSource.dataOffset
        let componentsPerVector = planeSource.componentsPerVector
        let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent

        let vectors = [SCNVector3](repeating: SCNVector3Zero, count: planeSource.vectorCount)
        let vertices = vectors.enumerated().map({
            (index: Int, element: SCNVector3) -> SCNVector3 in

            let vectorData = UnsafeMutablePointer<Float>.allocate(capacity: componentsPerVector)
            let nsByteRange = NSMakeRange(index * stride + offset, bytesPerVector)
            let byteRange = Range(nsByteRange)

            let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
                planeSource.data.copyBytes(to: buffer, from: byteRange)
            let vector = SCNVector3Make(buffer[0], buffer[1], buffer[2])
        })

        // Use `vertices` here: vertices[0].x, vertices[0].y, vertices[0].z
    }

答案 5 :(得分:1)

这是一个基于其他答案的 Swift 5.3 版本,它也支持与 bytesPerComponent 不同的 4(但未测试与 4 不同的大小):

extension SCNGeometrySource {
    var vertices: [SCNVector3] {
        let stride = self.dataStride
        let offset = self.dataOffset
        let componentsPerVector = self.componentsPerVector
        let bytesPerVector = componentsPerVector * self.bytesPerComponent

        func vectorFromData<FloatingPoint: BinaryFloatingPoint>(_ float: FloatingPoint.Type, index: Int) -> SCNVector3 {
            assert(bytesPerComponent == MemoryLayout<FloatingPoint>.size)
            let vectorData = UnsafeMutablePointer<FloatingPoint>.allocate(capacity: componentsPerVector)
            let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
            let rangeStart = index * stride + offset
            self.data.copyBytes(to: buffer, from: rangeStart..<(rangeStart + bytesPerVector))
            return SCNVector3(
                CGFloat.NativeType(vectorData[0]),
                CGFloat.NativeType(vectorData[1]),
                CGFloat.NativeType(vectorData[2])
            )
        }

        let vectors = [SCNVector3](repeating: SCNVector3Zero, count: self.vectorCount)
        return vectors.indices.map { index -> SCNVector3 in
            switch bytesPerComponent {
            case 4:
                return vectorFromData(Float32.self, index: index)
            case 8:
                return vectorFromData(Float64.self, index: index)
            case 16:
                return vectorFromData(Float80.self, index: index)
            default:
                return SCNVector3Zero
            }
        }
    }
}

答案 6 :(得分:0)

对于像我这样的人,想从SCNGeometryElement中提取面部数据。

请注意,我只考虑基本类型为三角形,索引大小为2或4。

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

答案 7 :(得分:0)

//调用此函数_ =顶点(节点:mySceneView.scene!.rootNode) //我已经在Swift 4.2中获得了音量:---此功能

String[] cmd = { "/bin/bash", "-c", "sledge connect --cluster_id=<name> --namespace=<name> --password=<pass>"};