问题
I wrote a method to parse an array containing vertex data. The goal of the method was to produce a new array of unique vertices and a new index from that data.
This is the struct I used to store the vertices in the array.
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
}
}
And this is the method I used to create the array of unique vertices and new index. The first argument of the method takes an array with the vertex data already ordered by the original index.
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)
}
I was able to successfully parse lower triangle count meshes, but when I try to run this on higher triangle count meshes it took over an hour to run.
I'm pretty new to coding, Swift is my first language, but I suspect it shouldn't take this long to do this task and I probably just wrote my method really inefficiently or maybe there's a completely different way I could approach this problem.
Anyhow I'd appreciate any help I could get. Thanks for reading.
Update 1: I re-wrote my method to create the vertex array first then change to it to a set to make the values unique and back to an array then ran it through the vertexArray loop looking for matches in the unique vertices array. This version of the method cut the process time from 21 seconds on my test mesh to about 12 seconds.
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)
}
Update 2:
Here are my updated struct and method after implementing some of the recommended solutions.
Struct:
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)
}
}
Method:
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)
}
After trying out various parts of the recommended solutions the method was able to process a model with 70K triangles in 13 minutes, previously it took over an hour and a half.
So this was a huge improvement, appreciate all the solutions so far, going to keep this open for maybe a day or two to see if any other recommendations come.
回答1:
Is there a particular reason why your == code compares hashes rather than each property? i.e.
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
}
Then if lhs.x
is not equal to rhs.x
, the code will fail quickly and move on to the next. Currently you're having to create a hash value again and again. Alternatively, you could speed up your current code by calculating the hash once in the constructor (and, say, making all the properties private(set)
like in the simplified example below).
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)
回答2:
It looks like you're trying to end up with a new set of Vertices, and a set of indices. If the new set of vertices contains a duplicate, remove that from the list of vertices, and double the index of the duplicate. Judging from your first function. Your second only added an index when there was a match. Which did you mean?
From the first.. if you have V1 V2 V3 V4, and V4 = V1, you want indices [0,0,1,2].
Is this correct? I would clean that up by iterating over the float array 8 at a time, then determine if there are duplicates, if so, figure out which are duplicates and then create your final two arrays. Here's my take..
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))
}
}
来源:https://stackoverflow.com/questions/40733050/inefficient-parsing-of-vertex-and-index-data-looking-for-more-efficient-methods