In SceneKit I'm trying to create a simple piece of custom geometry with a texture on it. I'm just trying to make a cube but have each face oriented properly so an SCNBox won't work.
The shape comes out perfectly fine, but the texture is mapped totally wrong.
This is what the geometry comes out looking like:
Based on what I understand, the texture coordinates for every face should be the same. But when I add the points for each face in the same order, it draws similarly incorrectly. So I played around with the order of the coordinates and got the front, back, and one side face to draw mostly correctly. I haven't been able to figure out a pattern or logic to what caused this though.
The front is how every face should look.
Here's how I'm making it:
public struct face {
var topLeft: SCNVector3
var topRight: SCNVector3
var bottomLeft: SCNVector3
var bottomRight: SCNVector3
}
func createCube(startFace: face, endFace: face) -> SCNGeometry {
var vertices: [SCNVector3] = []
var indices: [Int32] = []
var textCords: [vector_float2] = []
// Add table of contents to indices array because we're using polygons
let polygons = 6 // cube so 6 faces
indices.append(4) // front face
indices.append(4) // left face
indices.append(4) // right face
indices.append(4) // top face
indices.append(4) // bottom face
indices.append(4) // back face
// - Convenience Values -
// Indices offsets
let startFaceVertex: Int32 = 0
let endFaceVertex: Int32 = 4
// Vertex indices
let bottomLeftPos: Int32 = 0
let bottomRightPos: Int32 = 1
let topRightPos: Int32 = 2
let topLeftPos: Int32 = 3
// Texture Coordinates
let topLeft = CGPoint(x: 0, y: 1)
let topRight = CGPoint(x: 1, y: 1)
let bottomLeft = CGPoint(x: 0, y: 0)
let bottomRight = CGPoint(x: 1, y: 0)
// Add vertices
vertices.append(startFace.bottomLeft)
vertices.append(startFace.bottomRight)
vertices.append(startFace.topRight)
vertices.append(startFace.topLeft)
vertices.append(endFace.bottomLeft)
vertices.append(endFace.bottomRight)
vertices.append(endFace.topRight)
vertices.append(endFace.topLeft)
// Front Face
indices.append(startFaceVertex + bottomLeftPos)
indices.append(startFaceVertex + bottomRightPos)
indices.append(startFaceVertex + topRightPos)
indices.append(startFaceVertex + topLeftPos)
textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
// Left Face
indices.append(startFaceVertex + bottomLeftPos)
indices.append(endFaceVertex + bottomLeftPos)
indices.append(endFaceVertex + topLeftPos)
indices.append(startFaceVertex + topLeftPos)
textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
// Top Face
indices.append(endFaceVertex + topLeftPos)
indices.append(endFaceVertex + topRightPos)
indices.append(startFaceVertex + topRightPos)
indices.append(startFaceVertex + topLeftPos)
textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
// Right Face
indices.append(endFaceVertex + bottomRightPos)
indices.append(startFaceVertex + bottomRightPos)
indices.append(startFaceVertex + topRightPos)
indices.append(endFaceVertex + topRightPos)
textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
// Bottom Face
indices.append(startFaceVertex + bottomLeftPos)
indices.append(startFaceVertex + bottomRightPos)
indices.append(endFaceVertex + bottomRightPos)
indices.append(endFaceVertex + bottomLeftPos)
textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
// Back Face
indices.append(endFaceVertex + bottomLeftPos)
indices.append(endFaceVertex + bottomRightPos)
indices.append(endFaceVertex + topRightPos)
indices.append(endFaceVertex + topLeftPos)
textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
// Create geometry
let verticesSource = SCNGeometrySource(vertices: vertices)
let uvData = Data(bytes: textCords, count: textCords.count * MemoryLayout<vector_float2>.size)
let textureSource = SCNGeometrySource(data: uvData,
semantic: .texcoord,
vectorCount: textCords.count,
usesFloatComponents: true,
componentsPerVector: 2,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<vector_float2>.size)
let indexData = Data(bytes: indices,
count: indices.count * MemoryLayout<Int32>.size)
let elements = SCNGeometryElement(data: indexData,
primitiveType: .polygon,
primitiveCount: polygons,
bytesPerIndex: MemoryLayout<Int32>.size)
return SCNGeometry(sources: [verticesSource, textureSource], elements: [elements])
}
Solution
Got it working, here's my code for a working version:
public struct face {
var topLeft: SCNVector3
var topRight: SCNVector3
var bottomLeft: SCNVector3
var bottomRight: SCNVector3
}
let topLeft = CGPoint(x: 0, y: 1)
let topRight = CGPoint(x: 1, y: 1)
let bottomLeft = CGPoint(x: 0, y: 0)
let bottomRight = CGPoint(x: 1, y: 0)
func createCube(startFace: face, endFace: face) -> SCNGeometry {
var vertices: [SCNVector3] = []
var indexTable: [Int32] = []
var indices: [Int32] = []
var textCords: [vector_float2] = []
// Front Face
addFace(face: startFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)
// Left Face
let leftFace = face(topLeft: endFace.topLeft, topRight: startFace.topLeft, bottomLeft: endFace.bottomLeft, bottomRight: startFace.bottomLeft, center: SCNVector3Zero, originOffset: startFace.originOffset)
addFace(face: leftFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)
// Top Face
//let topFace = face(topLeft: startFace.topLeft, topRight: endFace.topLeft, bottomLeft: startFace.topRight, bottomRight: endFace.topRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
let topFace = face(topLeft: startFace.topLeft, topRight: endFace.topLeft, bottomLeft: startFace.topRight, bottomRight: endFace.topRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
addFace(face: topFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)
// Right Face
let rightFace = face(topLeft: startFace.topRight, topRight: endFace.topRight, bottomLeft: startFace.bottomRight, bottomRight: endFace.bottomRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
addFace(face: rightFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)
// Bottom Face
let bottomFace = face(topLeft: endFace.bottomLeft, topRight: startFace.bottomLeft, bottomLeft: endFace.bottomRight, bottomRight: startFace.bottomRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
addFace(face: bottomFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)
// Back Face
addFace(face: endFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)
// Create geometry
let verticesSource = SCNGeometrySource(vertices: vertices)
let uvData = Data(bytes: textCords, count: textCords.count * MemoryLayout<vector_float2>.size)
let textureSource = SCNGeometrySource(data: uvData,
semantic: .texcoord,
vectorCount: textCords.count,
usesFloatComponents: true,
componentsPerVector: 2,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<vector_float2>.size)
var finalIndices: [Int32] = []
finalIndices.append(contentsOf: indexTable)
finalIndices.append(contentsOf: indices)
let indexData = Data(bytes: finalIndices,
count: finalIndices.count * MemoryLayout<Int32>.size)
let elements = SCNGeometryElement(data: indexData,
primitiveType: .polygon,
primitiveCount: indexTable.count,
bytesPerIndex: MemoryLayout<Int32>.size)
return SCNGeometry(sources: [verticesSource, textureSource], elements: [elements])
}
fileprivate func addFace(face: face, textureOffset: CGPoint, textureSize: CGSize, toVertices: inout [SCNVector3], indexTable: inout [Int32], indices: inout [Int32], textCords: inout [vector_float2]) {
toVertices.append(face.topRight)
toVertices.append(face.topLeft)
toVertices.append(face.bottomLeft)
toVertices.append(face.bottomRight)
let polygonPointCount: Int32 = 4
indexTable.append(polygonPointCount)
for _ in 0..<polygonPointCount {
indices.append(Int32(indices.count))
}
textCords.append(vector_float2(Float(bottomRight.x + textureOffset.x + textureSize.width), Float(bottomRight.y + textureOffset.y)))
textCords.append(vector_float2(Float(bottomLeft.x + textureOffset.x + textureSize.width), Float(bottomLeft.y + textureOffset.y)))
textCords.append(vector_float2(Float(topLeft.x + textureOffset.x + textureSize.width), Float(topLeft.y + textureOffset.y)))
textCords.append(vector_float2(Float(topRight.x + textureOffset.x + textureSize.width), Float(topRight.y + textureOffset.y)))
}
