1
votes

I'm having issues getting the texture applied to a custom geometry, this is the result right now

Result

But I expect that each face has a different texture, and my texture is

Texture

This is the code that I've used to generate the cube, I tried so many things but nothing works for me, I know that something is wrong with what I'm doing but I don't get it.

Regards

2

2 Answers

1
votes

A single vertex can't have more than one texture coordinate. Since each vertex in a cube appears in a corner and is part of three different faces, you'll need to repeat the vertex data three times and match that up with the texture coordinates and then reference those with your indices.

Another way to look at it is that the same index that you're using to reference the vertex is also used to reference a texture coordinate, so since you only have 8 vertices, you'll only be able to reference the first 8 texture coordinates.

In one of the chapters from my book on SceneKit I create a custom cube geometry. Its texture coordinates display the complete texture on each face — so you'll have to change that part — but its indices should work for your cube as well:

// Indices that turn the source data into triangles
// ------------------------------------------------

int indices[] = {
    // bottom
    0, 2, 1,
    1, 2, 3,
    // back
    10, 14, 11,  // 2, 6, 3,   + 8
    11, 14, 15,  // 3, 6, 7,   + 8
    // left
    16, 20, 18,  // 0, 4, 2,   + 16
    18, 20, 22,  // 2, 4, 6,   + 16
    // right
    17, 19, 21,  // 1, 3, 5,   + 16
    19, 23, 21,  // 3, 7, 5,   + 16
    // front
    8,  9, 12,  // 0, 1, 4,   + 8
    9, 13, 12,  // 1, 5, 4,   + 8
    // top
    4, 5, 6,
    5, 7, 6
};

// Custom geometry data for a cube
// -------------------------------

SCNVector3 vertices[] = {
    SCNVector3Make(-halfSide, -halfSide,  halfSide),
    SCNVector3Make( halfSide, -halfSide,  halfSide),
    SCNVector3Make(-halfSide, -halfSide, -halfSide),
    SCNVector3Make( halfSide, -halfSide, -halfSide),
    SCNVector3Make(-halfSide,  halfSide,  halfSide),
    SCNVector3Make( halfSide,  halfSide,  halfSide),
    SCNVector3Make(-halfSide,  halfSide, -halfSide),
    SCNVector3Make( halfSide,  halfSide, -halfSide),

    // repeat exactly the same
    SCNVector3Make(-halfSide, -halfSide,  halfSide),
    SCNVector3Make( halfSide, -halfSide,  halfSide),
    SCNVector3Make(-halfSide, -halfSide, -halfSide),
    SCNVector3Make( halfSide, -halfSide, -halfSide),
    SCNVector3Make(-halfSide,  halfSide,  halfSide),
    SCNVector3Make( halfSide,  halfSide,  halfSide),
    SCNVector3Make(-halfSide,  halfSide, -halfSide),
    SCNVector3Make( halfSide,  halfSide, -halfSide),

    // repeat exactly the same
    SCNVector3Make(-halfSide, -halfSide,  halfSide),
    SCNVector3Make( halfSide, -halfSide,  halfSide),
    SCNVector3Make(-halfSide, -halfSide, -halfSide),
    SCNVector3Make( halfSide, -halfSide, -halfSide),
    SCNVector3Make(-halfSide,  halfSide,  halfSide),
    SCNVector3Make( halfSide,  halfSide,  halfSide),
    SCNVector3Make(-halfSide,  halfSide, -halfSide),
    SCNVector3Make( halfSide,  halfSide, -halfSide)
};

SCNVector3 normals[] = {
    // up and down
    SCNVector3Make( 0, -1, 0),
    SCNVector3Make( 0, -1, 0),
    SCNVector3Make( 0, -1, 0),
    SCNVector3Make( 0, -1, 0),

    SCNVector3Make( 0, 1, 0),
    SCNVector3Make( 0, 1, 0),
    SCNVector3Make( 0, 1, 0),
    SCNVector3Make( 0, 1, 0),

    // back and front
    SCNVector3Make( 0, 0,  1),
    SCNVector3Make( 0, 0,  1),
    SCNVector3Make( 0, 0, -1),
    SCNVector3Make( 0, 0, -1),

    SCNVector3Make( 0, 0, 1),
    SCNVector3Make( 0, 0, 1),
    SCNVector3Make( 0, 0, -1),
    SCNVector3Make( 0, 0, -1),

    // left and right
    SCNVector3Make(-1, 0, 0),
    SCNVector3Make( 1, 0, 0),
    SCNVector3Make(-1, 0, 0),
    SCNVector3Make( 1, 0, 0),

    SCNVector3Make(-1, 0, 0),
    SCNVector3Make( 1, 0, 0),
    SCNVector3Make(-1, 0, 0),
    SCNVector3Make( 1, 0, 0),
};

CGPoint UVs[] = {
    CGPointMake(0, 0), // bottom
    CGPointMake(1, 0), // bottom
    CGPointMake(0, 1), // bottom
    CGPointMake(1, 1), // bottom

    CGPointMake(0, 1), // top
    CGPointMake(1, 1), // top
    CGPointMake(0, 0), // top
    CGPointMake(1, 0), // top

    CGPointMake(0, 1), // front
    CGPointMake(1, 1), // front
    CGPointMake(1, 1), // back
    CGPointMake(0, 1), // back

    CGPointMake(0, 0), // front
    CGPointMake(1, 0), // front
    CGPointMake(1, 0), // back
    CGPointMake(0, 0), // back

    CGPointMake(1, 1), // left
    CGPointMake(0, 1), // right
    CGPointMake(0, 1), // left
    CGPointMake(1, 1), // right

    CGPointMake(1, 0), // left
    CGPointMake(0, 0), // right
    CGPointMake(0, 0), // left
    CGPointMake(1, 0), // right
};
0
votes

Whilst this is a different approach, I have made a Box Class which will render each side correctly:

class Box: SCNNode {

private var faceArray = [SCNMaterial]()

/// Creates An SCNBox With Either A Colour Or UIImage For Each Of It's Faces
/// (Either An Array [Colour] Or [UIImage] Must Be Input)
/// - Parameters:
///   - width: Optional CGFloat (Defaults To 20cm)
///   - height: Optional CGFloat (Defaults To 20cm)
///   - length: Optional CGFloat (Defaults To 20cm)
///   - colours: Optional [UIColor] - [Front, Right, Back, Left, Top, Bottom]
///   - images: Optional [UIImage] - [Front, Right, Back, Left, Top, Bottom]
init(width: CGFloat = 0.2, height: CGFloat = 0.2, length: CGFloat = 0.2, colours: [UIColor]?, images: [UIImage]?) {

    super.init()

    self.geometry = SCNBox(width: width, height: height, length: length, chamferRadius: 0)

    var sideArray = [Any]()

    if colours == nil{
        guard let imageArray = images else { return }
        sideArray = imageArray
    }else{
        guard let coloursArray = colours else { return }
        sideArray = coloursArray
    }

    for index in 0 ..< 6{
        let face = SCNMaterial()
        face.diffuse.contents = sideArray[index]
        faceArray.append(face)
    }

    self.geometry?.materials = faceArray

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
   }

}

You can initialise it like so, and by rotating can see each face is rendered correctly:

    let cube = Box(colours: [.red, .green, .cyan, .yellow, .blue, .black], images: nil)
    scene.rootNode.addChildNode(cube)

    let rotateAction = SCNAction.rotate(by: .pi * 2, around: SCNVector3(1, 0, 0), duration: 10)

    cube.runAction(rotateAction)