Repeating a texture over a plane in SceneKit
Asked Answered
P

4

14

I have a 32x32 .png image that I want to repeat over a SCNPlane. The code I've got (See below) results in the image being stretched to fit the size of the plane, rather than repeated.

CODE:

let planeGeo = SCNPlane(width: 15, height: 15)

let imageMaterial = SCNMaterial()
imageMaterial.diffuse.contents = UIImage(named: "art.scnassets/grid.png")

planeGeo.firstMaterial = imageMaterial

let plane = SCNNode(geometry: planeGeo)

plane.geometry?.firstMaterial?.diffuse.wrapS = SCNWrapMode.repeat
plane.geometry?.firstMaterial?.diffuse.wrapT = SCNWrapMode.repeat
Personalty answered 5/7, 2017 at 8:11 Comment(0)
P
14

I fixed it. It seems like the image was zoomed in. If I do imageMaterial.diffuse.contentsTransform = SCNMatrix4MakeScale(32, 32, 0), the image repeats.

Personalty answered 5/7, 2017 at 8:32 Comment(3)
find out a way to do this in the GUI?Suzysuzzy
@Suzysuzzy Unfortunately not. My best guess would be to use sprites in some way, but I haven't looked at it enough to give a good answer.Personalty
I posted an answer on how to do this in the GUI rather than as a comment since I know it can be hard sometimes to navigate through the SceneKit editor.Diego
T
13

I faced an identical issue when implementing plane visualisation in ARKit. I wanted to visualise the detected plane as a checkerboard pattern. I fixed it by creating a custom SCNNode called a "PlaneNode" with a correctly configured SCNMaterial. The material uses wrapS, wrapT = .repeat and calculates the scale correctly based on the size of the plane itself.

Looks like this:

enter image description here

Have a look at the code below, the inline comments contain the explanation.

class PlaneNode : SCNNode {

    init(planeAnchor: ARPlaneAnchor) {
        super.init()
        // Create the 3D plane geometry with the dimensions reported
        // by ARKit in the ARPlaneAnchor instance
        let planeGeometry = SCNPlane(width:CGFloat(planeAnchor.extent.x), height:CGFloat(planeAnchor.extent.z))
        // Instead of just visualizing the grid as a gray plane, we will render
        // it in some Tron style colours.
        let material = SCNMaterial()
        material.diffuse.contents = PaintCode.imageOfViewARPlane
        //the scale gives the number of times the image is repeated
        //ARKit givest the width and height in meters, in this case we want to repeat
        //the pattern each 2cm = 0.02m so we divide the width/height to find the number of patterns
        //we then round this so that we always have a clean repeat and not a truncated one
        let scaleX = (Float(planeGeometry.width)  / 0.02).rounded()
        let scaleY = (Float(planeGeometry.height) / 0.02).rounded()
        //we then apply the scaling
        material.diffuse.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
        //set repeat mode in both direction otherwise the patern is stretched!
        material.diffuse.wrapS = .repeat
        material.diffuse.wrapT = .repeat
        //apply material
        planeGeometry.materials = [material];
        //make a node for it
        self.geometry = planeGeometry
        // Move the plane to the position reported by ARKit
        position.x = planeAnchor.center.x
        position.y = 0
        position.z = planeAnchor.center.z
        // Planes in SceneKit are vertical by default so we need to rotate
        // 90 degrees to match planes in ARKit
        transform =  SCNMatrix4MakeRotation(-Float.pi / 2.0, 1.0, 0.0, 0.0);
    }

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

    func update(planeAnchor: ARPlaneAnchor) {
        guard let planeGeometry = geometry as? SCNPlane else {
            fatalError("update(planeAnchor: ARPlaneAnchor) called on node that has no SCNPlane geometry")
        }
        //update the size
        planeGeometry.width = CGFloat(planeAnchor.extent.x)
        planeGeometry.height = CGFloat(planeAnchor.extent.z)
        //and material properties
        let scaleX = (Float(planeGeometry.width)  / 0.02).rounded()
        let scaleY = (Float(planeGeometry.height) / 0.02).rounded()
        planeGeometry.firstMaterial?.diffuse.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
        // Move the plane to the position reported by ARKit
        position.x = planeAnchor.center.x
        position.y = 0
        position.z = planeAnchor.center.z

    }
}
Tetrastichous answered 18/11, 2017 at 9:8 Comment(1)
nicely illustratedMamiemamma
D
1

To do this in the SceneKit editor, select your plane (add one if needed) in the scene and then select the "Material Inspector" tab on the top right. Then, under "Properties" and where it says "Diffuse", select your texture. Now, expand the diffuse section by clicking the carat to the left of "Diffuse" and go down to where it says "Scale". Here, you can increase the scaling so that the texture can look repeated rather than stretched. For this question, the OP would have to set the scaling to 32x32.

enter image description here

Diego answered 12/4, 2019 at 16:52 Comment(0)
B
0

You can learn it from Scene kit viewer Suppose You have SCNplane in your scene kit

Create scene file drag a plane

Which size is 12 inches in meter it is 0.3048

and select image in diffuse

enter image description here

now You have image with 4 Grid as shown in image

enter image description here

we want each box to be show in each inches so for 12 Inches we need 12 box * 12 box as we have 12 inches box

to calculate it. First we need convert 0.3048 meter to inches

which is meters / 0.0254 answer is 12.

but we need each grid to show in each inch so we also need to divide 12 / 4 = 3

now goto show material inspector and change scale value to 3

you can see 12 boxes for 12 inch plane.

Hope it is helpful

Bushwhack answered 15/11, 2018 at 7:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.