18

我在理解场景套件几何时遇到问题。

我有来自 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

4

8 回答 8

27

几何源

当你打电话给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:.

该元素可以告诉您顶点是使用单个三角形还是三角形条。它还告诉您每个索引的字节数(索引可能是ints 或shorts ,这对于解码其data.

于 2013-06-22T12:45:17.830 回答
5

如果数据不连续(矢量大小不等于步幅),这是一种扩展方法,从 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
        }
    }
}
于 2018-07-10T08:12:39.890 回答
4

斯威夫特版本

Objective-C 版本和 this 基本相同。

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?
}
于 2016-05-30T10:16:59.170 回答
2

// 调用这个函数 _ = vertices(node: mySceneView.scene!.rootNode) // 我在 Swift 4.2 中得到了音量 :--- 这个函数

func vertices(node:SCNNode) -> [SCNVector3] {

    let planeSources1 = node.childNodes.first?.geometry

    let planeSources = planeSources1?.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)
            return SCNVector3Make(buffer[0], buffer[1], buffer[2])
        })
        var totalVolume = Float()
        var x1 = Float(),x2 = Float(),x3 = Float(),y1 = Float(),y2 = Float(),y3 = Float(),z1 = Float(),z2 = Float(),z3 = Float()
        var i = 0
        while i < vertices.count{

                x1 = vertices[i].x;
                y1 = vertices[i].y;
                z1 = vertices[i].z;

                x2 = vertices[i + 1].x;
                y2 = vertices[i + 1].y;
                z2 = vertices[i + 1].z;

                x3 = vertices[i + 2].x;
                y3 = vertices[i + 2].y;
                z3 = vertices[i + 2].z;

                totalVolume +=
                    (-x3 * y2 * z1 +
                        x2 * y3 * z1 +
                        x3 * y1 * z2 -
                        x1 * y3 * z2 -
                        x2 * y1 * z3 +
                        x1 * y2 * z3);

                        i = i + 3
        }
        totalVolume = totalVolume / 6;
        volume = "\(totalVolume)"
        print("Volume Volume Volume Volume Volume Volume Volume :\(totalVolume)")
        lbl_valume.text = "\(clean(String(totalVolume))) cubic mm"
    }
    return[]
}
于 2019-02-01T08:16:57.053 回答
2

这是一个基于其他答案的 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
            }
        }
    }
}
于 2021-03-22T15:14:31.347 回答
1

使用 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 上(只有苹果好做 smth 简单所以痛苦)。所以我不得不调整 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 []
}
于 2017-06-06T13:37:07.073 回答
1

斯威夫特 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
    }
于 2018-10-21T18:32:09.153 回答
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);
    }
}
于 2018-06-29T10:22:28.117 回答