Extracting vertices from scenekit

后端 未结 7 447
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-03 01:42

I\'m having a problem with understanding scenekit geometery.

I have the default cube from Blender, and I export as collada (DAE), and can bring it into scenekit....

相关标签:
7条回答
  • 2020-12-03 02:08

    With swift 3.1 you can extract vertices from SCNGeometry in a much faster and shorter way:

    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 []
    }
    

    ... Today i've noted that on osx this not going to work correct. This happens because on iOS SCNVector3 build with Float and on osx CGFloat (only apple good do smth simple so suffering). So I had to tweak the code for osx but this not gonna work as fast as on 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 []
    }
    
    0 讨论(0)
  • 2020-12-03 02:08

    The Swift 3 version:

        // `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
        }
    
    0 讨论(0)
  • 2020-12-03 02:14

    The Swift Version

    The Objective-C version and this are essentially identical.

    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?
    }
    
    0 讨论(0)
  • 2020-12-03 02:17

    For someone like me want to extract data of face from SCNGeometryElement.

    Notice I only consider primtive type is triangle and index size is 2 or 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);
        }
    }
    
    0 讨论(0)
  • 2020-12-03 02:19

    The geometry source

    When you call geometrySourcesForSemantic: you are given back an array of SCNGeometrySource objects with the given semantic in your case the sources for the vertex data).

    This data could have been encoded in many different ways and a multiple sources can use the same data with a different stride and offset. The source itself has a bunch of properties for you to be able to decode the data like for example

    • dataStride
    • dataOffset
    • vectorCount
    • componentsPerVector
    • bytesPerComponent

    You can use combinations of these to figure out which parts of the data to read and make vertices out of them.

    Decoding

    The stride tells you how many bytes you should step to get to the next vector and the offset tells you how many bytes offset from the start of that vector you should offset before getting to the relevant pars of the data for that vector. The number of bytes you should read for each vector is componentsPerVector * bytesPerComponent

    Code to read out all the vertices for a single geometry source would look something like this

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

    The geometry element

    This will give you all the vertices but won't tell you how they are used to construct the geometry. For that you need the geometry element that manages the indices for the vertices.

    You can get the number of geometry elements for a piece of geometry from the geometryElementCount property. Then you can get the different elements using geometryElementAtIndex:.

    The element can tell you if the vertices are used a individual triangles or a triangle strip. It also tells you the bytes per index (the indices may have been ints or shorts which will be necessary to decode its data.

    0 讨论(0)
  • 2020-12-03 02:21

    Here is an extension method if the data isn't contiguous (the vector size isn't equal to the stride) which can be the case when the geometry is loaded from a DAE file. It also doesn't use copyByte function.

    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
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题