【问题标题】:Inefficient parsing of Vertex and Index data, looking for more efficient methodsVertex和Index数据解析效率低下,寻找更高效的方法
【发布时间】:2017-04-05 14:36:03
【问题描述】:

我写了一个方法来解析一个包含顶点数据的数组。该方法的目标是根据该数据生成一个新的唯一顶点数组和一个新索引。

这是我用来在数组中存储顶点的结构。

struct Vertex: Hashable {
    var x, y, z,
    nx, ny, nz,
    s, t: Float

    var hashValue: Int {
        return "\(self.x),\(self.y),\(self.z),\(self.nx),\(self.ny),\(self.nz),\(self.s),\(self.t),".hashValue
    }

    static func ==(lhs: Vertex, rhs: Vertex) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
}

这是我用来创建唯一顶点数组和新索引的方法。该方法的第一个参数采用一个数组,其中顶点数据已经按原始索引排序。

func makeVertexIndex(_ array: [Float]) -> ([Vertex], [Int]) {
    var counter = 0
    var indexCounter = 0
    var holder = [Float]()
    var vertices = [Vertex]()
    var index = [Int]()

    for i in array {
        counter += 1

        if counter == 8 {
            counter = 0
            holder.append(i)
            let vertex = Vertex(x: holder[0], y: holder[1], z: holder[2],
                                nx: holder[3], ny: holder[4], nz: holder[5],
                                s: holder[6], t: holder[7])

            if vertices.contains(vertex) {
                guard let match = vertices.index(of: vertex) else { continue }
                index.append(match)
            } else {
                vertices.append(vertex)
                index.append(indexCounter)
                indexCounter += 1
            }

            holder.removeAll()
        } else {
            holder.append(i)
        }
    }

    return (vertices, index)
}

我能够成功解析三角形计数较低的网格,但是当我尝试在三角形计数较高的网格上运行时,它需要一个多小时才能运行。

我对编码很陌生,Swift 是我的第一语言,但我怀疑完成这项任务不应该花这么长时间,而且我可能只是写我的方法效率很低,或者也许有一种完全不同的方法我可以接近这个问题。

无论如何,如果我能得到任何帮助,我将不胜感激。感谢阅读。

更新 1: 我重新编写了创建顶点数组的方法,然后将其更改为一组以使值唯一并返回到数组,然后通过 vertexArray 循环运行它,以查找唯一顶点数组中的匹配项。此版本的方法将处理时间从我的测试网格上的 21 秒缩短到大约 12 秒。

func makeVertexIndex(_ array: [Float]) -> ([Vertex], [Int]) {
    var counter = 0
    var holder = [Float]()
    var vertexArray = [Vertex]()
    var vertices = [Vertex]()
    var index = [Int]()

    for i in array {
        counter += 1

        if counter == 8 {
            counter = 0
            holder.append(i)
            let vertex = Vertex(x: holder[0], y: holder[1], z: holder[2],
                                nx: holder[3], ny: holder[4], nz: holder[5],
                                s: holder[6], t: holder[7])

            vertexArray.append(vertex)

            holder.removeAll()
        } else {
            holder.append(i)
        }
    }

    let vertexSet = Set(vertexArray)
    vertices = Array(vertexSet)

    for v in vertexArray {
        guard let match = vertices.index(of: v) else { continue }
        index.append(match)
    }

    return (vertices, index)
}

更新 2:

这是我在实施一些推荐的解决方案后更新的结构和方法。

结构:

struct Vertex: Hashable {
    var x, y, z,
    nx, ny, nz,
    s, t: Float

    var hashValue: Int {
        return "\(self.x),\(self.y),\(self.z),\(self.nx),\(self.ny),\(self.nz),\(self.s),\(self.t)".hashValue
    }

    static func ==(lhs: Vertex, rhs: Vertex) -> Bool {
        return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z) && (lhs.nx == rhs.nx) &&
            (lhs.ny == rhs.ny) && (lhs.nz == rhs.nz) && (lhs.s == rhs.s) && (lhs.t == rhs.t)
    }
}

方法:

    func makeVertexIndex(_ array: [Float]) -> ([Vertex], [Int]) {
        var vertexArray = [Vertex]()
        vertexArray.reserveCapacity(array.count / 8)

        var vertices = [Vertex]()
        var index = [Int]()

        // Creating an array of Vertex from an array containing 
        // position/normal/texcoord in correct order.
        for i in stride(from: 0, to: array.count, by: 8) {
            let vertex = Vertex(x: array[i], y: array[i + 1], z: array[i + 2],
                                nx: array[i + 3], ny: array[i + 4], nz: array[i + 5],
                                s: array[i + 6], t: array[i + 7])

            vertexArray.append(vertex)
        }

        // Making the Vertex array unique by converting to set and back to array.
        let vertexSet = Set(vertexArray)
        vertices = Array(vertexSet)

        // Making new index by finding the matching vertex in the 
        // unique vertex array and adding that array index to the new index
        for v in vertexArray {
            guard let match = vertices.index(of: v) else { continue }
            index.append(match)
        }

        return (vertices, index)
    }

在尝试了推荐解决方案的各个部分后,该方法能够在 13 分钟内处理一个包含 70K 三角形的模型,而之前需要一个半小时以上。

所以这是一个巨大的改进,感谢迄今为止的所有解决方案,将保持开放状态可能一两天,看看是否有其他建议。

【问题讨论】:

    标签: arrays swift scenekit metal


    【解决方案1】:

    您的 == 代码比较哈希而不是每个属性是否有特殊原因?即

    static func ==(lhs: Vertex, rhs: Vertex) -> Bool {
        return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z) &&
            (lhs.nx == rhs.nx) && ...etc
    }
    

    然后如果lhs.x 不等于rhs.x,代码将很快失败并继续下一个。目前,您必须一次又一次地创建哈希值。或者,您可以通过在构造函数中计算一次哈希来加快当前代码的速度(例如,使所有属性 private(set) 如下面的简化示例所示)。

    struct Vertex {
        private(set) var x, y: Float
        let hash: Int
    
        init(x: Float, y: Float) {
            self.x = x
            self.y = y
            hash = "\(x),\(y)".hashValue
        }
    
        static func ==(lhs: Vertex, rhs: Vertex) -> Bool {
            return lhs.x == rhs.x
        }
    }
    
    let vertex = Vertex(x: 1.0, y: 2.0)
    print(vertex.hash)
    

    【讨论】:

    • 我目前正在测试您的解决方案,到目前为止,仅对 func == 进行更改就产生了巨大的影响,从 12 秒缩短到 3 秒。天哪,非常感谢。
    • 我现在将尝试实现您帖子的第二部分,当我尝试删除 var hashValue: Int { ... }我的结构的一部分
    • 我认为他在那里犯了一个小错字,该变量必须被称为 hashValue 以符合 Hashable。
    • 我能够为结构实现初始化程序而没有收到任何错误,但是每当我运行导入 3d 模型的方法时,它什么都不显示。但是我看到了你的初始化器逻辑,我会尝试更多地摆弄它,看看我是否可以让它工作。
    【解决方案2】:

    看起来您正试图以一组新的顶点和一组索引结束。如果新的顶点集包含重复,则将其从顶点列表中删除,并将重复的索引加倍。从你的第一个功能来看。您的第二个仅在匹配时添加了索引。你是什​​么意思?

    从一开始......如果你有 V1 V2 V3 V4,并且 V4 = V1,你需要索引 [0,0,1,2]。

    这是正确的吗?我会通过一次迭代浮点数组 8 来清理它,然后确定是否有重复,如果有,找出哪些是重复的,然后创建最后两个数组。这是我的看法..

    func makeVertexIndex(_ array: [Float]) -> ([Vertex], [Int]) {
    
        var newVertices = [Vertex]()
        for i in stride(from: 0, to: array.count, by: 8) {
            newVertices.append(Vertex(x: array[i], y: array[i + 1], z: array[i + 2],
                                   nx: array[i + 3], ny: array[i + 4], nz: array[i + 5],
                                   s: array[i + 6], t: array[i + 7]))
    
        }
    
    
        if newVertices.count > Set(newVertices).count {
            var uniqueVertices = [Vertex]()
            var indices = [Int]()
    
            var vertexIndex = [Vertex: Int]()
            var count = 0
    
            for vertex in newVertices {
                if let existingVertex = vertexIndex[vertex] {
                    indices.append(existingVertex)
                } else {
                    vertexIndex[vertex] = count
                    uniqueVertices.append(vertex)
                    count += 1
                }
            }
    
            return (uniqueVertices, indices.sorted())
    
        } else {
            return (newVertices, [Int](0..<newVertices.count))
    
        }
    }
    

    【讨论】:

    • 抱歉我的代码不是很清楚。基本上在我方法的最后一部分,vertexArray 循环用于创建新索引。 vertexArray 以正确的顺序包含所有顶点,包括重复项。因此,每次在唯一顶点数组中找到匹配项时,它都会存储索引值,以便稍后在 SCNGeometryElement 对象中使用。
    • 感谢您抽出宝贵时间发帖,我将尝试您的解决方案,看看会得到什么结果。
    • 没有汗水。上面的代码从顶点数组中过滤掉重复项,并返回唯一的顶点和一个索引数组,每当找到第一个副本的索引的重复项或三次项等时,都会将另一个条目添加到索引中。
    • stride(from:to:by:) 方法太酷了,我不知道!
    猜你喜欢
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    相关资源
    最近更新 更多