Подтвердить что ты не робот

Извлечение вершин из сцены

У меня возникла проблема с пониманием геометрии сценки.

У меня есть куб по умолчанию от Blender, и я экспортирую его как collada (DAE), и могу привести его в scenekit.... все хорошо.

Теперь я хочу видеть вершины для куба. В DAE я вижу следующее для "Cube-mesh-locations-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

4b9b3361

Ответ 1

Источник геометрии

Когда вы вызываете geometrySourcesForSemantic: вам возвращается массив объектов SCNGeometrySource с заданной семантикой в вашем случае источниками данных вершин).

Эти данные могли быть закодированы разными способами, и несколько источников могут использовать одни и те же данные с разным stride и offset. Сам источник имеет множество свойств, чтобы вы могли декодировать data как, например,

  • dataStride
  • dataOffset
  • vectorCount
  • componentsPerVector
  • bytesPerComponent

Вы можете использовать их комбинации, чтобы выяснить, какие части data нужно прочитать, и сделать из них вершины.

расшифровка

Шаг показывает, сколько байтов нужно выполнить, чтобы перейти к следующему вектору, а смещение показывает, сколько байтов смещено от начала этого вектора, которое вы должны сместить, прежде чем перейти к соответствующим частям данных для этого вектора. Число байтов, которые вы должны прочитать для каждого вектора, это componentPerVector * 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 или short что будет необходимо для декодирования его data.

Ответ 2

Быстрая версия

Версия 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?
}

Ответ 3

С быстрым 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 build с 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 []
}

Ответ 4

Вот метод расширения, если данные не являются смежными (размер вектора не равен шагу), который может иметь место, когда геометрия загружается из файла 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
        }
    }
}

Ответ 5

Версия 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
    }

Ответ 6

Для таких, как я, хочу извлечь данные лица из 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

//вызвать эту функцию _ = вершины (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[]
}