├── app
├── .gitignore
├── app-debug.apk
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_undo.png
│ │ │ │ ├── ic_colors.png
│ │ │ │ ├── ic_clear_all.png
│ │ │ │ ├── ic_fab_menu.png
│ │ │ │ └── ic_line_thickness.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_undo.png
│ │ │ │ ├── ic_colors.png
│ │ │ │ ├── ic_clear_all.png
│ │ │ │ ├── ic_fab_menu.png
│ │ │ │ └── ic_line_thickness.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_colors.png
│ │ │ │ ├── ic_undo.png
│ │ │ │ ├── ic_fab_menu.png
│ │ │ │ ├── ic_clear_all.png
│ │ │ │ └── ic_line_thickness.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_undo.png
│ │ │ │ ├── ic_colors.png
│ │ │ │ ├── ic_clear_all.png
│ │ │ │ ├── ic_fab_menu.png
│ │ │ │ └── ic_line_thickness.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-anydpi
│ │ │ │ ├── ic_clear_all.xml
│ │ │ │ ├── ic_fab_menu.xml
│ │ │ │ ├── ic_undo.xml
│ │ │ │ ├── ic_line_thickness.xml
│ │ │ │ └── ic_colors.xml
│ │ │ ├── anim
│ │ │ │ ├── fab_rotate_anitclock.xml
│ │ │ │ ├── fab_rotate_clock.xml
│ │ │ │ ├── fab_open.xml
│ │ │ │ └── fab_close.xml
│ │ │ ├── drawable
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── raw
│ │ │ │ ├── screenquad_vertex.glsl
│ │ │ │ ├── screenquad_fragment_oes.glsl
│ │ │ │ ├── line_frag.glsl
│ │ │ │ └── line_vert.glsl
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── dialog_line.xml
│ │ │ │ └── content_main.xml
│ │ ├── ic_launcher-playstore.png
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── yashk2000
│ │ │ │ └── arpaint
│ │ │ │ ├── Rendering
│ │ │ │ ├── Ray.java
│ │ │ │ ├── BiquadFilter.java
│ │ │ │ ├── ShaderUtil.java
│ │ │ │ ├── LineUtils.java
│ │ │ │ ├── BackgroundRenderer.java
│ │ │ │ └── LineShaderRenderer.java
│ │ │ │ ├── Utils
│ │ │ │ ├── PermissionsHelper.java
│ │ │ │ ├── Settings.java
│ │ │ │ └── DisplayRotationHelper.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── yashk2000
│ │ │ └── arpaint
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── yashk2000
│ │ └── arpaint
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── vcs.xml
├── misc.xml
├── runConfigurations.xml
├── gradle.xml
└── codeStyles
│ └── Project.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='ARPaint'
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/app/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/app-debug.apk
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ARPaint
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-hdpi/ic_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-mdpi/ic_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-hdpi/ic_colors.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-mdpi/ic_colors.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xhdpi/ic_colors.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xhdpi/ic_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xxhdpi/ic_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_clear_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-hdpi/ic_clear_all.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_fab_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-hdpi/ic_fab_menu.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_clear_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-mdpi/ic_clear_all.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_fab_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-mdpi/ic_fab_menu.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_fab_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xhdpi/ic_fab_menu.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xxhdpi/ic_colors.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_clear_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xhdpi/ic_clear_all.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_clear_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xxhdpi/ic_clear_all.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_fab_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xxhdpi/ic_fab_menu.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_line_thickness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-hdpi/ic_line_thickness.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_line_thickness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-mdpi/ic_line_thickness.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_line_thickness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xhdpi/ic_line_thickness.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_line_thickness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/drawable-xxhdpi/ic_line_thickness.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yashk2000/ARPaint/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #36DCD7
4 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun May 10 22:10:12 IST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Rendering/Ray.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Rendering;
2 |
3 | import javax.vecmath.Vector3f;
4 |
5 | public class Ray {
6 | public final Vector3f origin;
7 | public final Vector3f direction;
8 |
9 | public Ray(Vector3f origin, Vector3f direction) {
10 | this.origin = origin;
11 | this.direction = direction;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-anydpi/ic_clear_all.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-anydpi/ic_fab_menu.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/yashk2000/arpaint/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import org.junit.Test;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fab_rotate_anitclock.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fab_rotate_clock.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-anydpi/ic_undo.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-anydpi/ic_line_thickness.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fab_open.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fab_close.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
15 |
16 |
17 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-anydpi/ic_colors.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/screenquad_vertex.glsl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | attribute vec4 a_Position;
17 | attribute vec2 a_TexCoord;
18 |
19 | varying vec2 v_TexCoord;
20 |
21 | void main() {
22 | gl_Position = a_Position;
23 | v_TexCoord = a_TexCoord;
24 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/yashk2000/arpaint/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import android.content.Context;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 | import androidx.test.platform.app.InstrumentationRegistry;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * @see Testing documentation
15 | */
16 | @RunWith(AndroidJUnit4.class)
17 | public class ExampleInstrumentedTest {
18 | @Test
19 | public void useAppContext() {
20 | // Context of the app under test.
21 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
22 |
23 | assertEquals("com.yashk2000.arpaint", appContext.getPackageName());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Utils/PermissionsHelper.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Utils;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.pm.PackageManager;
6 | import androidx.core.app.ActivityCompat;
7 | import androidx.core.content.ContextCompat;
8 |
9 | public class PermissionsHelper {
10 | private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA;
11 | private static final int CAMERA_PERMISSION_CODE = 5;
12 |
13 | public static boolean hasCameraPermission(Activity activity) {
14 | return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION)
15 | == PackageManager.PERMISSION_GRANTED;
16 | }
17 |
18 | public static void requestCameraPermission(Activity activity) {
19 | ActivityCompat.requestPermissions(
20 | activity, new String[] {CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/screenquad_fragment_oes.glsl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 | #extension GL_OES_EGL_image_external : require
16 |
17 | precision mediump float;
18 | varying vec2 v_TexCoord;
19 | uniform samplerExternalOES sTexture;
20 |
21 |
22 | void main() {
23 | gl_FragColor = texture2D(sTexture, v_TexCoord);
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Utils/Settings.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Utils;
2 |
3 | import javax.vecmath.Vector3f;
4 |
5 | public class Settings {
6 | private static Vector3f color = new Vector3f(1f, 1f, 1f);
7 | private static final float strokeDrawDistance = 0.125f;
8 | private static final float minDistance = 0.000625f;
9 | private static final float nearClip = 0.001f;
10 | private static final float farClip = 100.0f;
11 |
12 | public static Vector3f getColor() {
13 | return color;
14 | }
15 |
16 | public static float getStrokeDrawDistance() {
17 | return strokeDrawDistance;
18 | }
19 |
20 | public static float getMinDistance() {
21 | return minDistance;
22 | }
23 |
24 | public static float getNearClip() {
25 | return nearClip;
26 | }
27 |
28 | public static float getFarClip() {
29 | return farClip;
30 | }
31 |
32 | public static void setColor(Vector3f color) {
33 | Settings.color = color;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
15 |
16 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/line_frag.glsl:
--------------------------------------------------------------------------------
1 | #extension GL_OES_standard_derivatives : enable
2 | precision mediump float;
3 |
4 | uniform float visibility;
5 | uniform float alphaTest;
6 | uniform float drawMode;
7 | uniform float nearCutOff;
8 | uniform float farCutOff;
9 | varying vec4 vColor;
10 | varying float depth;
11 | varying float vCounters;
12 | float map(float value, float inMin, float inMax, float outMin, float outMax) {
13 | // return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin);
14 | return ((value - inMin) / (inMax - inMin) * (outMax - outMin) + outMin);
15 | }
16 |
17 |
18 | void main() {
19 | vec4 c = vColor;
20 | if( c.a < alphaTest ) discard;
21 |
22 | if(drawMode > 0.0){
23 | if(depth <= nearCutOff){
24 | c.gb *= map(depth, nearCutOff, nearCutOff * 0.95, 0.0, 1.0);
25 | }
26 | if(depth > nearCutOff && depth < farCutOff){
27 | c.gb *= 0.0;
28 | }
29 | if(depth >= farCutOff){
30 | c.gb *= map(depth, farCutOff, farCutOff * 1.05, 0.0, 1.0);
31 | }
32 | }
33 |
34 |
35 | gl_FragColor = c;
36 |
37 | gl_FragColor.a *= step(vCounters,visibility);
38 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
22 |
28 |
29 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Rendering/BiquadFilter.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Rendering;
2 |
3 | import javax.vecmath.Vector3f;
4 |
5 | /** BiquadFilter is a object for easily lowpass filtering incomming values. */
6 | public class BiquadFilter {
7 | private Vector3f val = new Vector3f();
8 |
9 | private BiquadFilterInstance[] inst = new BiquadFilterInstance[3];
10 |
11 | public BiquadFilter(double Fc) {
12 | for (int i = 0; i < 3; i++) {
13 | inst[i] = new BiquadFilterInstance(Fc);
14 | }
15 | }
16 |
17 | public Vector3f update(Vector3f in) {
18 | val.x = (float) inst[0].process(in.x);
19 | val.y = (float) inst[1].process(in.y);
20 | val.z = (float) inst[2].process(in.z);
21 | return val;
22 | }
23 |
24 | private class BiquadFilterInstance {
25 | double a0, a1, a2, b1, b2;
26 | double Fc = 0.5, Q = 0.707, peakGain = 0.0;
27 | double z1 = 0.0, z2 = 0.0;
28 |
29 | BiquadFilterInstance(double fc) {
30 | Fc = fc;
31 | calcBiquad();
32 | }
33 |
34 | double process(double in) {
35 | double out = in * a0 + z1;
36 | z1 = in * a1 + z2 - b1 * out;
37 | z2 = in * a2 - b2 * out;
38 | return out;
39 | }
40 |
41 | void calcBiquad() {
42 | double norm;
43 | double K = Math.tan(Math.PI * Fc);
44 | norm = 1 / (1 + K / Q + K * K);
45 | a0 = K * K * norm;
46 | a1 = 2 * a0;
47 | a2 = a0;
48 | b1 = 2 * (K * K - 1) * norm;
49 | b2 = (1 - K / Q + K * K) * norm;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 | dependencies {
6 | classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4'
7 | }
8 | }
9 |
10 | plugins {
11 | id "com.diffplug.gradle.spotless" version "4.0.0"
12 | }
13 |
14 | apply plugin: 'com.android.application'
15 | apply plugin: 'jacoco-android'
16 |
17 | jacoco {
18 | toolVersion = "0.8.4"
19 | }
20 |
21 | spotless {
22 | java {
23 | googleJavaFormat()
24 | target '**/*.java'
25 | }
26 | }
27 |
28 | android {
29 | compileSdkVersion 29
30 | buildToolsVersion "29.0.3"
31 |
32 | defaultConfig {
33 | applicationId "com.yashk2000.arpaint"
34 | minSdkVersion 23
35 | targetSdkVersion 29
36 | versionCode 1
37 | versionName "1.0"
38 |
39 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
40 | }
41 |
42 | buildTypes {
43 | release {
44 | minifyEnabled false
45 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
46 | }
47 | }
48 |
49 | }
50 |
51 | dependencies {
52 | implementation fileTree(dir: 'libs', include: ['*.jar'])
53 |
54 | implementation 'javax.vecmath:vecmath:1.5.2'
55 | implementation 'com.google.ar:core:1.16.0'
56 | implementation 'androidx.appcompat:appcompat:1.1.0'
57 | implementation 'com.google.android.material:material:1.1.0'
58 | implementation 'androidx.core:core:1.2.0'
59 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
60 | testImplementation 'junit:junit:4.12'
61 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
62 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
63 | implementation 'com.github.duanhong169:colorpicker:1.1.6'
64 | }
65 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/line_vert.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | attribute vec3 position;
4 | attribute vec3 previous;
5 | attribute vec3 next;
6 | attribute float side;
7 | attribute float width;
8 | attribute float counters;
9 |
10 | uniform mat4 projectionMatrix;
11 | uniform mat4 modelViewMatrix;
12 | uniform vec2 resolution;
13 | uniform float lineWidth;
14 | uniform float lineDepthScale;
15 | uniform vec3 color;
16 | uniform float opacity;
17 | uniform float near;
18 | uniform float far;
19 | uniform float sizeAttenuation;
20 |
21 | varying vec4 vColor;
22 | varying float vCounters;
23 | varying float depth;
24 |
25 | vec2 fix( vec4 i, float aspect ) {
26 | vec2 res = i.xy / i.w;
27 | res.x *= aspect;
28 | vCounters = counters;
29 | return res;
30 | }
31 |
32 | float map(float value, float inMin, float inMax, float outMin, float outMax) {
33 | // return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin);
34 | return ((value - inMin) / (inMax - inMin) * (outMax - outMin) + outMin);
35 | }
36 |
37 | void main() {
38 |
39 | float aspect = resolution.x / resolution.y;
40 | float pixelWidthRatio = 1. / (resolution.x * projectionMatrix[0][0]);
41 |
42 | vColor = vec4( color, opacity );
43 |
44 | mat4 m = projectionMatrix * modelViewMatrix;
45 | vec4 finalPosition = m * vec4( position, 1.0 );
46 | vec4 prevPos = m * vec4( previous, 1.0 );
47 | vec4 nextPos = m * vec4( next, 1.0 );
48 |
49 | vec2 currentP = fix( finalPosition, aspect );
50 | vec2 prevP = fix( prevPos, aspect );
51 | vec2 nextP = fix( nextPos, aspect );
52 |
53 | float pixelWidth = finalPosition.w * pixelWidthRatio;
54 | float w = 1.8 * pixelWidth * lineWidth * width;
55 |
56 | if( sizeAttenuation == 1. ) {
57 | w = 1.8 * lineWidth * width * map(clamp((finalPosition.z-near)/far, 0.0, 1.0), 0.0, 1.0, 1.0, lineDepthScale);
58 | }
59 |
60 | vec2 dir;
61 | if( nextP == currentP ){
62 | dir = normalize( currentP - prevP );
63 | }
64 | else if( prevP == currentP ){
65 | dir = normalize( nextP - currentP );
66 | }
67 | else {
68 | vec2 dir1 = normalize( currentP - prevP );
69 | vec2 dir2 = normalize( nextP - currentP );
70 | dir = normalize( dir1 + dir2 );
71 |
72 | vec2 perp = vec2( -dir1.y, dir1.x );
73 | vec2 miter = vec2( -dir.y, dir.x );
74 | // w = clamp( w / dot( miter, perp ), 0., 4. * lineWidth * width );
75 |
76 | }
77 |
78 | //vec2 normal = ( cross( vec3( dir, 0. ), vec3( 0., 0., 1. ) ) ).xy;
79 | vec2 normal = vec2( -dir.y, dir.x );
80 | normal.x /= aspect;
81 | normal *= .5 * w;
82 |
83 | vec4 offset = vec4( normal * side, 0.0, 1.0 );
84 | finalPosition.xy += offset.xy;
85 |
86 |
87 |
88 | gl_Position = finalPosition;
89 |
90 | depth = gl_Position.z;
91 |
92 |
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Rendering/ShaderUtil.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Rendering;
2 |
3 | import android.content.Context;
4 | import android.opengl.GLES20;
5 | import android.util.Log;
6 | import java.io.BufferedReader;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 |
11 | /** Shader helper functions. */
12 | public class ShaderUtil {
13 | /**
14 | * Converts a raw text file, saved as a resource, into an OpenGL ES shader.
15 | *
16 | * @param type The type of shader we will be creating.
17 | * @param resId The resource ID of the raw text file about to be turned into a shader.
18 | * @return The shader object handler.
19 | */
20 | public static int loadGLShader(String tag, Context context, int type, int resId) {
21 | String code = readRawTextFile(context, resId);
22 | int shader = GLES20.glCreateShader(type);
23 | GLES20.glShaderSource(shader, code);
24 | GLES20.glCompileShader(shader);
25 |
26 | // Get the compilation status.
27 | final int[] compileStatus = new int[1];
28 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
29 |
30 | // If the compilation failed, delete the shader.
31 | if (compileStatus[0] == 0) {
32 | Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
33 | GLES20.glDeleteShader(shader);
34 | shader = 0;
35 | }
36 |
37 | if (shader == 0) {
38 | throw new RuntimeException("Error creating shader.");
39 | }
40 |
41 | return shader;
42 | }
43 |
44 | /**
45 | * Checks if we've had an error inside of OpenGL ES, and if so what that error is.
46 | *
47 | * @param label Label to report in case of error.
48 | * @throws RuntimeException If an OpenGL error is detected.
49 | */
50 | public static void checkGLError(String tag, String label) {
51 | int error;
52 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
53 | Log.e(tag, label + ": glError " + error);
54 | throw new RuntimeException(label + ": glError " + error);
55 | }
56 | }
57 |
58 | /**
59 | * Converts a raw text file into a string.
60 | *
61 | * @param resId The resource ID of the raw text file about to be turned into a shader.
62 | * @return The context of the text file, or null in case of error.
63 | */
64 | private static String readRawTextFile(Context context, int resId) {
65 | InputStream inputStream = context.getResources().openRawResource(resId);
66 | try {
67 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
68 | StringBuilder sb = new StringBuilder();
69 | String line;
70 | while ((line = reader.readLine()) != null) {
71 | sb.append(line).append("\n");
72 | }
73 | reader.close();
74 | return sb.toString();
75 | } catch (IOException e) {
76 | e.printStackTrace();
77 | }
78 | return null;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Utils/DisplayRotationHelper.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Utils;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.hardware.display.DisplayManager;
6 | import android.view.Display;
7 | import android.view.WindowManager;
8 | import com.google.ar.core.Session;
9 |
10 | /**
11 | * Helper to track the display rotations. In particular, the 180 degree rotations are not notified
12 | * by the onSurfaceChanged() callback, and thus they require listening to the android display
13 | * events.
14 | */
15 | public class DisplayRotationHelper implements DisplayManager.DisplayListener {
16 | private boolean viewportChanged;
17 | private int viewportWidth;
18 | private int viewportHeight;
19 | private final Context context;
20 | private final Display display;
21 |
22 | /**
23 | * Constructs the DisplayRotationHelper but does not register the listener yet.
24 | *
25 | * @param context the Android {@link Context}.
26 | */
27 | public DisplayRotationHelper(Context context) {
28 | this.context = context;
29 | display = context.getSystemService(WindowManager.class).getDefaultDisplay();
30 | }
31 |
32 | /** Registers the display listener. Should be called from {@link Activity#onResume()}. */
33 | public void onResume() {
34 | context.getSystemService(DisplayManager.class).registerDisplayListener(this, null);
35 | }
36 |
37 | /** Unregisters the display listener. Should be called from {@link Activity#onPause()}. */
38 | public void onPause() {
39 | context.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
40 | }
41 |
42 | /**
43 | * Records a change in surface dimensions. This will be later used by {@link
44 | * #updateSessionIfNeeded(Session)}. Should be called from {@link
45 | * android.opengl.GLSurfaceView.Renderer
46 | * #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}.
47 | *
48 | * @param width the updated width of the surface.
49 | * @param height the updated height of the surface.
50 | */
51 | public void onSurfaceChanged(int width, int height) {
52 | viewportWidth = width;
53 | viewportHeight = height;
54 | viewportChanged = true;
55 | }
56 |
57 | /**
58 | * Updates the session display geometry if a change was posted either by {@link
59 | * #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This
60 | * function should be called explicitly before each call to {@link Session#update()}. This
61 | * function will also clear the 'pending update' (viewportChanged) flag.
62 | *
63 | * @param session the {@link Session} object to update if display geometry changed.
64 | */
65 | public void updateSessionIfNeeded(Session session) {
66 | if (viewportChanged) {
67 | int displayRotation = display.getRotation();
68 | session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight);
69 | viewportChanged = false;
70 | }
71 | }
72 |
73 | /**
74 | * Returns the current rotation state of android display. Same as {@link Display#getRotation()}.
75 | */
76 | public int getRotation() {
77 | return display.getRotation();
78 | }
79 |
80 | @Override
81 | public void onDisplayAdded(int displayId) {}
82 |
83 | @Override
84 | public void onDisplayRemoved(int displayId) {}
85 |
86 | @Override
87 | public void onDisplayChanged(int displayId) {
88 | viewportChanged = true;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Rendering/LineUtils.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Rendering;
2 |
3 | import android.opengl.Matrix;
4 | import com.yashk2000.arpaint.Utils.Settings;
5 | import javax.vecmath.Vector2f;
6 | import javax.vecmath.Vector3f;
7 |
8 | public class LineUtils {
9 |
10 | public static float map(
11 | float value,
12 | float inputMin,
13 | float inputMax,
14 | float outputMin,
15 | float outputMax,
16 | boolean clamp) {
17 | float outVal =
18 | ((value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin);
19 |
20 | if (clamp) {
21 | if (outputMax < outputMin) {
22 | if (outVal < outputMax) outVal = outputMax;
23 | else if (outVal > outputMin) outVal = outputMin;
24 | } else {
25 | if (outVal > outputMax) outVal = outputMax;
26 | else if (outVal < outputMin) outVal = outputMin;
27 | }
28 | }
29 | return outVal;
30 | }
31 |
32 | public static float lerp(float start, float stop, float amt) {
33 | return start + (stop - start) * amt;
34 | }
35 |
36 | public static Vector3f GetWorldCoords(
37 | Vector2f touchPoint,
38 | float screenWidth,
39 | float screenHeight,
40 | float[] projectionMatrix,
41 | float[] viewMatrix) {
42 | Ray touchRay = projectRay(touchPoint, screenWidth, screenHeight, projectionMatrix, viewMatrix);
43 | touchRay.direction.scale(Settings.getStrokeDrawDistance());
44 | touchRay.origin.add(touchRay.direction);
45 | return touchRay.origin;
46 | }
47 |
48 | private static Ray screenPointToRay(Vector2f point, Vector2f viewportSize, float[] viewProjMtx) {
49 | point.y = viewportSize.y - point.y;
50 | float x = point.x * 2.0F / viewportSize.x - 1.0F;
51 | float y = point.y * 2.0F / viewportSize.y - 1.0F;
52 | float[] farScreenPoint = new float[] {x, y, 1.0F, 1.0F};
53 | float[] nearScreenPoint = new float[] {x, y, -1.0F, 1.0F};
54 | float[] nearPlanePoint = new float[4];
55 | float[] farPlanePoint = new float[4];
56 | float[] invertedProjectionMatrix = new float[16];
57 | Matrix.setIdentityM(invertedProjectionMatrix, 0);
58 | Matrix.invertM(invertedProjectionMatrix, 0, viewProjMtx, 0);
59 | Matrix.multiplyMV(nearPlanePoint, 0, invertedProjectionMatrix, 0, nearScreenPoint, 0);
60 | Matrix.multiplyMV(farPlanePoint, 0, invertedProjectionMatrix, 0, farScreenPoint, 0);
61 | Vector3f direction =
62 | new Vector3f(
63 | farPlanePoint[0] / farPlanePoint[3],
64 | farPlanePoint[1] / farPlanePoint[3],
65 | farPlanePoint[2] / farPlanePoint[3]);
66 | Vector3f origin =
67 | new Vector3f(
68 | new Vector3f(
69 | nearPlanePoint[0] / nearPlanePoint[3],
70 | nearPlanePoint[1] / nearPlanePoint[3],
71 | nearPlanePoint[2] / nearPlanePoint[3]));
72 | direction.sub(origin);
73 | direction.normalize();
74 | return new Ray(origin, direction);
75 | }
76 |
77 | private static Ray projectRay(
78 | Vector2f touchPoint,
79 | float screenWidth,
80 | float screenHeight,
81 | float[] projectionMatrix,
82 | float[] viewMatrix) {
83 | float[] viewProjMtx = new float[16];
84 | Matrix.multiplyMM(viewProjMtx, 0, projectionMatrix, 0, viewMatrix, 0);
85 | return screenPointToRay(touchPoint, new Vector2f(screenWidth, screenHeight), viewProjMtx);
86 | }
87 |
88 | public static boolean distanceCheck(Vector3f newPoint, Vector3f lastPoint) {
89 | Vector3f temp = new Vector3f();
90 | temp.sub(newPoint, lastPoint);
91 | return temp.length() > Settings.getMinDistance();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # ARPaint :paintbrush:
4 |
5 | An android app which uses [Google ARCore](https://developers.google.com/ar) to allow user to paint on a camera feed. You can try out the app by downloading the APK from [here](https://github.com/yashk2000/ARPaint/blob/master/app/app-debug.apk) :rocket:.
6 |
7 | ## Features Implemented :fire:
8 |
9 | - Draw anywhere on the canvas. The canvas being the camera feed :camera:
10 | - Change line thickness :straight_ruler:
11 | - Change line color :art:
12 | - Clear the entire screen :pencil2:
13 | - Undo last move :arrow_backward:
14 |
15 | ## Screenshots :bar_chart:
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ***Disclaimer: Do not mind the drawings in the screenshots, I'm not good at drawing.***
27 |
28 | ## Development Setup :triangular_ruler:
29 |
30 | Before you begin, you should have already downloaded the Android Studio SDK and set it up correctly. You can find a guide on how to do this here: [Setting up Android Studio](http://developer.android.com/sdk/installing/index.html?pkg=studio)
31 |
32 | ### Setting up the Android Project :black_nib:
33 |
34 | 1. Download the project source. You can do this either by forking and cloning the repository (recommended if you plan on pushing changes) or by downloading it as a ZIP file and extracting it.
35 |
36 | 2. Install the NDK in Android Studio.
37 |
38 | 3. Open Android Studio, you will see a **Welcome to Android** window. Under Quick Start, select *Import Project (Eclipse ADT, Gradle, etc.)*
39 |
40 | 4. Navigate to the directory where you saved the ARPaint project, select the root folder of the project (the folder named "ARPaint"), and hit OK. Android Studio should now begin building the project with Gradle.
41 |
42 | 5. Once this process is complete and Android Studio opens, check the Console for any build errors.
43 |
44 | - *Note:* If you receive a Gradle sync error titled, "failed to find ...", you should click on the link below the error message (if available) that says *Install missing platform(s) and sync project* and allow Android studio to fetch you what is missing.
45 |
46 | 7. You will also need to setup Android NDK. If a build error due to NDK persists, try replacing ndk with ndk version 20 from [ndk-archives](https://developer.android.com/ndk/downloads/older_releases). Once all build errors have been resolved, you should be all set to build the app and test it.
47 |
48 | 8. To Build the app, go to *Build > Make Project* (or alternatively press the Make Project icon in the toolbar).
49 |
50 | 9. If the app was built successfully, you can test it by running it on either a real device or an emulated one by going to *Run > Run 'app'* or pressing the Run icon in the toolbar.
51 |
52 | ## Learning about ARCore :mortar_board:
53 |
54 | Here are a few resources you can use to learn about ARCore:
55 |
56 | - [https://developers.google.com/ar](https://developers.google.com/ar)
57 | - [Google Codelabs](https://codelabs.developers.google.com/)
58 | - [https://developers.google.com/ar/develop/java/quickstart](https://developers.google.com/ar/develop/java/quickstart)
59 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
20 |
21 |
37 |
38 |
51 |
52 |
68 |
69 |
82 |
83 |
99 |
100 |
113 |
114 |
115 |
131 |
132 |
148 |
149 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Rendering/BackgroundRenderer.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Rendering;
2 |
3 | import android.content.Context;
4 | import android.opengl.GLES11Ext;
5 | import android.opengl.GLES20;
6 | import android.opengl.GLSurfaceView;
7 | import com.google.ar.core.Frame;
8 | import com.google.ar.core.Session;
9 | import com.yashk2000.arpaint.R;
10 | import java.nio.ByteBuffer;
11 | import java.nio.ByteOrder;
12 | import java.nio.FloatBuffer;
13 | import javax.microedition.khronos.egl.EGLConfig;
14 | import javax.microedition.khronos.opengles.GL10;
15 |
16 | /**
17 | * This class renders the AR background from camera feed. It creates and hosts the texture given to
18 | * ARCore to be filled with the camera image.
19 | */
20 | public class BackgroundRenderer {
21 | private static final String TAG = BackgroundRenderer.class.getSimpleName();
22 |
23 | private static final int COORDS_PER_VERTEX = 3;
24 | private static final int TEXCOORDS_PER_VERTEX = 2;
25 | private static final int FLOAT_SIZE = 4;
26 |
27 | private FloatBuffer mQuadVertices;
28 | private FloatBuffer mQuadTexCoord;
29 | private FloatBuffer mQuadTexCoordTransformed;
30 |
31 | private int mQuadProgram;
32 |
33 | private int mQuadPositionParam;
34 | private int mQuadTexCoordParam;
35 | private int mTextureId = -1;
36 | private int mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
37 |
38 | public BackgroundRenderer() {}
39 |
40 | /** @return */
41 | public int getTextureId() {
42 | return mTextureId;
43 | }
44 |
45 | /**
46 | * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on
47 | * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10,
48 | * EGLConfig)}.
49 | *
50 | * @param context Needed to access shader source.
51 | */
52 | public void createOnGlThread(Context context) {
53 | // Generate the background texture.
54 | int textures[] = new int[1];
55 | GLES20.glGenTextures(1, textures, 0);
56 | mTextureId = textures[0];
57 | GLES20.glBindTexture(mTextureTarget, mTextureId);
58 | GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
59 | GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
60 | GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
61 | GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
62 |
63 | int numVertices = 4;
64 | if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) {
65 | throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer.");
66 | }
67 |
68 | ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE);
69 | bbVertices.order(ByteOrder.nativeOrder());
70 | mQuadVertices = bbVertices.asFloatBuffer();
71 | mQuadVertices.put(QUAD_COORDS);
72 | mQuadVertices.position(0);
73 |
74 | ByteBuffer bbTexCoords =
75 | ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
76 | bbTexCoords.order(ByteOrder.nativeOrder());
77 | mQuadTexCoord = bbTexCoords.asFloatBuffer();
78 | mQuadTexCoord.put(QUAD_TEXCOORDS);
79 | mQuadTexCoord.position(0);
80 |
81 | ByteBuffer bbTexCoordsTransformed =
82 | ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
83 | bbTexCoordsTransformed.order(ByteOrder.nativeOrder());
84 | mQuadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer();
85 |
86 | int vertexShader =
87 | ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, R.raw.screenquad_vertex);
88 | int fragmentShader =
89 | ShaderUtil.loadGLShader(
90 | TAG, context, GLES20.GL_FRAGMENT_SHADER, R.raw.screenquad_fragment_oes);
91 |
92 | mQuadProgram = GLES20.glCreateProgram();
93 | GLES20.glAttachShader(mQuadProgram, vertexShader);
94 | GLES20.glAttachShader(mQuadProgram, fragmentShader);
95 | GLES20.glLinkProgram(mQuadProgram);
96 | GLES20.glUseProgram(mQuadProgram);
97 |
98 | ShaderUtil.checkGLError(TAG, "Program creation");
99 |
100 | mQuadPositionParam = GLES20.glGetAttribLocation(mQuadProgram, "a_Position");
101 | mQuadTexCoordParam = GLES20.glGetAttribLocation(mQuadProgram, "a_TexCoord");
102 |
103 | ShaderUtil.checkGLError(TAG, "Program parameters");
104 | }
105 |
106 | /**
107 | * Draws the AR background image. The image will be drawn such that virtual content rendered with
108 | * the matrices provided by {@link Frame#getViewMatrix(float[], int)} and {@link
109 | * Session#getProjectionMatrix(float[], int, float, float)} will accurately follow static physical
110 | * objects. This must be called before drawing virtual content.
111 | *
112 | * @param frame The last {@code Frame} returned by {@link Session#update()}.
113 | */
114 | public void draw(Frame frame) {
115 |
116 | if (frame == null) {
117 | return;
118 | }
119 |
120 | // If display rotation changed (also includes view size change), we need to re-query the uv
121 | // coordinates for the screen rect, as they may have changed as well.
122 | if (frame.hasDisplayGeometryChanged()) {
123 | frame.transformDisplayUvCoords(mQuadTexCoord, mQuadTexCoordTransformed);
124 | }
125 |
126 | // No need to test or write depth, the screen quad has arbitrary depth, and is expected
127 | // to be drawn first.
128 | GLES20.glDisable(GLES20.GL_DEPTH_TEST);
129 | GLES20.glDepthMask(false);
130 |
131 | GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);
132 |
133 | GLES20.glUseProgram(mQuadProgram);
134 |
135 | // Set the vertex positions.
136 | GLES20.glVertexAttribPointer(
137 | mQuadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, mQuadVertices);
138 |
139 | // Set the texture coordinates.
140 | GLES20.glVertexAttribPointer(
141 | mQuadTexCoordParam,
142 | TEXCOORDS_PER_VERTEX,
143 | GLES20.GL_FLOAT,
144 | false,
145 | 0,
146 | mQuadTexCoordTransformed);
147 |
148 | // Enable vertex arrays
149 | GLES20.glEnableVertexAttribArray(mQuadPositionParam);
150 | GLES20.glEnableVertexAttribArray(mQuadTexCoordParam);
151 |
152 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
153 |
154 | // Disable vertex arrays
155 | GLES20.glDisableVertexAttribArray(mQuadPositionParam);
156 | GLES20.glDisableVertexAttribArray(mQuadTexCoordParam);
157 |
158 | // Restore the depth state for further drawing.
159 | GLES20.glDepthMask(true);
160 | GLES20.glEnable(GLES20.GL_DEPTH_TEST);
161 |
162 | ShaderUtil.checkGLError(TAG, "Draw");
163 | }
164 |
165 | public static final float[] QUAD_COORDS =
166 | new float[] {
167 | -1.0f, -1.0f, 0.0f, -1.0f, +1.0f, 0.0f, +1.0f, -1.0f, 0.0f, +1.0f, +1.0f, 0.0f,
168 | };
169 |
170 | public static final float[] QUAD_TEXCOORDS =
171 | new float[] {
172 | 0.0f, 1.0f,
173 | 0.0f, 0.0f,
174 | 1.0f, 1.0f,
175 | 1.0f, 0.0f,
176 | };
177 | }
178 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/Rendering/LineShaderRenderer.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint.Rendering;
2 |
3 | import android.content.Context;
4 | import android.opengl.GLES20;
5 | import android.opengl.Matrix;
6 | import android.util.Log;
7 | import com.yashk2000.arpaint.R;
8 | import java.nio.ByteBuffer;
9 | import java.nio.ByteOrder;
10 | import java.nio.FloatBuffer;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.concurrent.atomic.AtomicBoolean;
14 | import javax.vecmath.Vector3f;
15 |
16 | /** Renders a point cloud. */
17 | public class LineShaderRenderer {
18 |
19 | private static final String TAG = LineShaderRenderer.class.getSimpleName();
20 | private static final int FLOATS_PER_POINT = 3; // X,Y,Z.
21 | private static final int BYTES_PER_FLOAT = 4;
22 | private static final int BYTES_PER_POINT = BYTES_PER_FLOAT * FLOATS_PER_POINT;
23 |
24 | private float[] mModelMatrix = new float[16];
25 | private float[] mModelViewMatrix = new float[16];
26 | private float[] mModelViewProjectionMatrix = new float[16];
27 |
28 | private int mPositionAttribute = 0;
29 | private int mPreviousAttribute = 0;
30 | private int mNextAttribute = 0;
31 | private int mSideAttribute = 0;
32 | private int mWidthAttribte = 0;
33 |
34 | private int mCountersAttribute = 0;
35 |
36 | private int mProjectionUniform = 0;
37 | private int mModelViewUniform = 0;
38 | private int mResolutionUniform = 0;
39 | private int mLineWidthUniform = 0;
40 | private int mColorUniform = 0;
41 | private int mOpacityUniform = 0;
42 | private int mNearUniform = 0;
43 | private int mFarUniform = 0;
44 | private int mSizeAttenuationUniform = 0;
45 | private int mDrawModeUniform = 0;
46 | private int mNearCutoffUniform = 0;
47 | private int mFarCutoffUniform = 0;
48 |
49 | private boolean mDrawMode = false;
50 |
51 | private int mVisibility = 0;
52 | private int mAlphaTest = 0;
53 |
54 | private float[] mPositions;
55 | private float[] mCounters;
56 | private float[] mNext;
57 | private float[] mSide;
58 | private float[] mWidth;
59 | private float[] mPrevious;
60 |
61 | private int mPositionAddress;
62 | private int mPreviousAddress;
63 | private int mNextAddress;
64 | private int mSideAddress;
65 | private int mWidthAddress;
66 | private int mCounterAddress;
67 |
68 | private int mNumPoints = 0;
69 | private int mNumBytes = 0;
70 |
71 | private int mVbo = 0;
72 | private int mVboSize = 0;
73 |
74 | private int mProgramName = 0;
75 | private float lineWidth = 0;
76 |
77 | private Vector3f mColor;
78 |
79 | public AtomicBoolean bNeedsUpdate = new AtomicBoolean();
80 |
81 | private int mLineDepthScaleUniform;
82 | private float mLineDepthScale = 10.0f;
83 |
84 | public float mDrawDistance;
85 |
86 | public LineShaderRenderer() {}
87 |
88 | public void createOnGlThread(Context context) {
89 | ShaderUtil.checkGLError(TAG, "before create");
90 |
91 | int buffers[] = new int[1];
92 | GLES20.glGenBuffers(1, buffers, 0);
93 | mVbo = buffers[0];
94 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo);
95 | mVboSize = 0;
96 | GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVboSize, null, GLES20.GL_DYNAMIC_DRAW);
97 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
98 |
99 | ShaderUtil.checkGLError(TAG, "buffer alloc");
100 |
101 | int vertexShader =
102 | ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, R.raw.line_vert);
103 | int fragmentShader =
104 | ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, R.raw.line_frag);
105 |
106 | mProgramName = GLES20.glCreateProgram();
107 | GLES20.glAttachShader(mProgramName, vertexShader);
108 | GLES20.glAttachShader(mProgramName, fragmentShader);
109 | GLES20.glLinkProgram(mProgramName);
110 | GLES20.glUseProgram(mProgramName);
111 |
112 | ShaderUtil.checkGLError(TAG, "program");
113 |
114 | mPositionAttribute = GLES20.glGetAttribLocation(mProgramName, "position");
115 | mPreviousAttribute = GLES20.glGetAttribLocation(mProgramName, "previous");
116 | mNextAttribute = GLES20.glGetAttribLocation(mProgramName, "next");
117 | mSideAttribute = GLES20.glGetAttribLocation(mProgramName, "side");
118 | mWidthAttribte = GLES20.glGetAttribLocation(mProgramName, "width");
119 | mCountersAttribute = GLES20.glGetAttribLocation(mProgramName, "counters");
120 | mProjectionUniform = GLES20.glGetUniformLocation(mProgramName, "projectionMatrix");
121 | mModelViewUniform = GLES20.glGetUniformLocation(mProgramName, "modelViewMatrix");
122 | mResolutionUniform = GLES20.glGetUniformLocation(mProgramName, "resolution");
123 | mLineWidthUniform = GLES20.glGetUniformLocation(mProgramName, "lineWidth");
124 | mColorUniform = GLES20.glGetUniformLocation(mProgramName, "color");
125 | mOpacityUniform = GLES20.glGetUniformLocation(mProgramName, "opacity");
126 | mNearUniform = GLES20.glGetUniformLocation(mProgramName, "near");
127 | mFarUniform = GLES20.glGetUniformLocation(mProgramName, "far");
128 | mSizeAttenuationUniform = GLES20.glGetUniformLocation(mProgramName, "sizeAttenuation");
129 | mVisibility = GLES20.glGetUniformLocation(mProgramName, "visibility");
130 | mAlphaTest = GLES20.glGetUniformLocation(mProgramName, "alphaTest");
131 | mDrawModeUniform = GLES20.glGetUniformLocation(mProgramName, "drawMode");
132 | mNearCutoffUniform = GLES20.glGetUniformLocation(mProgramName, "nearCutOff");
133 | mFarCutoffUniform = GLES20.glGetUniformLocation(mProgramName, "farCutOff");
134 | mLineDepthScaleUniform = GLES20.glGetUniformLocation(mProgramName, "lineDepthScale");
135 |
136 | ShaderUtil.checkGLError(TAG, "program params");
137 |
138 | Matrix.setIdentityM(mModelMatrix, 0);
139 |
140 | mColor = new Vector3f(1f, 1f, 1f);
141 | lineWidth = 0.5f;
142 | }
143 |
144 | public void setLineWidth(float width) {
145 | lineWidth = width;
146 | }
147 |
148 | public void setDrawDebug(boolean drawDebugMode) {
149 | mDrawMode = drawDebugMode;
150 | }
151 |
152 | public void setColor(Vector3f color) {
153 | mColor = new Vector3f(color);
154 | }
155 |
156 | public void setDistanceScale(float distanceScale) {
157 | this.mLineDepthScale = distanceScale;
158 | }
159 |
160 | public void updateStrokes(ArrayList> strokes) {
161 | mNumPoints = 0;
162 | for (ArrayList l : strokes) {
163 | mNumPoints += l.size() * 2 + 2;
164 | }
165 |
166 | ensureCapacity(mNumPoints);
167 |
168 | int offset = 0;
169 | for (ArrayList l : strokes) {
170 | offset = addLine(l, offset);
171 | }
172 | mNumBytes = offset;
173 | }
174 |
175 | private void ensureCapacity(int numPoints) {
176 | int count = 1024;
177 | if (mSide != null) {
178 | count = mSide.length;
179 | }
180 |
181 | while (count < numPoints) {
182 | count += 1024;
183 | }
184 |
185 | if (mSide == null || mSide.length < count) {
186 | Log.i(TAG, "alloc " + count);
187 | mPositions = new float[count * 3];
188 | mNext = new float[count * 3];
189 | mPrevious = new float[count * 3];
190 |
191 | mCounters = new float[count];
192 | mSide = new float[count];
193 | mWidth = new float[count];
194 | }
195 | }
196 |
197 | private int addLine(List line, int offset) {
198 | if (line == null || line.size() < 2) return offset;
199 |
200 | int lineSize = line.size();
201 |
202 | int ii = offset;
203 | for (int i = 0; i < lineSize; i++) {
204 |
205 | int iGood = i;
206 | if (iGood < 0) iGood = 0;
207 | if (iGood >= lineSize) iGood = lineSize - 1;
208 |
209 | int i_m_1 = (iGood - 1) < 0 ? iGood : iGood - 1;
210 | int i_p_1 = (iGood + 1) > (lineSize - 1) ? iGood : iGood + 1;
211 | float c = ((float) i / lineSize);
212 |
213 | Vector3f current = line.get(iGood);
214 | Vector3f previous = line.get(i_m_1);
215 | Vector3f next = line.get(i_p_1);
216 |
217 | if (i == 0) {
218 | setMemory(ii++, current, previous, next, c, lineWidth, 1f);
219 | }
220 |
221 | setMemory(ii++, current, previous, next, c, lineWidth, 1f);
222 | setMemory(ii++, current, previous, next, c, lineWidth, -1f);
223 |
224 | if (i == lineSize - 1) {
225 | setMemory(ii++, current, previous, next, c, lineWidth, -1f);
226 | }
227 | }
228 | return ii;
229 | }
230 |
231 | private void setMemory(
232 | int index,
233 | Vector3f pos,
234 | Vector3f prev,
235 | Vector3f next,
236 | float counter,
237 | float width,
238 | float side) {
239 | mPositions[index * 3] = pos.x;
240 | mPositions[index * 3 + 1] = pos.y;
241 | mPositions[index * 3 + 2] = pos.z;
242 |
243 | mNext[index * 3] = next.x;
244 | mNext[index * 3 + 1] = next.y;
245 | mNext[index * 3 + 2] = next.z;
246 |
247 | mPrevious[index * 3] = prev.x;
248 | mPrevious[index * 3 + 1] = prev.y;
249 | mPrevious[index * 3 + 2] = prev.z;
250 |
251 | mCounters[index] = counter;
252 | mSide[index] = side;
253 | mWidth[index] = width;
254 | }
255 |
256 | public void clear() {
257 | bNeedsUpdate.set(true);
258 | }
259 |
260 | public void upload() {
261 | bNeedsUpdate.set(false);
262 |
263 | FloatBuffer current = toFloatBuffer(mPositions);
264 | FloatBuffer next = toFloatBuffer(mNext);
265 | FloatBuffer previous = toFloatBuffer(mPrevious);
266 |
267 | FloatBuffer side = toFloatBuffer(mSide);
268 | FloatBuffer width = toFloatBuffer(mWidth);
269 | FloatBuffer counter = toFloatBuffer(mCounters);
270 |
271 | // mNumPoints = mPositions.length;
272 |
273 | mPositionAddress = 0;
274 | mNextAddress = mPositionAddress + mNumBytes * 3 * BYTES_PER_FLOAT;
275 | mPreviousAddress = mNextAddress + mNumBytes * 3 * BYTES_PER_FLOAT;
276 | mSideAddress = mPreviousAddress + mNumBytes * 3 * BYTES_PER_FLOAT;
277 |
278 | mWidthAddress = mSideAddress + mNumBytes * BYTES_PER_FLOAT;
279 | mCounterAddress = mWidthAddress + mNumBytes * BYTES_PER_FLOAT;
280 | mVboSize = mCounterAddress + mNumBytes * BYTES_PER_FLOAT;
281 |
282 | ShaderUtil.checkGLError(TAG, "before update");
283 |
284 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo);
285 |
286 | GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVboSize, null, GLES20.GL_DYNAMIC_DRAW);
287 |
288 | GLES20.glBufferSubData(
289 | GLES20.GL_ARRAY_BUFFER, mPositionAddress, mNumBytes * 3 * BYTES_PER_FLOAT, current);
290 | GLES20.glBufferSubData(
291 | GLES20.GL_ARRAY_BUFFER, mNextAddress, mNumBytes * 3 * BYTES_PER_FLOAT, next);
292 | GLES20.glBufferSubData(
293 | GLES20.GL_ARRAY_BUFFER, mPreviousAddress, mNumBytes * 3 * BYTES_PER_FLOAT, previous);
294 | GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mSideAddress, mNumBytes * BYTES_PER_FLOAT, side);
295 | GLES20.glBufferSubData(
296 | GLES20.GL_ARRAY_BUFFER, mWidthAddress, mNumBytes * BYTES_PER_FLOAT, width);
297 | GLES20.glBufferSubData(
298 | GLES20.GL_ARRAY_BUFFER, mCounterAddress, mNumBytes * BYTES_PER_FLOAT, counter);
299 |
300 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
301 |
302 | ShaderUtil.checkGLError(TAG, "after update");
303 | }
304 |
305 | public void draw(
306 | float[] cameraView,
307 | float[] cameraPerspective,
308 | float screenWidth,
309 | float screenHeight,
310 | float nearClip,
311 | float farClip) {
312 |
313 | Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0);
314 | Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0);
315 |
316 | ShaderUtil.checkGLError(TAG, "Before draw");
317 |
318 | GLES20.glUseProgram(mProgramName);
319 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo);
320 | GLES20.glVertexAttribPointer(
321 | mPositionAttribute,
322 | FLOATS_PER_POINT,
323 | GLES20.GL_FLOAT,
324 | false,
325 | BYTES_PER_POINT,
326 | mPositionAddress);
327 | GLES20.glVertexAttribPointer(
328 | mPreviousAttribute,
329 | FLOATS_PER_POINT,
330 | GLES20.GL_FLOAT,
331 | false,
332 | BYTES_PER_POINT,
333 | mPreviousAddress);
334 | GLES20.glVertexAttribPointer(
335 | mNextAttribute, FLOATS_PER_POINT, GLES20.GL_FLOAT, false, BYTES_PER_POINT, mNextAddress);
336 | GLES20.glVertexAttribPointer(
337 | mSideAttribute, 1, GLES20.GL_FLOAT, false, BYTES_PER_FLOAT, mSideAddress);
338 | GLES20.glVertexAttribPointer(
339 | mWidthAttribte, 1, GLES20.GL_FLOAT, false, BYTES_PER_FLOAT, mWidthAddress);
340 | GLES20.glVertexAttribPointer(
341 | mCountersAttribute, 1, GLES20.GL_FLOAT, false, BYTES_PER_FLOAT, mCounterAddress);
342 | GLES20.glUniformMatrix4fv(mModelViewUniform, 1, false, mModelViewMatrix, 0);
343 | GLES20.glUniformMatrix4fv(mProjectionUniform, 1, false, cameraPerspective, 0);
344 |
345 | GLES20.glUniform2f(mResolutionUniform, screenWidth, screenHeight);
346 | GLES20.glUniform1f(mLineWidthUniform, 0.01f);
347 | GLES20.glUniform3f(mColorUniform, mColor.x, mColor.y, mColor.z);
348 | GLES20.glUniform1f(mOpacityUniform, 1.0f);
349 | GLES20.glUniform1f(mNearUniform, nearClip);
350 | GLES20.glUniform1f(mFarUniform, farClip);
351 | GLES20.glUniform1f(mSizeAttenuationUniform, 1.0f);
352 | GLES20.glUniform1f(mVisibility, 1.0f);
353 | GLES20.glUniform1f(mAlphaTest, 1.0f);
354 | GLES20.glUniform1f(mDrawModeUniform, mDrawMode ? 1.0f : 0.0f);
355 | GLES20.glUniform1f(mNearCutoffUniform, mDrawDistance - 0.0075f);
356 | GLES20.glUniform1f(mFarCutoffUniform, mDrawDistance + 0.0075f);
357 | GLES20.glUniform1f(mLineDepthScaleUniform, mLineDepthScale);
358 |
359 | GLES20.glEnableVertexAttribArray(mPositionAttribute);
360 | GLES20.glEnableVertexAttribArray(mPreviousAttribute);
361 | GLES20.glEnableVertexAttribArray(mNextAttribute);
362 | GLES20.glEnableVertexAttribArray(mSideAttribute);
363 | GLES20.glEnableVertexAttribArray(mWidthAttribte);
364 | GLES20.glEnableVertexAttribArray(mCountersAttribute);
365 |
366 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mNumBytes);
367 |
368 | GLES20.glDisableVertexAttribArray(mCountersAttribute);
369 | GLES20.glDisableVertexAttribArray(mWidthAttribte);
370 | GLES20.glDisableVertexAttribArray(mSideAttribute);
371 | GLES20.glDisableVertexAttribArray(mNextAttribute);
372 | GLES20.glDisableVertexAttribArray(mPreviousAttribute);
373 | GLES20.glDisableVertexAttribArray(mPositionAttribute);
374 |
375 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
376 |
377 | ShaderUtil.checkGLError(TAG, "Draw");
378 | }
379 |
380 | private FloatBuffer toFloatBuffer(float[] data) {
381 | FloatBuffer buff;
382 | ByteBuffer bb = ByteBuffer.allocateDirect(data.length * BYTES_PER_FLOAT);
383 | bb.order(ByteOrder.nativeOrder());
384 | buff = bb.asFloatBuffer();
385 | buff.put(data);
386 | buff.position(0);
387 | return buff;
388 | }
389 | }
390 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yashk2000/arpaint/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.yashk2000.arpaint;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.graphics.Color;
8 | import android.opengl.GLES20;
9 | import android.opengl.GLSurfaceView;
10 | import android.opengl.Matrix;
11 | import android.os.Bundle;
12 | import android.util.DisplayMetrics;
13 | import android.util.Log;
14 | import android.view.GestureDetector;
15 | import android.view.MotionEvent;
16 | import android.view.View;
17 | import android.view.WindowManager;
18 | import android.view.animation.Animation;
19 | import android.view.animation.AnimationUtils;
20 | import android.widget.SeekBar;
21 | import android.widget.TextView;
22 | import android.widget.Toast;
23 | import androidx.annotation.NonNull;
24 | import androidx.appcompat.app.AppCompatActivity;
25 | import androidx.core.view.GestureDetectorCompat;
26 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
27 | import com.google.ar.core.ArCoreApk;
28 | import com.google.ar.core.Camera;
29 | import com.google.ar.core.Config;
30 | import com.google.ar.core.Frame;
31 | import com.google.ar.core.Session;
32 | import com.google.ar.core.TrackingState;
33 | import com.google.ar.core.exceptions.CameraNotAvailableException;
34 | import com.yashk2000.arpaint.Rendering.BackgroundRenderer;
35 | import com.yashk2000.arpaint.Rendering.BiquadFilter;
36 | import com.yashk2000.arpaint.Rendering.LineShaderRenderer;
37 | import com.yashk2000.arpaint.Rendering.LineUtils;
38 | import com.yashk2000.arpaint.Utils.DisplayRotationHelper;
39 | import com.yashk2000.arpaint.Utils.PermissionsHelper;
40 | import com.yashk2000.arpaint.Utils.Settings;
41 | import java.util.ArrayList;
42 | import java.util.Objects;
43 | import java.util.concurrent.atomic.AtomicBoolean;
44 | import java.util.concurrent.atomic.AtomicReference;
45 | import javax.microedition.khronos.egl.EGLConfig;
46 | import javax.microedition.khronos.opengles.GL10;
47 | import javax.vecmath.Vector2f;
48 | import javax.vecmath.Vector3f;
49 | import top.defaults.colorpicker.ColorPickerPopup;
50 |
51 | public class MainActivity extends AppCompatActivity
52 | implements GLSurfaceView.Renderer,
53 | GestureDetector.OnGestureListener,
54 | GestureDetector.OnDoubleTapListener {
55 |
56 | private String TAG = MainActivity.class.getSimpleName();
57 | private GLSurfaceView surfaceView;
58 | private Session session;
59 | private boolean installRequested;
60 | private DisplayRotationHelper displayRotationHelper;
61 | private boolean paused = false;
62 | private float screenWidth = 0;
63 | private float screenHeight = 0;
64 | private BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
65 | private Frame frame;
66 | private AtomicBoolean isTracking = new AtomicBoolean(true);
67 | private float[] zeroMatrix = new float[16];
68 | private Vector3f lastPoint;
69 | private GestureDetectorCompat detector;
70 | private ArrayList> strokes;
71 | private float[] projmtx = new float[16];
72 | private float[] viewmtx = new float[16];
73 | private LineShaderRenderer lineShaderRenderer = new LineShaderRenderer();
74 | private AtomicBoolean touchDown = new AtomicBoolean(false);
75 | private float[] lastFramePosition;
76 | private AtomicBoolean newStroke = new AtomicBoolean(false);
77 | private AtomicReference lastTouch = new AtomicReference<>();
78 | private AtomicBoolean reCenterView = new AtomicBoolean(false);
79 | private AtomicBoolean lineParameters = new AtomicBoolean(false);
80 |
81 | private BiquadFilter biquadFilter;
82 |
83 | private FloatingActionButton fab_main, fab_color, fab_line, fab_clear, fab_undo;
84 | private Animation fab_open, fab_close, fab_clock, fab_anticlock;
85 | TextView color, thickness, clear, undo;
86 |
87 | private AtomicBoolean clearDrawing = new AtomicBoolean(false);
88 | private AtomicBoolean undoMove = new AtomicBoolean(false);
89 |
90 | Boolean isOpen = false;
91 |
92 | private SeekBar lineWidthBar;
93 |
94 | private float lineWidthMax = 0.33f;
95 |
96 | @Override
97 | protected void onCreate(Bundle savedInstanceState) {
98 | super.onCreate(savedInstanceState);
99 | getWindow()
100 | .setFlags(
101 | WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
102 | setContentView(R.layout.activity_main);
103 | Objects.requireNonNull(getSupportActionBar()).hide();
104 |
105 | surfaceView = findViewById(R.id.surfaceview);
106 |
107 | final SharedPreferences sharedPreferences = this.getPreferences(Context.MODE_PRIVATE);
108 |
109 | displayRotationHelper = new DisplayRotationHelper(this);
110 | Matrix.setIdentityM(zeroMatrix, 0);
111 |
112 | lastPoint = new Vector3f(0, 0, 0);
113 | installRequested = false;
114 |
115 | surfaceView.setPreserveEGLContextOnPause(true);
116 | surfaceView.setEGLContextClientVersion(2);
117 | surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
118 | surfaceView.setRenderer(this);
119 | surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
120 |
121 | detector = new GestureDetectorCompat(this, this);
122 | detector.setOnDoubleTapListener(this);
123 | strokes = new ArrayList<>();
124 |
125 | fab_main = findViewById(R.id.fab);
126 | fab_color = findViewById(R.id.color);
127 | fab_line = findViewById(R.id.line);
128 | fab_clear = findViewById(R.id.clear);
129 | fab_undo = findViewById(R.id.undo);
130 | fab_close = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fab_close);
131 | fab_open = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fab_open);
132 | fab_clock = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fab_rotate_clock);
133 | fab_anticlock =
134 | AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fab_rotate_anitclock);
135 |
136 | color = findViewById(R.id.textview_color);
137 | thickness = findViewById(R.id.textview_line_thickness);
138 | clear = findViewById(R.id.textview_clear);
139 | undo = findViewById(R.id.textview_undo);
140 |
141 | fab_main.setOnClickListener(
142 | new View.OnClickListener() {
143 | @Override
144 | public void onClick(View view) {
145 |
146 | if (isOpen) {
147 | color.setVisibility(View.INVISIBLE);
148 | thickness.setVisibility(View.INVISIBLE);
149 | clear.setVisibility(View.INVISIBLE);
150 | undo.setVisibility(View.INVISIBLE);
151 | fab_clear.startAnimation(fab_close);
152 | fab_undo.startAnimation(fab_close);
153 | fab_line.startAnimation(fab_close);
154 | fab_color.startAnimation(fab_close);
155 | fab_main.startAnimation(fab_anticlock);
156 | fab_clear.setClickable(false);
157 | fab_line.setClickable(false);
158 | fab_color.setClickable(false);
159 | fab_undo.setClickable(false);
160 | isOpen = false;
161 | } else {
162 | color.setVisibility(View.VISIBLE);
163 | thickness.setVisibility(View.VISIBLE);
164 | clear.setVisibility(View.VISIBLE);
165 | undo.setVisibility(View.VISIBLE);
166 | fab_clear.startAnimation(fab_open);
167 | fab_undo.startAnimation(fab_open);
168 | fab_line.startAnimation(fab_open);
169 | fab_color.startAnimation(fab_open);
170 | fab_main.startAnimation(fab_clock);
171 | fab_clear.setClickable(true);
172 | fab_line.setClickable(true);
173 | fab_color.setClickable(true);
174 | fab_undo.setClickable(true);
175 | isOpen = true;
176 | }
177 | }
178 | });
179 |
180 | fab_line.setOnClickListener(
181 | new View.OnClickListener() {
182 | @Override
183 | public void onClick(View view) {
184 | Dialog dialog = new Dialog(MainActivity.this);
185 | dialog.setContentView(R.layout.dialog_line);
186 | dialog.show();
187 |
188 | lineWidthBar = dialog.findViewById(R.id.lineWidth);
189 |
190 | lineWidthBar.setProgress(sharedPreferences.getInt("LineWidth", 10));
191 |
192 | lineWidthMax =
193 | LineUtils.map((float) lineWidthBar.getProgress(), 0f, 100f, 0.1f, 5f, true);
194 |
195 | SeekBar.OnSeekBarChangeListener seekBarChangeListener =
196 | new SeekBar.OnSeekBarChangeListener() {
197 | @Override
198 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
199 | SharedPreferences.Editor editor = sharedPreferences.edit();
200 |
201 | if (seekBar == lineWidthBar) {
202 | editor.putInt("LineWidth", progress);
203 | lineWidthMax = LineUtils.map((float) progress, 0f, 100f, 0.1f, 5f, true);
204 | }
205 | lineShaderRenderer.bNeedsUpdate.set(true);
206 |
207 | editor.apply();
208 | }
209 |
210 | @Override
211 | public void onStartTrackingTouch(SeekBar seekBar) {}
212 |
213 | @Override
214 | public void onStopTrackingTouch(SeekBar seekBar) {}
215 | };
216 |
217 | lineWidthBar.setOnSeekBarChangeListener(seekBarChangeListener);
218 | }
219 | });
220 |
221 | fab_color.setOnClickListener(
222 | new View.OnClickListener() {
223 | @Override
224 | public void onClick(View view) {
225 | new ColorPickerPopup.Builder(getBaseContext())
226 | .initialColor(Color.WHITE)
227 | .enableBrightness(true)
228 | .enableAlpha(true)
229 | .okTitle("Choose")
230 | .cancelTitle(null)
231 | .showIndicator(true)
232 | .showValue(false)
233 | .build()
234 | .show(
235 | view,
236 | new ColorPickerPopup.ColorPickerObserver() {
237 | @Override
238 | public void onColorPicked(int color) {
239 | Vector3f curColor =
240 | new Vector3f(
241 | Color.red(color) / 255f,
242 | Color.green(color) / 255f,
243 | Color.blue(color) / 255f);
244 | Settings.setColor(curColor);
245 | }
246 | });
247 | }
248 | });
249 |
250 | fab_clear.setOnClickListener(
251 | new View.OnClickListener() {
252 | @Override
253 | public void onClick(View view) {
254 | clearDrawing.set(true);
255 | }
256 | });
257 |
258 | fab_undo.setOnClickListener(
259 | new View.OnClickListener() {
260 | @Override
261 | public void onClick(View view) {
262 | undoMove.set(true);
263 | }
264 | });
265 | }
266 |
267 | @Override
268 | protected void onResume() {
269 | super.onResume();
270 |
271 | if (session == null) {
272 | Exception exception = null;
273 | try {
274 | switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) {
275 | case INSTALL_REQUESTED:
276 | installRequested = true;
277 | return;
278 | case INSTALLED:
279 | break;
280 | }
281 |
282 | if (!PermissionsHelper.hasCameraPermission(this)) {
283 | PermissionsHelper.requestCameraPermission(this);
284 | return;
285 | }
286 |
287 | session = new Session(this);
288 | } catch (Exception e) {
289 | exception = e;
290 | }
291 |
292 | assert session != null;
293 | Config config = new Config(session);
294 | if (!session.isSupported(config)) {
295 | Log.e(TAG, "Exception creating session Device Does Not Support ARCore", exception);
296 | }
297 | session.configure(config);
298 | }
299 | try {
300 | session.resume();
301 | } catch (CameraNotAvailableException e) {
302 | e.printStackTrace();
303 | }
304 | surfaceView.onResume();
305 | displayRotationHelper.onResume();
306 | paused = false;
307 | }
308 |
309 | @Override
310 | protected void onPause() {
311 | super.onPause();
312 | if (session != null) {
313 | displayRotationHelper.onPause();
314 | surfaceView.onPause();
315 | session.pause();
316 | }
317 |
318 | paused = true;
319 |
320 | DisplayMetrics displayMetrics = new DisplayMetrics();
321 | getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
322 | screenHeight = displayMetrics.heightPixels;
323 | screenWidth = displayMetrics.widthPixels;
324 | }
325 |
326 | private void update() {
327 |
328 | if (session == null) {
329 | return;
330 | }
331 |
332 | displayRotationHelper.updateSessionIfNeeded(session);
333 |
334 | try {
335 |
336 | session.setCameraTextureName(backgroundRenderer.getTextureId());
337 |
338 | frame = session.update();
339 | Camera camera = frame.getCamera();
340 |
341 | TrackingState state = camera.getTrackingState();
342 |
343 | if (state == TrackingState.TRACKING && !isTracking.get()) {
344 | isTracking.set(true);
345 | } else if (state == TrackingState.STOPPED && isTracking.get()) {
346 | isTracking.set(false);
347 | touchDown.set(false);
348 | }
349 |
350 | camera.getProjectionMatrix(projmtx, 0, Settings.getNearClip(), Settings.getFarClip());
351 | camera.getViewMatrix(viewmtx, 0);
352 |
353 | float[] position = new float[3];
354 | camera.getPose().getTranslation(position, 0);
355 |
356 | if (lastFramePosition != null) {
357 | Vector3f distance = new Vector3f(position[0], position[1], position[2]);
358 | distance.sub(
359 | new Vector3f(lastFramePosition[0], lastFramePosition[1], lastFramePosition[2]));
360 |
361 | if (distance.length() > 0.15) {
362 | touchDown.set(false);
363 | }
364 | }
365 | lastFramePosition = position;
366 |
367 | Matrix.multiplyMM(viewmtx, 0, viewmtx, 0, zeroMatrix, 0);
368 |
369 | if (newStroke.get()) {
370 | newStroke.set(false);
371 | addStroke(lastTouch.get());
372 | lineShaderRenderer.bNeedsUpdate.set(true);
373 | } else if (touchDown.get()) {
374 | addPoint(lastTouch.get());
375 | lineShaderRenderer.bNeedsUpdate.set(true);
376 | }
377 |
378 | if (reCenterView.get()) {
379 | reCenterView.set(false);
380 | zeroMatrix = getCalibrationMatrix();
381 | }
382 |
383 | if (undoMove.get()) {
384 | undoMove.set(false);
385 | if (strokes.size() > 0) {
386 | strokes.remove(strokes.size() - 1);
387 | lineShaderRenderer.bNeedsUpdate.set(true);
388 | }
389 | }
390 |
391 | if (clearDrawing.get()) {
392 | clearDrawing.set(false);
393 | clearScreen();
394 | lineShaderRenderer.bNeedsUpdate.set(true);
395 | }
396 |
397 | lineShaderRenderer.setDrawDebug(lineParameters.get());
398 | if (lineShaderRenderer.bNeedsUpdate.get()) {
399 | lineShaderRenderer.setColor(Settings.getColor());
400 | lineShaderRenderer.mDrawDistance = Settings.getStrokeDrawDistance();
401 | float distanceScale = 0.0f;
402 | lineShaderRenderer.setDistanceScale(distanceScale);
403 | lineShaderRenderer.setLineWidth(lineWidthMax);
404 | lineShaderRenderer.clear();
405 | lineShaderRenderer.updateStrokes(strokes);
406 | lineShaderRenderer.upload();
407 | }
408 |
409 | } catch (Exception e) {
410 | e.printStackTrace();
411 | }
412 | }
413 |
414 | public void clearScreen() {
415 | strokes.clear();
416 | lineShaderRenderer.clear();
417 | }
418 |
419 | private void addPoint(Vector2f touchPoint) {
420 | Vector3f newPoint =
421 | LineUtils.GetWorldCoords(touchPoint, screenWidth, screenHeight, projmtx, viewmtx);
422 | addPoint(newPoint);
423 | }
424 |
425 | private void addStroke(Vector3f newPoint) {
426 | float lineSmoothing = 0.1f;
427 | biquadFilter = new BiquadFilter(lineSmoothing);
428 | for (int i = 0; i < 1500; i++) {
429 | biquadFilter.update(newPoint);
430 | }
431 | Vector3f p = biquadFilter.update(newPoint);
432 | lastPoint = new Vector3f(p);
433 | strokes.add(new ArrayList());
434 | strokes.get(strokes.size() - 1).add(lastPoint);
435 | }
436 |
437 | private void addPoint(Vector3f newPoint) {
438 | if (LineUtils.distanceCheck(newPoint, lastPoint)) {
439 | Vector3f p = biquadFilter.update(newPoint);
440 | lastPoint = new Vector3f(p);
441 | strokes.get(strokes.size() - 1).add(lastPoint);
442 | }
443 | }
444 |
445 | public float[] getCalibrationMatrix() {
446 | float[] t = new float[3];
447 | float[] m = new float[16];
448 |
449 | frame.getCamera().getPose().getTranslation(t, 0);
450 | float[] z = frame.getCamera().getPose().getZAxis();
451 | Vector3f zAxis = new Vector3f(z[0], z[1], z[2]);
452 | zAxis.y = 0;
453 | zAxis.normalize();
454 |
455 | double rotate = Math.atan2(zAxis.x, zAxis.z);
456 |
457 | Matrix.setIdentityM(m, 0);
458 | Matrix.translateM(m, 0, t[0], t[1], t[2]);
459 | Matrix.rotateM(m, 0, (float) Math.toDegrees(rotate), 0, 1, 0);
460 | return m;
461 | }
462 |
463 | private void addStroke(Vector2f touchPoint) {
464 | Vector3f newPoint =
465 | LineUtils.GetWorldCoords(touchPoint, screenWidth, screenHeight, projmtx, viewmtx);
466 | addStroke(newPoint);
467 | }
468 |
469 | @Override
470 | public boolean onTouchEvent(MotionEvent tap) {
471 | this.detector.onTouchEvent(tap);
472 |
473 | if (tap.getAction() == MotionEvent.ACTION_DOWN) {
474 | lastTouch.set(new Vector2f(tap.getX(), tap.getY()));
475 | touchDown.set(true);
476 | newStroke.set(true);
477 | return true;
478 | } else if (tap.getAction() == MotionEvent.ACTION_MOVE
479 | || tap.getAction() == MotionEvent.ACTION_POINTER_DOWN) {
480 | lastTouch.set(new Vector2f(tap.getX(), tap.getY()));
481 | touchDown.set(true);
482 | return true;
483 | } else if (tap.getAction() == MotionEvent.ACTION_UP
484 | || tap.getAction() == MotionEvent.ACTION_CANCEL) {
485 | touchDown.set(false);
486 | lastTouch.set(new Vector2f(tap.getX(), tap.getY()));
487 | return true;
488 | }
489 |
490 | return super.onTouchEvent(tap);
491 | }
492 |
493 | @Override
494 | public boolean onSingleTapConfirmed(MotionEvent e) {
495 | return false;
496 | }
497 |
498 | @Override
499 | public boolean onDoubleTap(MotionEvent e) {
500 | return false;
501 | }
502 |
503 | @Override
504 | public boolean onDoubleTapEvent(MotionEvent e) {
505 | return false;
506 | }
507 |
508 | @Override
509 | public boolean onDown(MotionEvent e) {
510 | return false;
511 | }
512 |
513 | @Override
514 | public void onShowPress(MotionEvent e) {}
515 |
516 | @Override
517 | public boolean onSingleTapUp(MotionEvent e) {
518 | return false;
519 | }
520 |
521 | @Override
522 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
523 | return false;
524 | }
525 |
526 | @Override
527 | public void onLongPress(MotionEvent e) {}
528 |
529 | @Override
530 | public void onBackPressed() {
531 | Intent a = new Intent(Intent.ACTION_MAIN);
532 | a.addCategory(Intent.CATEGORY_HOME);
533 | a.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
534 | startActivity(a);
535 | }
536 |
537 | @Override
538 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
539 | return false;
540 | }
541 |
542 | @Override
543 | public void onRequestPermissionsResult(
544 | int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
545 | if (!PermissionsHelper.hasCameraPermission(this)) {
546 | Toast.makeText(this, "Camera permission is needed", Toast.LENGTH_LONG).show();
547 | }
548 | }
549 |
550 | @Override
551 | public void onSurfaceCreated(GL10 gl, EGLConfig config) {
552 | if (session == null) {
553 | return;
554 | }
555 |
556 | GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
557 |
558 | backgroundRenderer.createOnGlThread(this);
559 |
560 | try {
561 |
562 | session.setCameraTextureName(backgroundRenderer.getTextureId());
563 | lineShaderRenderer.createOnGlThread(this);
564 |
565 | } catch (Exception e) {
566 | e.printStackTrace();
567 | }
568 | }
569 |
570 | @Override
571 | public void onSurfaceChanged(GL10 gl, int width, int height) {
572 | GLES20.glViewport(0, 0, width, height);
573 | displayRotationHelper.onSurfaceChanged(width, height);
574 | screenWidth = width;
575 | screenHeight = height;
576 | }
577 |
578 | @Override
579 | public void onDrawFrame(GL10 gl) {
580 | if (paused) return;
581 |
582 | update();
583 |
584 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
585 |
586 | if (frame == null) {
587 | return;
588 | }
589 |
590 | backgroundRenderer.draw(frame);
591 |
592 | if (frame.getCamera().getTrackingState() == TrackingState.TRACKING) {
593 | lineShaderRenderer.draw(
594 | viewmtx,
595 | projmtx,
596 | screenWidth,
597 | screenHeight,
598 | Settings.getNearClip(),
599 | Settings.getFarClip());
600 | }
601 | }
602 | }
603 |
--------------------------------------------------------------------------------