Section 4 - Add Textured Paints

Add controls to enable the textured paint feature of Remodel-AR.

Step 1

Start with the code from “Add Export Features”.

//
//  ContentView.swift
//  Painty
//sw

import RemodelAR
import SwiftUI

struct ContentView: View {
    @ObservedObject var model = ARStateModel()

    @State private var colorIndex = 0
    @State private var showStroke = true

    var body: some View {
        ZStack {
            ZStack(alignment: .bottom, content: {
                arView
                    .edgesIgnoringSafeArea(.all)
                VStack {
                    Spacer()
                    HStack {
                        savePhotoButton
                        save3DModelButton
                        resetSceneButton
                    }
                    colorPicker
                }
            })
        }
        .onAppear {
            model.pickColor(paint: colorItems[0])
        }
    }
}

private extension ContentView {
    var arView: ARView {
        RemodelARLib.makeARView(model: model, arMethod: .Lidar)
    }

    var colorItems: [WallPaint] {
        let numHues = 20
        var colors = [WallPaint]()
        for i in 0..<numHues {
            let color_8_8 = Color(hue: Double(i)/Double(numHues),
                                  saturation: 0.8,
                                  brightness: 0.8)
            let color_8_6 = Color(hue: Double(i)/Double(numHues),
                                  saturation: 0.8,
                                  brightness: 0.6)
            let color_8_4 = Color(hue: Double(i)/Double(numHues),
                                  saturation: 0.8,
                                  brightness: 0.4)
            let color_8_2 = Color(hue: Double(i)/Double(numHues),
                                  saturation: 0.8,
                                  brightness: 0.2)

            colors.append(WallPaint(id: "\(i * 4 + 1)", color: color_8_8.uiColor()))
            colors.append(WallPaint(id: "\(i * 4 + 2)", color: color_8_6.uiColor()))
            colors.append(WallPaint(id: "\(i * 4 + 3)", color: color_8_4.uiColor()))
            colors.append(WallPaint(id: "\(i * 4 + 4)", color: color_8_2.uiColor()))
        }
        return colors
    }

    var colorPicker: some View {
        ScrollView(.horizontal) {
            HStack {
                ForEach(0..<colorItems.count) { i in
                    Button(action: {
                        showStroke = true
                        colorIndex = i
                        model.pickColor(paint: colorItems[i])
                    }) {
                        RoundedRectangle(cornerRadius: 17)
                            .strokeBorder(lineWidth: (showStroke && i == colorIndex) ? 5 : 0)
                            .foregroundColor(.white)
                            .background(Color(colorItems[i].color))
                            .clipShape(RoundedRectangle(cornerRadius: 17))
                            .frame(width: 74, height: 74)
                            .animation(Animation.interpolatingSpring(stiffness: 60, damping: 15))
                    }
                    .onTapGesture {
                        self.showStroke = true
                    }
                }
            }
            .padding()
        }
    }
}

private extension ContentView {
    var savePhotoButton: some View {
        Button(action: {
            model.sharePhoto()
        }, label: {
            Image(systemName: "camera.fill")
                .foregroundColor(.white)
        })
        .padding()
        .background(Color(.sRGB, white: 0, opacity: 0.15))
        .cornerRadius(10)
    }

    var save3DModelButton: some View {
        Button(action: {
            model.save3DModel()
        }, label: {
            Image("saveMesh")
                .foregroundColor(.white)
        })
        .padding()
        .background(Color(.sRGB, white: 0, opacity: 0.15))
        .cornerRadius(10)
    }

    var resetSceneButton: some View {
        Button(action: {
            model.resetScene()
        }, label: {
            Image("reset")
                .foregroundColor(.white)
        })
        .padding()
        .background(Color(.sRGB, white: 0, opacity: 0.15))
        .cornerRadius(10)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Step 2

Create the required @State variables.

    @State private var textureIndex = -1
    @State private var showTextureStroke = false

Step 3

Create a new extension to the ContentView. Add a variable to return an array of texture images.

private extension ContentView {
    var textureNames: [String] {
        [
            "chalk",
            "concrete",
            "concreteLines",
            "corium",
            "ebdaa",
            "elora",
            "glostex",
            "graniteArenal",
            "khayalBeauty010",
            "linetex",
            "marmo",
            "marotex",
            "mashasco",
            "newtex",
            "rawa",
            "rawaKothban",
            "said",
            "texture",
            "tourmaline",
            "worood"
        ]
    }

    var textureImages: [UIImage] {
        textureNames.compactMap({ UIImage(named: $0) })
    }
}

Step 4

Add a variable to the extension that creates a texture picker view

    var texturePicker: some View {
        ScrollView(.horizontal) {
            HStack {
                ForEach(0..<textureImages.count) { i in
                    Button(action: {
                        if i == textureIndex {
                            showTextureStroke = false
                            textureIndex = -1
                            model.pickTexture(texture: nil)
                        } else {
                            showTextureStroke = true
                            textureIndex = i
                            model.pickTexture(texture: textureImages[i])
                        }
                    }) {
                        RoundedRectangle(cornerRadius: 17)
                            .strokeBorder(lineWidth: (showTextureStroke && i == textureIndex) ? 5 : 0)
                            .foregroundColor(.white)
                            .background(Image(uiImage: textureImages[i]))
                            .clipShape(RoundedRectangle(cornerRadius: 17))
                            .frame(width: 74, height: 74)
                            .animation(Animation.interpolatingSpring(stiffness: 60, damping: 15))
                    }
                    .onTapGesture {
                        self.showTextureStroke.toggle()
                    }
                }
            }
            .padding()
        }
    }

Step 5

Modify the view body to add the newly created texturePicker view.

    var body: some View {
        ZStack {
            ZStack(alignment: .bottom, content: {
                arView
                    .edgesIgnoringSafeArea(.all)
                VStack {
                    Spacer()
                    HStack {
                        savePhotoButton
                        save3DModelButton
                        resetSceneButton
                    }
                    VStack(spacing: -20) {
                        texturePicker
                        colorPicker
                    }
                }
            })
        }
        .onAppear {
            model.pickColor(paint: colorItems[0])
        }
    }

Step 6

Build and run your project. You will see a texture picker has been added to the view. Scroll through the texture picker and select different textures before tapping on the walls. You should be able to paint walls with different textures now. To remove a texture from a wall, tap the selected texture to deselect it, then tap on the wall to paint without a texture.

If at any point you need help from the Passio team, please reach out to us at support@passiolife.com

Last updated