├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ └── styles.xml │ │ │ ├── drawable │ │ │ │ └── sw_logo.png │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── values-v19 │ │ │ │ └── styles.xml │ │ │ └── layout │ │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── pddstudio │ │ │ └── starviewdemo │ │ │ └── MainActivity.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── pddstudio │ │ │ └── starviewdemo │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── pddstudio │ │ └── starviewdemo │ │ └── ApplicationTest.java ├── proguard-rules.pro └── build.gradle ├── library ├── .gitignore ├── gradle.properties ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── pddstudio │ │ │ │ └── starview │ │ │ │ ├── opengl │ │ │ │ ├── misc │ │ │ │ │ ├── Renderable.java │ │ │ │ │ ├── Const.java │ │ │ │ │ └── MathHelper.java │ │ │ │ ├── ShaderType.java │ │ │ │ ├── RawResourceReader.java │ │ │ │ ├── ShaderHelper.java │ │ │ │ ├── ParticleSystem.java │ │ │ │ ├── ParticlesGenerator.java │ │ │ │ ├── ParticleSystemRenderer.java │ │ │ │ └── GLTextureView.java │ │ │ │ └── StarView.java │ │ └── res │ │ │ └── raw │ │ │ ├── star_frag.glsl │ │ │ └── star_vert.glsl │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── pddstudio │ │ │ └── starview │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── pddstudio │ │ └── starview │ │ └── ApplicationTest.java ├── build.gradle ├── proguard-rules.pro └── maven-push.gradle ├── settings.gradle ├── gfx └── star_view_previmg.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | POM_NAME=StarView 3 | POM_ARTIFACT_ID=starview 4 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /gfx/star_view_previmg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/gfx/star_view_previmg.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | StarView.Demo 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/sw_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/app/src/main/res/drawable/sw_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/StarView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/misc/Renderable.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl.misc; 2 | 3 | /** 4 | * Started by Artem Kholodnyi on 11/1/15 12:41 PM 5 | */ 6 | public interface Renderable { 7 | void render(); 8 | void release(); 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jan 12 13:22:22 CET 2017 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-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/raw/star_frag.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform float u_Time; 4 | uniform vec2 u_Resolution; 5 | 6 | varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment. 7 | varying float v_Radius; 8 | 9 | void main() 10 | { 11 | // Render a star 12 | float color = smoothstep(1.0, 0.0, length(v_TexCoordinate - vec2(0.5)) / v_Radius); 13 | gl_FragColor = vec4(color); 14 | } 15 | -------------------------------------------------------------------------------- /library/src/test/java/com/pddstudio/starview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/test/java/com/pddstudio/starviewdemo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starviewdemo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /library/src/androidTest/java/com/pddstudio/starview/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/pddstudio/starviewdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starviewdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/ShaderType.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | 4 | import android.opengl.GLES20; 5 | import android.support.annotation.IntDef; 6 | 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | 10 | /** 11 | * Started by Artem Kholodnyi on 11/1/15 12:25 PM 12 | */ 13 | @Retention(RetentionPolicy.SOURCE) 14 | @IntDef({GLES20.GL_VERTEX_SHADER, GLES20.GL_FRAGMENT_SHADER}) 15 | public @interface ShaderType { 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values-v19/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/misc/Const.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl.misc; 2 | 3 | /** 4 | * Started by Artem Kholodnyi on 11/1/15 12:55 PM -- com.yalantis.com.yalantis.starwars.util.gl 5 | */ 6 | public class Const { 7 | 8 | public static final int POSITION_DATA_SIZE = 3; 9 | public static final int NORMALS_DATA_SIZE = 3; 10 | public static final int TEXTURE_COORDS_DATA_SIZE = 2; 11 | public static final int TILE_XY_DATA_SIZE = 3; 12 | 13 | 14 | public static final int STAR_DATA_SIZE = 4; 15 | public static final int BYTES_PER_FLOAT = 4; 16 | public static final int POINTS_PER_TILE = 6; 17 | } 18 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'maven-push.gradle' 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.2" 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 25 11 | versionCode 2 12 | versionName "1.1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | provided 'com.android.support:appcompat-v7:25.1.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/pddstudio/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/pddstudio/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.pddstudio.starviewdemo" 9 | minSdkVersion 16 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:25.1.0' 26 | compile project(':library') 27 | } 28 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/misc/MathHelper.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl.misc; 2 | 3 | 4 | /** 5 | * Created by Artem Kholodnyi on 11/13/15. 6 | */ 7 | public class MathHelper { 8 | 9 | public static float mix(float x, float y, float a) { 10 | return x * (1 - a) + y * a; 11 | } 12 | 13 | public static float mix(float a, float b , double k) { 14 | return (float) (a * (1 - k) + b * k); 15 | } 16 | 17 | public static float smoothstep(float edge0, float edge1, float x) { 18 | float t = (float) clamp((x - edge0) / (edge1 - edge0), 0f, 1f); 19 | return t * t * (3f - 2f * t); 20 | } 21 | 22 | public static double clamp(float x, float f, double c) { 23 | return Math.max(f, Math.min(x, c)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/starviewdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starviewdemo; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | setFullScreen(); 14 | } 15 | 16 | private void setFullScreen() { 17 | getWindow().getDecorView().setSystemUiVisibility( 18 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 19 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 20 | | View.SYSTEM_UI_FLAG_FULLSCREEN 21 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Base .gitignore taken from "https://raw.githubusercontent.com/github/gitignore/master/Android.gitignore" 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # Intellij 37 | *.iml 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/libraries 41 | .idea/ 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | .idea/vcs.xml 49 | .idea/misc.xml 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 16 | 17 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/RawResourceReader.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.annotation.RawRes; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | 12 | /** 13 | * Created by Artem Kholodnyi on 11/1/15 12:17 PM 14 | * 15 | */ 16 | public class RawResourceReader { 17 | 18 | /** 19 | * Reads a raw resource text file into a String 20 | * @param context 21 | * @param resId 22 | * @return 23 | */ 24 | @Nullable 25 | public static String readTextFileFromRawResource(@NonNull final Context context, 26 | @RawRes final int resId) { 27 | 28 | final BufferedReader bufferedReader = new BufferedReader( 29 | new InputStreamReader(context.getResources().openRawResource(resId)) 30 | ); 31 | 32 | String line; 33 | final StringBuilder body = new StringBuilder(); 34 | 35 | try { 36 | while ((line = bufferedReader.readLine()) != null) { 37 | body.append(line).append('\n'); 38 | } 39 | } catch (IOException e) { 40 | return null; 41 | } 42 | 43 | return body.toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | VERSION_NAME=1.1.0 20 | VERSION_CODE=2 21 | GROUP=com.pddstudio 22 | 23 | POM_DESCRIPTION=StarView Layout for Android 24 | POM_URL=https://github.com/pddstudio/starview 25 | POM_SCM_URL=https://github.com/pddstudio/starview 26 | POM_SCM_CONNECTION=scm:git@github.com:pddstudio/starview.git 27 | POM_SCM_DEV_CONNECTION=scm:git@github.com:pddstudio/starview.git 28 | POM_LICENCE_NAME=The MIT License (MIT) 29 | POM_LICENCE_URL=https://opensource.org/licenses/MIT 30 | POM_LICENCE_DIST=repo 31 | POM_DEVELOPER_ID=pddstudio 32 | POM_DEVELOPER_NAME=Patrick J -------------------------------------------------------------------------------- /library/src/main/res/raw/star_vert.glsl: -------------------------------------------------------------------------------- 1 | uniform mat4 u_MVPMatrix; // A constant representing the combined model/view/projection matrix. 2 | uniform mat4 u_MVMatrix; // A constant representing the combined model/view matrix. 3 | uniform float u_Time; 4 | uniform vec2 u_Resolution; 5 | 6 | attribute vec4 a_Position; //initial 7 | attribute vec2 a_TexCoordinate; 8 | attribute vec4 a_Misc; //initial 9 | 10 | varying vec2 v_TexCoordinate; 11 | varying float v_Radius; 12 | 13 | #define RADIUS 3.5 14 | 15 | float rand( vec2 co ) 16 | { 17 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 18 | } 19 | 20 | void rotate( in float angle, inout vec2 vector ) 21 | { 22 | mat2 rotationMatrix = mat2( cos( angle ), -sin( angle ), 23 | sin( angle ), cos( angle )); 24 | vector *= rotationMatrix; 25 | } 26 | 27 | void main() 28 | { 29 | // Transform the vertex into eye space. 30 | //v_Position = vec3(u_MVMatrix * a_Position); 31 | 32 | float aspect = u_Resolution.x / u_Resolution.y; 33 | 34 | // Pass through the texture coordinate. 35 | v_TexCoordinate = a_TexCoordinate; 36 | 37 | vec2 centerPos = a_Position.xy; 38 | 39 | float f = mix(1.0, a_Misc.t, u_Time); 40 | 41 | centerPos *= mod(f, RADIUS); 42 | 43 | float size = a_Misc.s; 44 | 45 | size = mix(0.0, size, mod(f, RADIUS)/RADIUS); 46 | 47 | vec2 relativePos = vec2( 48 | (a_TexCoordinate.s - 0.5) * 2.0 * size, 49 | (a_TexCoordinate.t - 0.5) * 2.0 * size 50 | ); 51 | 52 | vec2 v = vec2(0.0, 1.0); 53 | 54 | 55 | vec4 pos = vec4( 56 | relativePos + centerPos, 57 | 0.0, 58 | 1.0 59 | ); 60 | 61 | 62 | gl_Position = u_MVPMatrix * pos; 63 | 64 | v_Radius = size * 2.5; 65 | } 66 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/maven-push.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | apply plugin: 'signing' 3 | 4 | def isReleaseBuild() { 5 | return VERSION_NAME.contains("SNAPSHOT") == false 6 | } 7 | 8 | def getReleaseRepositoryUrl() { 9 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 10 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 11 | } 12 | 13 | def getSnapshotRepositoryUrl() { 14 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 15 | : "https://oss.sonatype.org/content/repositories/snapshots/" 16 | } 17 | 18 | def getRepositoryUsername() { 19 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 20 | } 21 | 22 | def getRepositoryPassword() { 23 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 24 | } 25 | 26 | afterEvaluate { project -> 27 | uploadArchives { 28 | repositories { 29 | mavenDeployer { 30 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 31 | 32 | pom.groupId = GROUP 33 | pom.artifactId = POM_ARTIFACT_ID 34 | pom.version = VERSION_NAME 35 | 36 | repository(url: getReleaseRepositoryUrl()) { 37 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 38 | } 39 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 40 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 41 | } 42 | 43 | pom.project { 44 | name POM_NAME 45 | packaging POM_PACKAGING 46 | description POM_DESCRIPTION 47 | url POM_URL 48 | 49 | scm { 50 | url POM_SCM_URL 51 | connection POM_SCM_CONNECTION 52 | developerConnection POM_SCM_DEV_CONNECTION 53 | } 54 | 55 | licenses { 56 | license { 57 | name POM_LICENCE_NAME 58 | url POM_LICENCE_URL 59 | distribution POM_LICENCE_DIST 60 | } 61 | } 62 | 63 | developers { 64 | developer { 65 | id POM_DEVELOPER_ID 66 | name POM_DEVELOPER_NAME 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | signing { 75 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 76 | sign configurations.archives 77 | } 78 | 79 | //task androidJavadocs(type: Javadoc) { 80 | //source = android.sourceSets.main.allJava 81 | //} 82 | 83 | //task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 84 | //classifier = 'javadoc' 85 | //from androidJavadocs.destinationDir 86 | //} 87 | 88 | task androidSourcesJar(type: Jar) { 89 | classifier = 'sources' 90 | from android.sourceSets.main.java.sourceFiles 91 | } 92 | 93 | artifacts { 94 | archives androidSourcesJar 95 | } 96 | } -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/StarView.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.content.pm.ConfigurationInfo; 6 | import android.opengl.GLSurfaceView; 7 | import android.support.annotation.NonNull; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | 11 | import com.pddstudio.starview.opengl.GLTextureView; 12 | import com.pddstudio.starview.opengl.ParticleSystemRenderer; 13 | 14 | /** 15 | * An animated OpenGL based {@linkplain android.view.TextureView} which can be used to display a space-like overlay in your layout. 16 | */ 17 | public class StarView extends GLTextureView { 18 | 19 | private ActivityManager activityManager; 20 | private ConfigurationInfo configurationInfo; 21 | private boolean supportsEs2; 22 | 23 | public StarView(Context context) { 24 | super(context); 25 | 26 | if (!isInEditMode()) { 27 | 28 | activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 29 | configurationInfo = activityManager.getDeviceConfigurationInfo(); 30 | supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000; 31 | 32 | if (supportsEs2) { 33 | // Request an OpenGL ES 2.0 compatible context. 34 | 35 | // Set the renderer to our demo renderer, defined below. 36 | ParticleSystemRenderer mRenderer = new ParticleSystemRenderer(this); 37 | 38 | setEGLContextClientVersion(2); 39 | setEGLConfigChooser(8, 8, 8, 8, 0, 0); 40 | setRenderer(mRenderer); 41 | setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 42 | } else { 43 | if (!isInEditMode()) throw new UnsupportedOperationException(); 44 | } 45 | 46 | } 47 | 48 | } 49 | 50 | public StarView(Context context, AttributeSet attrs) { 51 | super(context, attrs); 52 | 53 | if (!isInEditMode()) { 54 | 55 | activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 56 | configurationInfo = activityManager.getDeviceConfigurationInfo(); 57 | supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000; 58 | 59 | if (supportsEs2) { 60 | // Request an OpenGL ES 2.0 compatible context. 61 | 62 | // Set the renderer to our demo renderer, defined below. 63 | ParticleSystemRenderer mRenderer = new ParticleSystemRenderer(this); 64 | 65 | setEGLContextClientVersion(2); 66 | setEGLConfigChooser(8, 8, 8, 8, 0, 0); 67 | setRenderer(mRenderer); 68 | setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 69 | } else { 70 | if (!isInEditMode()) throw new UnsupportedOperationException(); 71 | } 72 | 73 | } 74 | 75 | } 76 | 77 | @Override 78 | protected void onVisibilityChanged(@NonNull View changedView, int visibility) { 79 | super.onVisibilityChanged(changedView, visibility); 80 | 81 | if (isInEditMode()) { 82 | return; 83 | } 84 | 85 | if (visibility == View.VISIBLE) { 86 | //Resume the starView 87 | onResume(); 88 | } else { 89 | //Pause the starView 90 | onPause(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/ShaderHelper.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | import android.opengl.GLES20; 4 | import android.util.Log; 5 | 6 | public class ShaderHelper { 7 | private static final String TAG = "ShaderHelper"; 8 | 9 | /** 10 | * Helper function to compile a shader. 11 | * 12 | * @param shaderType The shader type. 13 | * @param shaderSource The shader source code. 14 | * @return An OpenGL handle to the shader. 15 | */ 16 | public static int compileShader(@ShaderType final int shaderType, final String shaderSource) { 17 | int shaderHandle = GLES20.glCreateShader(shaderType); 18 | 19 | if (shaderHandle != 0) { 20 | GLES20.glShaderSource(shaderHandle, shaderSource); 21 | 22 | GLES20.glCompileShader(shaderHandle); 23 | 24 | final int[] compilationStatus = new int[1]; 25 | GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compilationStatus, 0); 26 | 27 | if (compilationStatus[0] == 0) { 28 | GLES20.glDeleteShader(shaderHandle); 29 | shaderHandle = 0; 30 | } 31 | } 32 | 33 | if (shaderHandle == 0) { 34 | throw new RuntimeException("Error creating shader."); 35 | } 36 | 37 | return shaderHandle; 38 | } 39 | 40 | /** 41 | * Helper function to compile and link a program. 42 | * 43 | * @param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader. 44 | * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader. 45 | * @param attributes Attributes that need to be bound to the program. 46 | * @return An OpenGL handle to the program. 47 | */ 48 | public static int createAndLinkProgram(final int vertexShaderHandle, 49 | final int fragmentShaderHandle, 50 | final String[] attributes) { 51 | 52 | int programHandle = GLES20.glCreateProgram(); 53 | 54 | if (programHandle != 0) { 55 | // Bind the vertex shader to the program. 56 | GLES20.glAttachShader(programHandle, vertexShaderHandle); 57 | 58 | // Bind the fragment shader to the program. 59 | GLES20.glAttachShader(programHandle, fragmentShaderHandle); 60 | 61 | // Bind attributes 62 | if (attributes != null) { 63 | final int size = attributes.length; 64 | for (int i = 0; i < size; i++) { 65 | GLES20.glBindAttribLocation(programHandle, i, attributes[i]); 66 | } 67 | } 68 | 69 | // Link the two shaders together into a program. 70 | GLES20.glLinkProgram(programHandle); 71 | 72 | // Get the link status. 73 | final int[] linkStatus = new int[1]; 74 | GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); 75 | 76 | // If the link failed, delete the program. 77 | if (linkStatus[0] == 0) { 78 | Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle)); 79 | GLES20.glDeleteProgram(programHandle); 80 | programHandle = 0; 81 | } 82 | } 83 | 84 | if (programHandle == 0) { 85 | throw new RuntimeException("Error creating program."); 86 | } 87 | 88 | return programHandle; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/ParticleSystem.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | 4 | import android.opengl.GLES20; 5 | 6 | import com.pddstudio.starview.opengl.misc.Const; 7 | import com.pddstudio.starview.opengl.misc.Renderable; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.nio.ByteOrder; 11 | import java.nio.FloatBuffer; 12 | import java.nio.ShortBuffer; 13 | 14 | import static android.opengl.GLES20.GL_ARRAY_BUFFER; 15 | import static android.opengl.GLES20.GL_STATIC_DRAW; 16 | import static android.opengl.GLES20.glBindBuffer; 17 | import static android.opengl.GLES20.glBufferData; 18 | import static android.opengl.GLES20.glGenBuffers; 19 | 20 | /** 21 | * Created by Artem Kholodnyi on 11/12/15. 22 | */ 23 | public class ParticleSystem implements Renderable { 24 | private final ParticleSystemRenderer mRenderer; 25 | public static final int PARTICLE_COUNT = 1_000; 26 | private int mBufferId; 27 | 28 | public static final int POS_DATA_SIZE = 3; 29 | public static final int TEXTURE_COORDS_DATA_SIZE = 2; 30 | public static final int MISC_DATA_SIZE = 3; 31 | 32 | 33 | public ParticleSystem(ParticleSystemRenderer renderer, FloatBuffer vertexBuffer) { 34 | long startTime = System.currentTimeMillis(); 35 | 36 | mRenderer = renderer; 37 | 38 | // Copy buffer into OpenGL's memory. After, we don't need to keep the client-side buffers around. 39 | final int buffers[] = new int[1]; 40 | glGenBuffers(1, buffers, 0); 41 | 42 | glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); 43 | glBufferData(GL_ARRAY_BUFFER, vertexBuffer.capacity() * Const.BYTES_PER_FLOAT, vertexBuffer, GL_STATIC_DRAW); 44 | 45 | glBindBuffer(GL_ARRAY_BUFFER, 0); 46 | 47 | mBufferId = buffers[0]; 48 | 49 | vertexBuffer.limit(0); 50 | } 51 | 52 | 53 | // use to make native order buffers 54 | private ShortBuffer makeShortBuffer(short[] arr) { 55 | ByteBuffer bb = ByteBuffer.allocateDirect(arr.length*4); 56 | bb.order(ByteOrder.nativeOrder()); 57 | ShortBuffer ib = bb.asShortBuffer(); 58 | ib.put(arr); 59 | ib.position(0); 60 | return ib; 61 | } 62 | 63 | @Override 64 | public void render() { 65 | final int stride = Const.BYTES_PER_FLOAT 66 | * (POS_DATA_SIZE + TEXTURE_COORDS_DATA_SIZE + MISC_DATA_SIZE); 67 | 68 | // a_Position 69 | glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBufferId); 70 | GLES20.glEnableVertexAttribArray(mRenderer.positionHandle); 71 | GLES20.glVertexAttribPointer(mRenderer.positionHandle, 72 | POS_DATA_SIZE, 73 | GLES20.GL_FLOAT, 74 | false, 75 | stride, 76 | 0 77 | ); 78 | 79 | // a_TexCoordinate 80 | glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBufferId); 81 | GLES20.glEnableVertexAttribArray(mRenderer.textureCoordinateHandle); 82 | GLES20.glVertexAttribPointer(mRenderer.textureCoordinateHandle, 83 | TEXTURE_COORDS_DATA_SIZE, 84 | GLES20.GL_FLOAT, 85 | false, 86 | stride, 87 | Const.BYTES_PER_FLOAT * (POS_DATA_SIZE) 88 | ); 89 | 90 | // a_Misc 91 | glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBufferId); 92 | GLES20.glEnableVertexAttribArray(mRenderer.miscHandle); 93 | GLES20.glVertexAttribPointer(mRenderer.miscHandle, 94 | MISC_DATA_SIZE, 95 | GLES20.GL_FLOAT, 96 | false, 97 | stride, 98 | Const.BYTES_PER_FLOAT * (POS_DATA_SIZE + TEXTURE_COORDS_DATA_SIZE) 99 | ); 100 | 101 | // Clear the currently bound buffer (so future OpenGL calls do not use this buffer). 102 | glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); 103 | 104 | // Draw tiles 105 | GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, PARTICLE_COUNT * 6); 106 | } 107 | 108 | @Override 109 | public void release() { 110 | final int[] buffersToDelete = new int[] { mBufferId }; 111 | GLES20.glDeleteBuffers(buffersToDelete.length, buffersToDelete, 0); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StarView 2 | An animated View inspired by StarWars. 3 | This View was extracted from [StarWars.Android by Yalantis](https://github.com/Yalantis/StarWars.Android) and modified as a standalone View which can be perfectly used for animated backgrounds. 4 | 5 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-StarView-green.svg?style=true)](https://android-arsenal.com/details/1/3100) 6 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.pddstudio/starview/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.pddstudio/starview) 7 | 8 | ## Sample 9 | ### Screenshot 10 | ![](https://raw.githubusercontent.com/PDDStudio/StarView/master/gfx/star_view_previmg.png) 11 | 12 | *Check out the [release page](https://github.com/PDDStudio/StarView/releases) for a live preview or watch the [sample video on YouTube](https://youtu.be/btYqwkWHgW8)* 13 | 14 | ## Getting Started 15 | 16 | ### Include into your Project 17 | Add the following dependency to your project: 18 | 19 | ```java 20 | dependencies { 21 | // other dependencies here... 22 | compile 'com.pddstudio:starview:{version}' 23 | } 24 | 25 | ``` 26 | 27 | Make sure to replace `{version}` with the latest release. 28 | You can find the latest version inside the badge at the top of this readme. 29 | 30 | ### Include the View into your Layout 31 | Simply add `StarView` on top of your layout: 32 | 33 | ```xml 34 | 40 | 41 | 44 | 45 | 48 | 49 | 52 | 53 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | Make sure to wrap the content of your activity / fragment into an `FrameLayout` to use it on top of `StarView` 69 | 70 | ### Prepare the rest of your application 71 | - StarView doesn't have any background, so it's recommended to set your Fragment/Activity background color to a darker one 72 | 73 | ## About & Contact 74 | - In case you've a question feel free to hit me up via E-Mail (patrick.pddstudio@googlemail.com) 75 | - or [Google+](http://plus.google.com/+PatrickJung42) 76 | 77 | ## Contributors 78 | *A big thanks goes to everyone who contributed to this project! 79 | If you want to contribute to this project, feel free to do so and open a pull request with your new feature/enhancement.* 80 | 81 | - [Tristan Vanderaerden](https://github.com/tristanvda) 82 | 83 | ## License 84 | ``` 85 | The MIT License (MIT) 86 | 87 | Copyright © 2015 Yalantis, https://yalantis.com 88 | Copyright © 2016 PDDStudio, https://pddstudio.com 89 | 90 | Permission is hereby granted, free of charge, to any person obtaining a copy 91 | of this software and associated documentation files (the "Software"), to deal 92 | in the Software without restriction, including without limitation the rights 93 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 94 | copies of the Software, and to permit persons to whom the Software is 95 | furnished to do so, subject to the following conditions: 96 | 97 | The above copyright notice and this permission notice shall be included in 98 | all copies or substantial portions of the Software. 99 | 100 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 101 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 102 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 103 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 104 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 105 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 106 | THE SOFTWARE. 107 | ``` -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/ParticlesGenerator.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | 4 | import com.pddstudio.starview.opengl.misc.Const; 5 | import com.pddstudio.starview.opengl.misc.MathHelper; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.nio.ByteOrder; 9 | import java.nio.FloatBuffer; 10 | 11 | /** 12 | * Started by Artem Kholodnyi on 11/29/15 6:13 PM -- com.yalantis.starwarsdemo.particlesys 13 | */ 14 | public class ParticlesGenerator implements Runnable { 15 | private final ParticleSystemRenderer mRenderer; 16 | 17 | public ParticlesGenerator(ParticleSystemRenderer particleSystemRenderer) { 18 | mRenderer = particleSystemRenderer; 19 | } 20 | 21 | @Override 22 | public void run() { 23 | final FloatBuffer vertexBuffer = makeInterleavedBuffer( 24 | generatePositions(), 25 | generateTextureUv(), 26 | generateMisc(), 27 | ParticleSystem.PARTICLE_COUNT 28 | ); 29 | mRenderer.queue(new Runnable() { 30 | @Override 31 | public void run() { 32 | mRenderer.setParticleSystem(new ParticleSystem(mRenderer, vertexBuffer)); 33 | } 34 | }); 35 | } 36 | 37 | private float[] generatePositions() { 38 | final int size = ParticleSystem.PARTICLE_COUNT * ParticleSystem.POS_DATA_SIZE * 6; 39 | 40 | final float height = 5.0f; 41 | final float width = height * mRenderer.ratio; 42 | 43 | final float[] posData = new float[size]; 44 | 45 | final float z = 0f; 46 | 47 | int offset = 0; 48 | for (int p = 0; p < ParticleSystem.PARTICLE_COUNT; p++) { 49 | final float uc = MathHelper.mix(-width, width, Math.random()); 50 | final float vc = MathHelper.mix(-height, height, Math.random()); 51 | 52 | final float u0 = uc;// + delta; 53 | final float v0 = vc;// + delta; 54 | final float u1 = uc;// - delta; 55 | final float v1 = vc;// - delta; 56 | 57 | final int elementsPerPoint = ParticleSystem.POS_DATA_SIZE; 58 | 59 | // 1---2 60 | // | / | 61 | // 3---4 62 | 63 | final float[] p1 = {u1, v0, z}; 64 | final float[] p2 = {u0, v0, z}; 65 | final float[] p3 = {u1, v1, z}; 66 | final float[] p4 = {u0, v1, z}; 67 | 68 | for (int i = 0; i < elementsPerPoint; i++) 69 | posData[offset++] = p1[i]; 70 | for (int i = 0; i < elementsPerPoint; i++) 71 | posData[offset++] = p3[i]; 72 | for (int i = 0; i < elementsPerPoint; i++) 73 | posData[offset++] = p2[i]; 74 | 75 | for (int i = 0; i < elementsPerPoint; i++) 76 | posData[offset++] = p3[i]; 77 | for (int i = 0; i < elementsPerPoint; i++) 78 | posData[offset++] = p4[i]; 79 | for (int i = 0; i < elementsPerPoint; i++) 80 | posData[offset++] = p2[i]; 81 | } 82 | 83 | return posData; 84 | } 85 | 86 | private float[] generateTextureUv() { 87 | int size = ParticleSystem.TEXTURE_COORDS_DATA_SIZE * 6; 88 | 89 | final float[] thisUvData = new float[size]; 90 | 91 | final float u0 = 0f; 92 | final float v0 = 0f; 93 | final float u1 = 1f; 94 | final float v1 = 1f; 95 | 96 | final int elementsPerPoint = ParticleSystem.TEXTURE_COORDS_DATA_SIZE; 97 | 98 | int offset = 0; 99 | 100 | // 1---2 101 | // | / | 102 | // 3---4 103 | 104 | final float[] p1 = {u1, v0}; 105 | final float[] p2 = {u0, v0}; 106 | final float[] p3 = {u1, v1}; 107 | final float[] p4 = {u0, v1}; 108 | 109 | for (int i = 0; i < elementsPerPoint; i++) 110 | thisUvData[offset++] = p1[i]; 111 | for (int i = 0; i < elementsPerPoint; i++) 112 | thisUvData[offset++] = p3[i]; 113 | for (int i = 0; i < elementsPerPoint; i++) 114 | thisUvData[offset++] = p2[i]; 115 | 116 | for (int i = 0; i < elementsPerPoint; i++) 117 | thisUvData[offset++] = p3[i]; 118 | for (int i = 0; i < elementsPerPoint; i++) 119 | thisUvData[offset++] = p4[i]; 120 | for (int i = 0; i < elementsPerPoint; i++) 121 | thisUvData[offset++] = p2[i]; 122 | 123 | return thisUvData; 124 | } 125 | 126 | private float[] generateMisc() { 127 | final int dataLength = ParticleSystem.PARTICLE_COUNT * ParticleSystem.MISC_DATA_SIZE * 6; 128 | 129 | float[] starData = new float[dataLength]; 130 | 131 | int offset = 0; 132 | for (int i = 0; i < ParticleSystem.PARTICLE_COUNT; i++) { 133 | final float particleSize = MathHelper.mix(0.05f, 0.15f, Math.random()); 134 | float accel = MathHelper.mix(1.1f, 3f, Math.random()); 135 | float rand = (float) Math.random(); 136 | 137 | for (int v = 0; v < 6; v++) { 138 | starData[offset++] = particleSize; 139 | starData[offset++] = accel; 140 | starData[offset++] = rand; 141 | } 142 | } 143 | 144 | return starData; 145 | 146 | } 147 | 148 | 149 | private FloatBuffer makeInterleavedBuffer(float[] posData, 150 | float[] uvData, 151 | float[] miscData, 152 | int numStars) { 153 | 154 | int dataLength = posData.length 155 | + uvData.length * numStars 156 | + miscData.length; 157 | 158 | final FloatBuffer interleavedBuffer = ByteBuffer.allocateDirect(dataLength * Const.BYTES_PER_FLOAT) 159 | .order(ByteOrder.nativeOrder()).asFloatBuffer(); 160 | 161 | int positionOffset = 0, uvOffset = 0, starDataOffset = 0; 162 | 163 | for (int i = 0; i < numStars; i++) { 164 | for (int v = 0; v < 6; v++) { 165 | interleavedBuffer.put(posData, positionOffset, ParticleSystem.POS_DATA_SIZE); 166 | positionOffset += ParticleSystem.POS_DATA_SIZE; 167 | 168 | interleavedBuffer.put(uvData, uvOffset, ParticleSystem.TEXTURE_COORDS_DATA_SIZE); 169 | uvOffset = (uvOffset + ParticleSystem.TEXTURE_COORDS_DATA_SIZE ) % uvData.length; 170 | 171 | interleavedBuffer.put(miscData, starDataOffset, ParticleSystem.MISC_DATA_SIZE); 172 | starDataOffset += ParticleSystem.MISC_DATA_SIZE; 173 | } 174 | } 175 | 176 | interleavedBuffer.position(0); 177 | return interleavedBuffer; 178 | } 179 | } -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/ParticleSystemRenderer.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.opengl.GLES20; 7 | import android.opengl.Matrix; 8 | 9 | import com.pddstudio.starview.R; 10 | 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | 14 | import javax.microedition.khronos.egl.EGLConfig; 15 | import javax.microedition.khronos.opengles.GL10; 16 | 17 | /** 18 | * Created by Artem Kholodnyi on 11/12/15. 19 | */ 20 | public class ParticleSystemRenderer implements GLTextureView.Renderer { 21 | public float ratio; 22 | public int mvpMatrixHandle; 23 | public int mvMatrixHandle = -1; 24 | public int positionHandle; 25 | public int normalHandle; 26 | public int textureCoordinateHandle; 27 | public int programHandle; 28 | public int miscHandle; 29 | public int sizeX = 35; 30 | public int sizeY = 70; 31 | public float mTime; 32 | private GLTextureView mGlTextureView; 33 | /** 34 | * Store the model matrix. This matrix is used to move models from object space (where each model can be thought 35 | * of being located at the center of the universe) to world space. 36 | */ 37 | private float[] mModelMatrix = new float[16]; 38 | /** 39 | * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space; 40 | * it positions things relative to our eye. 41 | */ 42 | private float[] mViewMatrix = new float[16]; 43 | /** Store the projection matrix. This is used to project the scene onto a 2D viewport. */ 44 | private float[] mProjectionMatrix = new float[16]; 45 | /** Allocate storage for the final combined matrix. This will be passed into the shader program. */ 46 | private float[] mMVPMatrix = new float[16]; 47 | private float[] mTemporaryMatrix = new float[16]; 48 | private int timeHandle; 49 | private long mStartTime; 50 | private int frames; 51 | private long startTime; 52 | private boolean mStart; 53 | private long timePassed; 54 | private float dt; 55 | private long t_current; 56 | private long t_prev; 57 | private float dt_prev = 1; 58 | private ValueAnimator animator; 59 | private Bitmap mBitmap; 60 | private ParticleSystem mParticleSystem; 61 | private Context appContext; 62 | private int resolutionHandle; 63 | private int mWidth; 64 | private int mHeight; 65 | private int timesRepeated; 66 | private float delta; 67 | private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); 68 | 69 | 70 | public ParticleSystemRenderer(GLTextureView glSurfaceView) { 71 | mGlTextureView = glSurfaceView; 72 | appContext = glSurfaceView.getContext(); 73 | } 74 | 75 | @Override 76 | public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { 77 | GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 78 | 79 | // Use culling to remove back faces. 80 | GLES20.glEnable(GLES20.GL_CULL_FACE); 81 | GLES20.glFrontFace(GLES20.GL_CW); 82 | 83 | // Enable depth testing 84 | GLES20.glEnable(GLES20.GL_DEPTH_TEST); 85 | 86 | // Position the eye in front of the origin. 87 | final float eyeX = 0.0f; 88 | final float eyeY = 0.0f; 89 | final float eyeZ = 0.0f; 90 | 91 | // We are looking toward the distance 92 | final float lookX = 0.0f; 93 | final float lookY = 0.0f; 94 | final float lookZ = 1.0f; 95 | 96 | // Set our up vector. This is where our head would be pointing were we holding the camera. 97 | final float upX = 0.0f; 98 | final float upY = 1.0f; 99 | final float upZ = 0.0f; 100 | 101 | Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); 102 | 103 | final String vertexShader = RawResourceReader.readTextFileFromRawResource(appContext, R.raw.star_vert); 104 | final String fragmentShader = RawResourceReader.readTextFileFromRawResource(appContext, R.raw.star_frag); 105 | 106 | final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader); 107 | final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader); 108 | 109 | programHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 110 | new String[]{"a_Position", "a_TexCoordinate", "a_TileXY"}); 111 | } 112 | 113 | /** 114 | * 115 | */ 116 | @Override 117 | public void onSurfaceChanged(GL10 unused, int width, int height) { 118 | // Set the OpenGL viewport to the same size as the surface. 119 | GLES20.glViewport(0, 0, width, height); 120 | 121 | mWidth = width; 122 | mHeight = height; 123 | 124 | // Create a new perspective projection matrix. The height will stay the same 125 | // while the width will vary as per aspect ratio. 126 | final float ratio = (float) width / height; 127 | 128 | final float left = -ratio; 129 | @SuppressWarnings("UnnecessaryLocalVariable") 130 | final float right = ratio; 131 | final float bottom = -1.0f; 132 | final float top = 1.0f; 133 | final float near = 1.0f; 134 | final float far = 10.0f; 135 | 136 | this.ratio = ratio; 137 | 138 | Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); 139 | 140 | mStartTime = System.currentTimeMillis(); 141 | 142 | mExecutor.execute(new ParticlesGenerator(this)); 143 | } 144 | 145 | @Override 146 | public void onDrawFrame(GL10 gl10) { 147 | logFrame(); 148 | drawGl(); 149 | 150 | if (mParticleSystem != null) { 151 | mParticleSystem.render(); 152 | } 153 | } 154 | 155 | @Override 156 | public void onSurfaceDestroyed(GL10 gl) { 157 | //TODO: maybe some cleaning should happen here? 158 | } 159 | 160 | private void drawGl() { 161 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 162 | 163 | GLES20.glUseProgram(programHandle); 164 | 165 | // Set program handles 166 | mvpMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix"); 167 | mvMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVMatrix"); 168 | timeHandle = GLES20.glGetUniformLocation(programHandle, "u_Time"); 169 | resolutionHandle = GLES20.glGetUniformLocation(programHandle, "u_Resolution"); 170 | 171 | positionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position"); 172 | normalHandle = GLES20.glGetAttribLocation(programHandle, "a_Normal"); 173 | textureCoordinateHandle = GLES20.glGetAttribLocation(programHandle, "a_TexCoordinate"); 174 | miscHandle = GLES20.glGetAttribLocation(programHandle, "a_Misc"); 175 | 176 | Matrix.setIdentityM(mModelMatrix, 0); 177 | Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, 5f); 178 | 179 | Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); 180 | 181 | // Pass in the modelview matrix. 182 | GLES20.glUniformMatrix4fv(mvMatrixHandle, 1, false, mMVPMatrix, 0); 183 | 184 | Matrix.multiplyMM(mTemporaryMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); 185 | System.arraycopy(mTemporaryMatrix, 0, mMVPMatrix, 0, 16); 186 | 187 | // Pass in the combined matrix. 188 | GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mMVPMatrix, 0); 189 | 190 | // Pass in u_Time 191 | GLES20.glUniform1f(timeHandle, (System.currentTimeMillis() - mStartTime) / 3500f); 192 | 193 | // u_Resolution 194 | GLES20.glUniform2f(resolutionHandle, mWidth, mHeight); 195 | 196 | GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); 197 | GLES20.glEnable(GLES20.GL_BLEND); 198 | } 199 | 200 | public void logFrame() { 201 | frames++; 202 | timePassed = (System.nanoTime() - startTime) / 1_000_000; 203 | if(timePassed >= 10_000) { 204 | frames = 0; 205 | startTime = System.nanoTime(); 206 | } 207 | } 208 | 209 | public void onTouchEvent() { 210 | if (mStart) { 211 | reset(); 212 | } 213 | mStart = !mStart; 214 | mStartTime = System.nanoTime(); 215 | } 216 | 217 | private void reset() { 218 | if (animator != null) { 219 | animator.cancel(); 220 | } 221 | mStartTime = 0; 222 | dt = 0; 223 | t_prev = 0; 224 | } 225 | 226 | 227 | public ParticleSystem getParticleSystem() { 228 | return mParticleSystem; 229 | } 230 | 231 | public void setParticleSystem(final ParticleSystem particleSystem) { 232 | mParticleSystem = particleSystem; 233 | } 234 | 235 | public void queue(Runnable runnable) { 236 | mGlTextureView.queueEvent(runnable); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /library/src/main/java/com/pddstudio/starview/opengl/GLTextureView.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.starview.opengl; 2 | 3 | import android.content.Context; 4 | import android.graphics.SurfaceTexture; 5 | import android.opengl.GLDebugHelper; 6 | import android.util.AttributeSet; 7 | import android.util.Log; 8 | import android.view.TextureView; 9 | import android.view.View; 10 | 11 | import java.io.Writer; 12 | import java.lang.ref.WeakReference; 13 | import java.util.ArrayList; 14 | 15 | import javax.microedition.khronos.egl.EGL10; 16 | import javax.microedition.khronos.egl.EGL11; 17 | import javax.microedition.khronos.egl.EGLConfig; 18 | import javax.microedition.khronos.egl.EGLContext; 19 | import javax.microedition.khronos.egl.EGLDisplay; 20 | import javax.microedition.khronos.egl.EGLSurface; 21 | import javax.microedition.khronos.opengles.GL; 22 | import javax.microedition.khronos.opengles.GL10; 23 | 24 | public class GLTextureView 25 | extends TextureView 26 | implements TextureView.SurfaceTextureListener, 27 | View.OnLayoutChangeListener { 28 | 29 | private final static String TAG = "GLTextureView"; 30 | private final static boolean LOG_ATTACH_DETACH = true; 31 | private final static boolean LOG_THREADS = true; 32 | private final static boolean LOG_PAUSE_RESUME = true; 33 | private final static boolean LOG_SURFACE = true; 34 | private final static boolean LOG_RENDERER = true; 35 | private final static boolean LOG_RENDERER_DRAW_FRAME = false; 36 | private final static boolean LOG_EGL = true; 37 | /** 38 | * The renderer only renders 39 | * when the surface is created, or when {@link #requestRender} is called. 40 | * 41 | * @see #getRenderMode() 42 | * @see #setRenderMode(int) 43 | * @see #requestRender() 44 | */ 45 | public final static int RENDERMODE_WHEN_DIRTY = 0; 46 | /** 47 | * The renderer is called 48 | * continuously to re-render the scene. 49 | * 50 | * @see #getRenderMode() 51 | * @see #setRenderMode(int) 52 | */ 53 | public final static int RENDERMODE_CONTINUOUSLY = 1; 54 | 55 | /** 56 | * Check glError() after every GL call and throw an exception if glError indicates 57 | * that an error has occurred. This can be used to help track down which OpenGL ES call 58 | * is causing an error. 59 | * 60 | * @see #getDebugFlags 61 | * @see #setDebugFlags 62 | */ 63 | public final static int DEBUG_CHECK_GL_ERROR = 1; 64 | 65 | /** 66 | * Log GL calls to the system log at "verbose" level with tag "GLTextureView". 67 | * 68 | * @see #getDebugFlags 69 | * @see #setDebugFlags 70 | */ 71 | public final static int DEBUG_LOG_GL_CALLS = 2; 72 | 73 | /** 74 | * Standard View constructor. In order to render something, you 75 | * must call {@link #setRenderer} to register a renderer. 76 | */ 77 | public GLTextureView(Context context) { 78 | super(context); 79 | init(); 80 | } 81 | 82 | /** 83 | * Standard View constructor. In order to render something, you 84 | * must call {@link #setRenderer} to register a renderer. 85 | */ 86 | public GLTextureView(Context context, AttributeSet attrs) { 87 | super(context, attrs); 88 | init(); 89 | } 90 | 91 | @Override 92 | protected void finalize() throws Throwable { 93 | try { 94 | if (mGLThread != null) { 95 | // GLThread may still be running if this view was never 96 | // attached to a window. 97 | mGLThread.requestExitAndWait(); 98 | } 99 | } finally { 100 | super.finalize(); 101 | } 102 | } 103 | 104 | private void init() { 105 | setSurfaceTextureListener(this); 106 | } 107 | 108 | /** 109 | * Set the glWrapper. If the glWrapper is not null, its 110 | * {@link GLWrapper#wrap(javax.microedition.khronos.opengles.GL)} method is called 111 | * whenever a surface is created. A GLWrapper can be used to wrap 112 | * the GL object that's passed to the renderer. Wrapping a GL 113 | * object enables examining and modifying the behavior of the 114 | * GL calls made by the renderer. 115 | *

116 | * Wrapping is typically used for debugging purposes. 117 | *

118 | * The default value is null. 119 | * 120 | * @param glWrapper the new GLWrapper 121 | */ 122 | public void setGLWrapper(GLWrapper glWrapper) { 123 | mGLWrapper = glWrapper; 124 | } 125 | 126 | /** 127 | * Set the debug flags to a new value. The value is 128 | * constructed by OR-together zero or more 129 | * of the DEBUG_CHECK_* constants. The debug flags take effect 130 | * whenever a surface is created. The default value is zero. 131 | * 132 | * @param debugFlags the new debug flags 133 | * @see #DEBUG_CHECK_GL_ERROR 134 | * @see #DEBUG_LOG_GL_CALLS 135 | */ 136 | public void setDebugFlags(int debugFlags) { 137 | mDebugFlags = debugFlags; 138 | } 139 | 140 | /** 141 | * Get the current value of the debug flags. 142 | * 143 | * @return the current value of the debug flags. 144 | */ 145 | public int getDebugFlags() { 146 | return mDebugFlags; 147 | } 148 | 149 | /** 150 | * Control whether the EGL context is preserved when the GLTextureView is paused and 151 | * resumed. 152 | *

153 | * If set to true, then the EGL context may be preserved when the GLTextureView is paused. 154 | * Whether the EGL context is actually preserved or not depends upon whether the 155 | * Android device that the program is running on can support an arbitrary number of EGL 156 | * contexts or not. Devices that can only support a limited number of EGL contexts must 157 | * release the EGL context in order to allow multiple applications to share the GPU. 158 | *

159 | * If set to false, the EGL context will be released when the GLTextureView is paused, 160 | * and recreated when the GLTextureView is resumed. 161 | *

162 | *

163 | * The default is false. 164 | * 165 | * @param preserveOnPause preserve the EGL context when paused 166 | */ 167 | public void setPreserveEGLContextOnPause(boolean preserveOnPause) { 168 | mPreserveEGLContextOnPause = preserveOnPause; 169 | } 170 | 171 | /** 172 | * @return true if the EGL context will be preserved when paused 173 | */ 174 | public boolean getPreserveEGLContextOnPause() { 175 | return mPreserveEGLContextOnPause; 176 | } 177 | 178 | /** 179 | * Set the renderer associated with this view. Also starts the thread that 180 | * will call the renderer, which in turn causes the rendering to start. 181 | *

This method should be called once and only once in the life-cycle of 182 | * a GLTextureView. 183 | *

The following GLTextureView methods can only be called before 184 | * setRenderer is called: 185 | *

    186 | *
  • {@link #setEGLConfigChooser(boolean)} 187 | *
  • {@link #setEGLConfigChooser(EGLConfigChooser)} 188 | *
  • {@link #setEGLConfigChooser(int, int, int, int, int, int)} 189 | *
190 | *

191 | * The following GLTextureView methods can only be called after 192 | * setRenderer is called: 193 | *

    194 | *
  • {@link #getRenderMode()} 195 | *
  • {@link #onPause()} 196 | *
  • {@link #onResume()} 197 | *
  • {@link #queueEvent(Runnable)} 198 | *
  • {@link #requestRender()} 199 | *
  • {@link #setRenderMode(int)} 200 | *
201 | * 202 | * @param renderer the renderer to use to perform OpenGL drawing. 203 | */ 204 | public void setRenderer(Renderer renderer) { 205 | checkRenderThreadState(); 206 | if (mEGLConfigChooser == null) { 207 | mEGLConfigChooser = new SimpleEGLConfigChooser(true); 208 | } 209 | if (mEGLContextFactory == null) { 210 | mEGLContextFactory = new DefaultContextFactory(); 211 | } 212 | if (mEGLWindowSurfaceFactory == null) { 213 | mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); 214 | } 215 | mRenderer = renderer; 216 | mGLThread = new GLThread(mThisWeakRef); 217 | mGLThread.start(); 218 | } 219 | 220 | /** 221 | * Install a custom EGLContextFactory. 222 | *

If this method is 223 | * called, it must be called before {@link #setRenderer(Renderer)} 224 | * is called. 225 | *

226 | * If this method is not called, then by default 227 | * a context will be created with no shared context and 228 | * with a null attribute list. 229 | */ 230 | public void setEGLContextFactory(EGLContextFactory factory) { 231 | checkRenderThreadState(); 232 | mEGLContextFactory = factory; 233 | } 234 | 235 | /** 236 | * Install a custom EGLWindowSurfaceFactory. 237 | *

If this method is 238 | * called, it must be called before {@link #setRenderer(Renderer)} 239 | * is called. 240 | *

241 | * If this method is not called, then by default 242 | * a window surface will be created with a null attribute list. 243 | */ 244 | public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { 245 | checkRenderThreadState(); 246 | mEGLWindowSurfaceFactory = factory; 247 | } 248 | 249 | /** 250 | * Install a custom EGLConfigChooser. 251 | *

If this method is 252 | * called, it must be called before {@link #setRenderer(Renderer)} 253 | * is called. 254 | *

255 | * If no setEGLConfigChooser method is called, then by default the 256 | * view will choose an EGLConfig that is compatible with the current 257 | * android.view.Surface, with a depth buffer depth of 258 | * at least 16 bits. 259 | * 260 | * @param configChooser 261 | */ 262 | public void setEGLConfigChooser(EGLConfigChooser configChooser) { 263 | checkRenderThreadState(); 264 | mEGLConfigChooser = configChooser; 265 | } 266 | 267 | /** 268 | * Install a config chooser which will choose a config 269 | * as close to 16-bit RGB as possible, with or without an optional depth 270 | * buffer as close to 16-bits as possible. 271 | *

If this method is 272 | * called, it must be called before {@link #setRenderer(Renderer)} 273 | * is called. 274 | *

275 | * If no setEGLConfigChooser method is called, then by default the 276 | * view will choose an RGB_888 surface with a depth buffer depth of 277 | * at least 16 bits. 278 | * 279 | * @param needDepth 280 | */ 281 | public void setEGLConfigChooser(boolean needDepth) { 282 | setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); 283 | } 284 | 285 | /** 286 | * Install a config chooser which will choose a config 287 | * with at least the specified depthSize and stencilSize, 288 | * and exactly the specified redSize, greenSize, blueSize and alphaSize. 289 | *

If this method is 290 | * called, it must be called before {@link #setRenderer(Renderer)} 291 | * is called. 292 | *

293 | * If no setEGLConfigChooser method is called, then by default the 294 | * view will choose an RGB_888 surface with a depth buffer depth of 295 | * at least 16 bits. 296 | */ 297 | public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, 298 | int alphaSize, int depthSize, int stencilSize) { 299 | setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, 300 | blueSize, alphaSize, depthSize, stencilSize)); 301 | } 302 | 303 | /** 304 | * Inform the default EGLContextFactory and default EGLConfigChooser 305 | * which EGLContext client version to pick. 306 | *

Use this method to create an OpenGL ES 2.0-compatible context. 307 | * Example: 308 | *

 309 |      * public MyView(Context context) {
 310 |      * super(context);
 311 |      * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
 312 |      * setRenderer(new MyRenderer());
 313 |      * }
 314 |      * 
315 | *

Note: Activities which require OpenGL ES 2.0 should indicate this by 316 | * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's 317 | * AndroidManifest.xml file. 318 | *

If this method is called, it must be called before {@link #setRenderer(Renderer)} 319 | * is called. 320 | *

This method only affects the behavior of the default EGLContexFactory and the 321 | * default EGLConfigChooser. If 322 | * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied 323 | * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context. 324 | * If 325 | * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied 326 | * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config. 327 | * 328 | * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0 329 | */ 330 | public void setEGLContextClientVersion(int version) { 331 | checkRenderThreadState(); 332 | mEGLContextClientVersion = version; 333 | } 334 | 335 | /** 336 | * Set the rendering mode. When renderMode is 337 | * RENDERMODE_CONTINUOUSLY, the renderer is called 338 | * repeatedly to re-render the scene. When renderMode 339 | * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface 340 | * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. 341 | *

342 | * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance 343 | * by allowing the GPU and CPU to idle when the view does not need to be updated. 344 | *

345 | * This method can only be called after {@link #setRenderer(Renderer)} 346 | * 347 | * @param renderMode one of the RENDERMODE_X constants 348 | * @see #RENDERMODE_CONTINUOUSLY 349 | * @see #RENDERMODE_WHEN_DIRTY 350 | */ 351 | public void setRenderMode(int renderMode) { 352 | mGLThread.setRenderMode(renderMode); 353 | } 354 | 355 | /** 356 | * Get the current rendering mode. May be called 357 | * from any thread. Must not be called before a renderer has been set. 358 | * 359 | * @return the current rendering mode. 360 | * @see #RENDERMODE_CONTINUOUSLY 361 | * @see #RENDERMODE_WHEN_DIRTY 362 | */ 363 | public int getRenderMode() { 364 | return mGLThread.getRenderMode(); 365 | } 366 | 367 | /** 368 | * Request that the renderer render a frame. 369 | * This method is typically used when the render mode has been set to 370 | * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. 371 | * May be called 372 | * from any thread. Must not be called before a renderer has been set. 373 | */ 374 | public void requestRender() { 375 | mGLThread.requestRender(); 376 | } 377 | 378 | /** 379 | * This method is part of the SurfaceHolder.Callback interface, and is 380 | * not normally called or subclassed by clients of GLTextureView. 381 | */ 382 | public void surfaceCreated(SurfaceTexture texture) { 383 | mGLThread.surfaceCreated(); 384 | } 385 | 386 | /** 387 | * This method is part of the SurfaceHolder.Callback interface, and is 388 | * not normally called or subclassed by clients of GLTextureView. 389 | */ 390 | public void surfaceDestroyed(SurfaceTexture texture) { 391 | // Surface will be destroyed when we return 392 | mGLThread.surfaceDestroyed(); 393 | } 394 | 395 | /** 396 | * This method is part of the SurfaceHolder.Callback interface, and is 397 | * not normally called or subclassed by clients of GLTextureView. 398 | */ 399 | public void surfaceChanged(SurfaceTexture texture, int format, int w, int h) { 400 | mGLThread.onWindowResize(w, h); 401 | } 402 | 403 | /** 404 | * Inform the view that the activity is paused. The owner of this view must 405 | * call this method when the activity is paused. Calling this method will 406 | * pause the rendering thread. 407 | * Must not be called before a renderer has been set. 408 | */ 409 | public void onPause() { 410 | mGLThread.onPause(); 411 | } 412 | 413 | /** 414 | * Inform the view that the activity is resumed. The owner of this view must 415 | * call this method when the activity is resumed. Calling this method will 416 | * recreate the OpenGL display and resume the rendering 417 | * thread. 418 | * Must not be called before a renderer has been set. 419 | */ 420 | public void onResume() { 421 | mGLThread.onResume(); 422 | } 423 | 424 | /** 425 | * Queue a runnable to be run on the GL rendering thread. This can be used 426 | * to communicate with the Renderer on the rendering thread. 427 | * Must not be called before a renderer has been set. 428 | * 429 | * @param r the runnable to be run on the GL rendering thread. 430 | */ 431 | public void queueEvent(Runnable r) { 432 | mGLThread.queueEvent(r); 433 | } 434 | 435 | /** 436 | * This method is used as part of the View class and is not normally 437 | * called or subclassed by clients of GLTextureView. 438 | */ 439 | @Override 440 | protected void onAttachedToWindow() { 441 | super.onAttachedToWindow(); 442 | if (LOG_ATTACH_DETACH) { 443 | Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); 444 | } 445 | if (mDetached && (mRenderer != null)) { 446 | int renderMode = RENDERMODE_CONTINUOUSLY; 447 | if (mGLThread != null) { 448 | renderMode = mGLThread.getRenderMode(); 449 | } 450 | mGLThread = new GLThread(mThisWeakRef); 451 | if (renderMode != RENDERMODE_CONTINUOUSLY) { 452 | mGLThread.setRenderMode(renderMode); 453 | } 454 | mGLThread.start(); 455 | } 456 | mDetached = false; 457 | } 458 | 459 | /** 460 | * This method is used as part of the View class and is not normally 461 | * called or subclassed by clients of GLTextureView. 462 | * Must not be called before a renderer has been set. 463 | */ 464 | @Override 465 | protected void onDetachedFromWindow() { 466 | if (LOG_ATTACH_DETACH) { 467 | Log.d(TAG, "onDetachedFromWindow"); 468 | } 469 | if (mGLThread != null) { 470 | mGLThread.requestExitAndWait(); 471 | } 472 | mDetached = true; 473 | super.onDetachedFromWindow(); 474 | } 475 | 476 | public void onLayoutChange(View v, int left, int top, int right, int bottom, 477 | int oldLeft, int oldTop, int oldRight, int oldBottom) { 478 | surfaceChanged(getSurfaceTexture(), 0, right - left, bottom - top); 479 | } 480 | 481 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 482 | surfaceCreated(surface); 483 | surfaceChanged(surface, 0, width, height); 484 | } 485 | 486 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 487 | surfaceChanged(surface, 0, width, height); 488 | } 489 | 490 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 491 | surfaceDestroyed(surface); 492 | return true; 493 | } 494 | 495 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 496 | requestRender(); 497 | } 498 | 499 | // ---------------------------------------------------------------------- 500 | 501 | /** 502 | * An interface used to wrap a GL interface. 503 | *

Typically 504 | * used for implementing debugging and tracing on top of the default 505 | * GL interface. You would typically use this by creating your own class 506 | * that implemented all the GL methods by delegating to another GL instance. 507 | * Then you could add your own behavior before or after calling the 508 | * delegate. All the GLWrapper would do was instantiate and return the 509 | * wrapper GL instance: 510 | *

 511 |      * class MyGLWrapper implements GLWrapper {
 512 |      * GL wrap(GL gl) {
 513 |      * return new MyGLImplementation(gl);
 514 |      * }
 515 |      * static class MyGLImplementation implements GL,GL10,GL11,... {
 516 |      * ...
 517 |      * }
 518 |      * }
 519 |      * 
520 | * 521 | * @see #setGLWrapper(GLWrapper) 522 | */ 523 | public interface GLWrapper { 524 | /** 525 | * Wraps a gl interface in another gl interface. 526 | * 527 | * @param gl a GL interface that is to be wrapped. 528 | * @return either the input argument or another GL object that wraps the input argument. 529 | */ 530 | GL wrap(GL gl); 531 | } 532 | 533 | /** 534 | * A generic renderer interface. 535 | *

536 | * The renderer is responsible for making OpenGL calls to render a frame. 537 | *

538 | * GLTextureView clients typically create their own classes that implement 539 | * this interface, and then call {@link GLTextureView#setRenderer} to 540 | * register the renderer with the GLTextureView. 541 | *

542 | *

543 | *

544 | *

Developer Guides

545 | *

For more information about how to use OpenGL, read the 546 | * OpenGL developer guide.

547 | *
548 | *

549 | *

Threading

550 | * The renderer will be called on a separate thread, so that rendering 551 | * performance is decoupled from the UI thread. Clients typically need to 552 | * communicate with the renderer from the UI thread, because that's where 553 | * input events are received. Clients can communicate using any of the 554 | * standard Java techniques for cross-thread communication, or they can 555 | * use the {@link GLTextureView#queueEvent(Runnable)} convenience method. 556 | *

557 | *

EGL Context Lost

558 | * There are situations where the EGL rendering context will be lost. This 559 | * typically happens when device wakes up after going to sleep. When 560 | * the EGL context is lost, all OpenGL resources (such as textures) that are 561 | * associated with that context will be automatically deleted. In order to 562 | * keep rendering correctly, a renderer must recreate any lost resources 563 | * that it still needs. The {@link #onSurfaceCreated(javax.microedition.khronos.opengles.GL10, javax.microedition.khronos.egl.EGLConfig)} method 564 | * is a convenient place to do this. 565 | * 566 | * @see #setRenderer(Renderer) 567 | */ 568 | public interface Renderer { 569 | /** 570 | * Called when the surface is created or recreated. 571 | *

572 | * Called when the rendering thread 573 | * starts and whenever the EGL context is lost. The EGL context will typically 574 | * be lost when the Android device awakes after going to sleep. 575 | *

576 | * Since this method is called at the beginning of rendering, as well as 577 | * every time the EGL context is lost, this method is a convenient place to put 578 | * code to create resources that need to be created when the rendering 579 | * starts, and that need to be recreated when the EGL context is lost. 580 | * Textures are an example of a resource that you might want to create 581 | * here. 582 | *

583 | * Note that when the EGL context is lost, all OpenGL resources associated 584 | * with that context will be automatically deleted. You do not need to call 585 | * the corresponding "glDelete" methods such as glDeleteTextures to 586 | * manually delete these lost resources. 587 | *

588 | * 589 | * @param gl the GL interface. Use instanceof to 590 | * test if the interface supports GL11 or higher interfaces. 591 | * @param config the EGLConfig of the created surface. Can be used 592 | * to create matching pbuffers. 593 | */ 594 | void onSurfaceCreated(GL10 gl, EGLConfig config); 595 | 596 | /** 597 | * Called when the surface changed size. 598 | *

599 | * Called after the surface is created and whenever 600 | * the OpenGL ES surface size changes. 601 | *

602 | * Typically you will set your viewport here. If your camera 603 | * is fixed then you could also set your projection matrix here: 604 | *

 605 |          * void onSurfaceChanged(GL10 gl, int width, int height) {
 606 |          * gl.glViewport(0, 0, width, height);
 607 |          * // for a fixed camera, set the projection too
 608 |          * float ratio = (float) width / height;
 609 |          * gl.glMatrixMode(GL10.GL_PROJECTION);
 610 |          * gl.glLoadIdentity();
 611 |          * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
 612 |          * }
 613 |          * 
614 | * 615 | * @param gl the GL interface. Use instanceof to 616 | * test if the interface supports GL11 or higher interfaces. 617 | * @param width 618 | * @param height 619 | */ 620 | void onSurfaceChanged(GL10 gl, int width, int height); 621 | 622 | /** 623 | * Called to draw the current frame. 624 | *

625 | * This method is responsible for drawing the current frame. 626 | *

627 | * The implementation of this method typically looks like this: 628 | *

 629 |          * void onDrawFrame(GL10 gl) {
 630 |          * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
 631 |          * //... other gl calls to render the scene ...
 632 |          * }
 633 |          * 
634 | * 635 | * @param gl the GL interface. Use instanceof to 636 | * test if the interface supports GL11 or higher interfaces. 637 | */ 638 | void onDrawFrame(GL10 gl); 639 | 640 | void onSurfaceDestroyed(GL10 gl); 641 | } 642 | 643 | /** 644 | * An interface for customizing the eglCreateContext and eglDestroyContext calls. 645 | *

646 | * This interface must be implemented by clients wishing to call 647 | * {@link GLTextureView#setEGLContextFactory(EGLContextFactory)} 648 | */ 649 | public interface EGLContextFactory { 650 | EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); 651 | 652 | void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); 653 | } 654 | 655 | private class DefaultContextFactory implements EGLContextFactory { 656 | private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 657 | 658 | public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { 659 | int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, 660 | EGL10.EGL_NONE}; 661 | 662 | return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, 663 | mEGLContextClientVersion != 0 ? attrib_list : null); 664 | } 665 | 666 | public void destroyContext(EGL10 egl, EGLDisplay display, 667 | EGLContext context) { 668 | if (!egl.eglDestroyContext(display, context)) { 669 | Log.e("DefaultContextFactory", "display:" + display + " context: " + context); 670 | if (LOG_THREADS) { 671 | Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId()); 672 | } 673 | EglHelper.throwEglException("eglDestroyContex", egl.eglGetError()); 674 | } 675 | } 676 | } 677 | 678 | /** 679 | * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. 680 | *

681 | * This interface must be implemented by clients wishing to call 682 | * {@link GLTextureView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} 683 | */ 684 | public interface EGLWindowSurfaceFactory { 685 | /** 686 | * @return null if the surface cannot be constructed. 687 | */ 688 | EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, 689 | Object nativeWindow); 690 | 691 | void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); 692 | } 693 | 694 | private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { 695 | 696 | public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, 697 | EGLConfig config, Object nativeWindow) { 698 | EGLSurface result = null; 699 | try { 700 | result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); 701 | } catch (IllegalArgumentException e) { 702 | // This exception indicates that the surface flinger surface 703 | // is not valid. This can happen if the surface flinger surface has 704 | // been torn down, but the application has not yet been 705 | // notified via SurfaceHolder.Callback.surfaceDestroyed. 706 | // In theory the application should be notified first, 707 | // but in practice sometimes it is not. See b/4588890 708 | Log.e(TAG, "eglCreateWindowSurface", e); 709 | } 710 | return result; 711 | } 712 | 713 | public void destroySurface(EGL10 egl, EGLDisplay display, 714 | EGLSurface surface) { 715 | egl.eglDestroySurface(display, surface); 716 | } 717 | } 718 | 719 | /** 720 | * An interface for choosing an EGLConfig configuration from a list of 721 | * potential configurations. 722 | *

723 | * This interface must be implemented by clients wishing to call 724 | * {@link GLTextureView#setEGLConfigChooser(EGLConfigChooser)} 725 | */ 726 | public interface EGLConfigChooser { 727 | /** 728 | * Choose a configuration from the list. Implementors typically 729 | * implement this method by calling 730 | * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the 731 | * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. 732 | * 733 | * @param egl the EGL10 for the current display. 734 | * @param display the current display. 735 | * @return the chosen configuration. 736 | */ 737 | EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); 738 | } 739 | 740 | private abstract class BaseConfigChooser 741 | implements EGLConfigChooser { 742 | public BaseConfigChooser(int[] configSpec) { 743 | mConfigSpec = filterConfigSpec(configSpec); 744 | } 745 | 746 | public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 747 | int[] num_config = new int[1]; 748 | if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, 749 | num_config)) { 750 | throw new IllegalArgumentException("eglChooseConfig failed"); 751 | } 752 | 753 | int numConfigs = num_config[0]; 754 | 755 | if (numConfigs <= 0) { 756 | throw new IllegalArgumentException( 757 | "No configs match configSpec"); 758 | } 759 | 760 | EGLConfig[] configs = new EGLConfig[numConfigs]; 761 | if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, 762 | num_config)) { 763 | throw new IllegalArgumentException("eglChooseConfig#2 failed"); 764 | } 765 | EGLConfig config = chooseConfig(egl, display, configs); 766 | if (config == null) { 767 | throw new IllegalArgumentException("No config chosen"); 768 | } 769 | return config; 770 | } 771 | 772 | abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 773 | EGLConfig[] configs); 774 | 775 | protected int[] mConfigSpec; 776 | 777 | private int[] filterConfigSpec(int[] configSpec) { 778 | if (mEGLContextClientVersion != 2) { 779 | return configSpec; 780 | } 781 | /* We know none of the subclasses define EGL_RENDERABLE_TYPE. 782 | * And we know the configSpec is well formed. 783 | */ 784 | int len = configSpec.length; 785 | int[] newConfigSpec = new int[len + 2]; 786 | System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); 787 | newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; 788 | newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ 789 | newConfigSpec[len + 1] = EGL10.EGL_NONE; 790 | return newConfigSpec; 791 | } 792 | } 793 | 794 | /** 795 | * Choose a configuration with exactly the specified r,g,b,a sizes, 796 | * and at least the specified depth and stencil sizes. 797 | */ 798 | private class ComponentSizeChooser extends BaseConfigChooser { 799 | public ComponentSizeChooser(int redSize, int greenSize, int blueSize, 800 | int alphaSize, int depthSize, int stencilSize) { 801 | super(new int[]{ 802 | EGL10.EGL_RED_SIZE, redSize, 803 | EGL10.EGL_GREEN_SIZE, greenSize, 804 | EGL10.EGL_BLUE_SIZE, blueSize, 805 | EGL10.EGL_ALPHA_SIZE, alphaSize, 806 | EGL10.EGL_DEPTH_SIZE, depthSize, 807 | EGL10.EGL_STENCIL_SIZE, stencilSize, 808 | EGL10.EGL_NONE}); 809 | mValue = new int[1]; 810 | mRedSize = redSize; 811 | mGreenSize = greenSize; 812 | mBlueSize = blueSize; 813 | mAlphaSize = alphaSize; 814 | mDepthSize = depthSize; 815 | mStencilSize = stencilSize; 816 | } 817 | 818 | @Override 819 | public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 820 | EGLConfig[] configs) { 821 | for (EGLConfig config : configs) { 822 | int d = findConfigAttrib(egl, display, config, 823 | EGL10.EGL_DEPTH_SIZE, 0); 824 | int s = findConfigAttrib(egl, display, config, 825 | EGL10.EGL_STENCIL_SIZE, 0); 826 | if ((d >= mDepthSize) && (s >= mStencilSize)) { 827 | int r = findConfigAttrib(egl, display, config, 828 | EGL10.EGL_RED_SIZE, 0); 829 | int g = findConfigAttrib(egl, display, config, 830 | EGL10.EGL_GREEN_SIZE, 0); 831 | int b = findConfigAttrib(egl, display, config, 832 | EGL10.EGL_BLUE_SIZE, 0); 833 | int a = findConfigAttrib(egl, display, config, 834 | EGL10.EGL_ALPHA_SIZE, 0); 835 | if ((r == mRedSize) && (g == mGreenSize) 836 | && (b == mBlueSize) && (a == mAlphaSize)) { 837 | return config; 838 | } 839 | } 840 | } 841 | return null; 842 | } 843 | 844 | private int findConfigAttrib(EGL10 egl, EGLDisplay display, 845 | EGLConfig config, int attribute, int defaultValue) { 846 | 847 | if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 848 | return mValue[0]; 849 | } 850 | return defaultValue; 851 | } 852 | 853 | private int[] mValue; 854 | // Subclasses can adjust these values: 855 | protected int mRedSize; 856 | protected int mGreenSize; 857 | protected int mBlueSize; 858 | protected int mAlphaSize; 859 | protected int mDepthSize; 860 | protected int mStencilSize; 861 | } 862 | 863 | /** 864 | * This class will choose a RGB_888 surface with 865 | * or without a depth buffer. 866 | */ 867 | private class SimpleEGLConfigChooser extends ComponentSizeChooser { 868 | public SimpleEGLConfigChooser(boolean withDepthBuffer) { 869 | super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0); 870 | } 871 | } 872 | 873 | /** 874 | * An EGL helper class. 875 | */ 876 | 877 | private static class EglHelper { 878 | public EglHelper(WeakReference glSurfaceViewWeakRef) { 879 | mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; 880 | } 881 | 882 | /** 883 | * Initialize EGL for a given configuration spec. 884 | * 885 | * @param 886 | */ 887 | public void start() { 888 | if (LOG_EGL) { 889 | Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); 890 | } 891 | /* 892 | * Get an EGL instance 893 | */ 894 | mEgl = (EGL10) EGLContext.getEGL(); 895 | 896 | /* 897 | * Get to the default display. 898 | */ 899 | mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 900 | 901 | if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 902 | throw new RuntimeException("eglGetDisplay failed"); 903 | } 904 | 905 | /* 906 | * We can now initialize EGL for that display 907 | */ 908 | int[] version = new int[2]; 909 | if (!mEgl.eglInitialize(mEglDisplay, version)) { 910 | throw new RuntimeException("eglInitialize failed"); 911 | } 912 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 913 | if (view == null) { 914 | mEglConfig = null; 915 | mEglContext = null; 916 | } else { 917 | mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); 918 | 919 | /* 920 | * Create an EGL context. We want to do this as rarely as we can, because an 921 | * EGL context is a somewhat heavy object. 922 | */ 923 | mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); 924 | } 925 | if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { 926 | mEglContext = null; 927 | throwEglException("createContext"); 928 | } 929 | if (LOG_EGL) { 930 | Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); 931 | } 932 | 933 | mEglSurface = null; 934 | } 935 | 936 | /** 937 | * Create an egl surface for the current SurfaceHolder surface. If a surface 938 | * already exists, destroy it before creating the new surface. 939 | * 940 | * @return true if the surface was created successfully. 941 | */ 942 | public boolean createSurface() { 943 | if (LOG_EGL) { 944 | Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); 945 | } 946 | /* 947 | * Check preconditions. 948 | */ 949 | if (mEgl == null) { 950 | throw new RuntimeException("egl not initialized"); 951 | } 952 | if (mEglDisplay == null) { 953 | throw new RuntimeException("eglDisplay not initialized"); 954 | } 955 | if (mEglConfig == null) { 956 | throw new RuntimeException("mEglConfig not initialized"); 957 | } 958 | 959 | /* 960 | * The window size has changed, so we need to create a new 961 | * surface. 962 | */ 963 | destroySurfaceImp(); 964 | 965 | /* 966 | * Create an EGL surface we can render into. 967 | */ 968 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 969 | if (view != null) { 970 | mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, 971 | mEglDisplay, mEglConfig, view.getSurfaceTexture()); 972 | } else { 973 | mEglSurface = null; 974 | } 975 | 976 | if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 977 | int error = mEgl.eglGetError(); 978 | if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 979 | Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 980 | } 981 | return false; 982 | } 983 | 984 | /* 985 | * Before we can issue GL commands, we need to make sure 986 | * the context is current and bound to a surface. 987 | */ 988 | if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 989 | /* 990 | * Could not make the context current, probably because the underlying 991 | * SurfaceView surface has been destroyed. 992 | */ 993 | logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); 994 | return false; 995 | } 996 | 997 | return true; 998 | } 999 | 1000 | /** 1001 | * Create a GL object for the current EGL context. 1002 | * 1003 | * @return 1004 | */ 1005 | GL createGL() { 1006 | 1007 | GL gl = mEglContext.getGL(); 1008 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1009 | if (view != null) { 1010 | if (view.mGLWrapper != null) { 1011 | gl = view.mGLWrapper.wrap(gl); 1012 | } 1013 | 1014 | if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { 1015 | int configFlags = 0; 1016 | Writer log = null; 1017 | if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { 1018 | configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; 1019 | } 1020 | if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { 1021 | log = new LogWriter(); 1022 | } 1023 | gl = GLDebugHelper.wrap(gl, configFlags, log); 1024 | } 1025 | } 1026 | return gl; 1027 | } 1028 | 1029 | /** 1030 | * Display the current render surface. 1031 | * 1032 | * @return the EGL error code from eglSwapBuffers. 1033 | */ 1034 | public int swap() { 1035 | if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 1036 | return mEgl.eglGetError(); 1037 | } 1038 | return EGL10.EGL_SUCCESS; 1039 | } 1040 | 1041 | public void destroySurface() { 1042 | if (LOG_EGL) { 1043 | Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId()); 1044 | } 1045 | destroySurfaceImp(); 1046 | } 1047 | 1048 | private void destroySurfaceImp() { 1049 | if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { 1050 | mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 1051 | EGL10.EGL_NO_SURFACE, 1052 | EGL10.EGL_NO_CONTEXT); 1053 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1054 | if (view != null) { 1055 | view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); 1056 | } 1057 | mEglSurface = null; 1058 | } 1059 | } 1060 | 1061 | public void finish() { 1062 | if (LOG_EGL) { 1063 | Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); 1064 | } 1065 | if (mEglContext != null) { 1066 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1067 | if (view != null) { 1068 | view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); 1069 | } 1070 | mEglContext = null; 1071 | } 1072 | if (mEglDisplay != null) { 1073 | mEgl.eglTerminate(mEglDisplay); 1074 | mEglDisplay = null; 1075 | } 1076 | } 1077 | 1078 | private void throwEglException(String function) { 1079 | throwEglException(function, mEgl.eglGetError()); 1080 | } 1081 | 1082 | public static void throwEglException(String function, int error) { 1083 | String message = formatEglError(function, error); 1084 | if (LOG_THREADS) { 1085 | Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " 1086 | + message); 1087 | } 1088 | throw new RuntimeException(message); 1089 | } 1090 | 1091 | public static void logEglErrorAsWarning(String tag, String function, int error) { 1092 | Log.w(tag, formatEglError(function, error)); 1093 | } 1094 | 1095 | public static String formatEglError(String function, int error) { 1096 | return function + " failed: " + error; 1097 | } 1098 | 1099 | private WeakReference mGLSurfaceViewWeakRef; 1100 | EGL10 mEgl; 1101 | EGLDisplay mEglDisplay; 1102 | EGLSurface mEglSurface; 1103 | EGLConfig mEglConfig; 1104 | EGLContext mEglContext; 1105 | 1106 | } 1107 | 1108 | /** 1109 | * A generic GL Thread. Takes care of initializing EGL and GL. Delegates 1110 | * to a Renderer instance to do the actual drawing. Can be configured to 1111 | * render continuously or on request. 1112 | *

1113 | * All potentially blocking synchronization is done through the 1114 | * sGLThreadManager object. This avoids multiple-lock ordering issues. 1115 | */ 1116 | static class GLThread extends Thread { 1117 | GLThread(WeakReference glSurfaceViewWeakRef) { 1118 | super(); 1119 | mWidth = 0; 1120 | mHeight = 0; 1121 | mRequestRender = true; 1122 | mRenderMode = RENDERMODE_CONTINUOUSLY; 1123 | mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; 1124 | } 1125 | 1126 | @Override 1127 | public void run() { 1128 | setName("GLThread " + getId()); 1129 | if (LOG_THREADS) { 1130 | Log.i("GLThread", "starting tid=" + getId()); 1131 | } 1132 | 1133 | try { 1134 | guardedRun(); 1135 | } catch (InterruptedException e) { 1136 | // fall thru and exit normally 1137 | } finally { 1138 | sGLThreadManager.threadExiting(this); 1139 | } 1140 | } 1141 | 1142 | /* 1143 | * This private method should only be called inside a 1144 | * synchronized(sGLThreadManager) block. 1145 | */ 1146 | private void stopEglSurfaceLocked() { 1147 | if (mHaveEglSurface) { 1148 | mHaveEglSurface = false; 1149 | mEglHelper.destroySurface(); 1150 | } 1151 | } 1152 | 1153 | /* 1154 | * This private method should only be called inside a 1155 | * synchronized(sGLThreadManager) block. 1156 | */ 1157 | private void stopEglContextLocked() { 1158 | if (mHaveEglContext) { 1159 | mEglHelper.finish(); 1160 | mHaveEglContext = false; 1161 | sGLThreadManager.releaseEglContextLocked(this); 1162 | } 1163 | } 1164 | 1165 | private void guardedRun() throws InterruptedException { 1166 | mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); 1167 | mHaveEglContext = false; 1168 | mHaveEglSurface = false; 1169 | try { 1170 | GL10 gl = null; 1171 | boolean createEglContext = false; 1172 | boolean createEglSurface = false; 1173 | boolean createGlInterface = false; 1174 | boolean lostEglContext = false; 1175 | boolean sizeChanged = false; 1176 | boolean wantRenderNotification = false; 1177 | boolean doRenderNotification = false; 1178 | boolean askedToReleaseEglContext = false; 1179 | int w = 0; 1180 | int h = 0; 1181 | Runnable event = null; 1182 | 1183 | while (true) { 1184 | synchronized (sGLThreadManager) { 1185 | while (true) { 1186 | if (mShouldExit) { 1187 | return; 1188 | } 1189 | 1190 | if (!mEventQueue.isEmpty()) { 1191 | event = mEventQueue.remove(0); 1192 | break; 1193 | } 1194 | 1195 | // Update the pause state. 1196 | boolean pausing = false; 1197 | if (mPaused != mRequestPaused) { 1198 | pausing = mRequestPaused; 1199 | mPaused = mRequestPaused; 1200 | sGLThreadManager.notifyAll(); 1201 | if (LOG_PAUSE_RESUME) { 1202 | Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId()); 1203 | } 1204 | } 1205 | 1206 | // Do we need to give up the EGL context? 1207 | if (mShouldReleaseEglContext) { 1208 | if (LOG_SURFACE) { 1209 | Log.i("GLThread", "releasing EGL context because asked to tid=" + getId()); 1210 | } 1211 | stopEglSurfaceLocked(); 1212 | stopEglContextLocked(); 1213 | mShouldReleaseEglContext = false; 1214 | askedToReleaseEglContext = true; 1215 | } 1216 | 1217 | // Have we lost the EGL context? 1218 | if (lostEglContext) { 1219 | stopEglSurfaceLocked(); 1220 | stopEglContextLocked(); 1221 | lostEglContext = false; 1222 | } 1223 | 1224 | // When pausing, release the EGL surface: 1225 | if (pausing && mHaveEglSurface) { 1226 | if (LOG_SURFACE) { 1227 | Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); 1228 | } 1229 | stopEglSurfaceLocked(); 1230 | } 1231 | 1232 | // When pausing, optionally release the EGL Context: 1233 | if (pausing && mHaveEglContext) { 1234 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1235 | boolean preserveEglContextOnPause = view == null ? 1236 | false : view.mPreserveEGLContextOnPause; 1237 | if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) { 1238 | stopEglContextLocked(); 1239 | if (LOG_SURFACE) { 1240 | Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); 1241 | } 1242 | } 1243 | } 1244 | 1245 | // When pausing, optionally terminate EGL: 1246 | if (pausing) { 1247 | if (sGLThreadManager.shouldTerminateEGLWhenPausing()) { 1248 | mEglHelper.finish(); 1249 | if (LOG_SURFACE) { 1250 | Log.i("GLThread", "terminating EGL because paused tid=" + getId()); 1251 | } 1252 | } 1253 | } 1254 | 1255 | // Have we lost the SurfaceView surface? 1256 | if ((!mHasSurface) && (!mWaitingForSurface)) { 1257 | if (LOG_SURFACE) { 1258 | Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); 1259 | } 1260 | if (mHaveEglSurface) { 1261 | stopEglSurfaceLocked(); 1262 | } 1263 | mWaitingForSurface = true; 1264 | mSurfaceIsBad = false; 1265 | sGLThreadManager.notifyAll(); 1266 | } 1267 | 1268 | // Have we acquired the surface view surface? 1269 | if (mHasSurface && mWaitingForSurface) { 1270 | if (LOG_SURFACE) { 1271 | Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); 1272 | } 1273 | mWaitingForSurface = false; 1274 | sGLThreadManager.notifyAll(); 1275 | } 1276 | 1277 | if (doRenderNotification) { 1278 | if (LOG_SURFACE) { 1279 | Log.i("GLThread", "sending render notification tid=" + getId()); 1280 | } 1281 | wantRenderNotification = false; 1282 | doRenderNotification = false; 1283 | mRenderComplete = true; 1284 | sGLThreadManager.notifyAll(); 1285 | } 1286 | 1287 | // Ready to draw? 1288 | if (readyToDraw()) { 1289 | 1290 | // If we don't have an EGL context, try to acquire one. 1291 | if (!mHaveEglContext) { 1292 | if (askedToReleaseEglContext) { 1293 | askedToReleaseEglContext = false; 1294 | } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) { 1295 | try { 1296 | mEglHelper.start(); 1297 | } catch (RuntimeException t) { 1298 | sGLThreadManager.releaseEglContextLocked(this); 1299 | throw t; 1300 | } 1301 | mHaveEglContext = true; 1302 | createEglContext = true; 1303 | 1304 | sGLThreadManager.notifyAll(); 1305 | } 1306 | } 1307 | 1308 | if (mHaveEglContext && !mHaveEglSurface) { 1309 | mHaveEglSurface = true; 1310 | createEglSurface = true; 1311 | createGlInterface = true; 1312 | sizeChanged = true; 1313 | } 1314 | 1315 | if (mHaveEglSurface) { 1316 | if (mSizeChanged) { 1317 | sizeChanged = true; 1318 | w = mWidth; 1319 | h = mHeight; 1320 | wantRenderNotification = true; 1321 | if (LOG_SURFACE) { 1322 | Log.i("GLThread", 1323 | "noticing that we want render notification tid=" 1324 | + getId()); 1325 | } 1326 | 1327 | // Destroy and recreate the EGL surface. 1328 | createEglSurface = true; 1329 | 1330 | mSizeChanged = false; 1331 | } 1332 | mRequestRender = false; 1333 | sGLThreadManager.notifyAll(); 1334 | break; 1335 | } 1336 | } 1337 | 1338 | // By design, this is the only place in a GLThread thread where we wait(). 1339 | if (LOG_THREADS) { 1340 | Log.i("GLThread", "waiting tid=" + getId() 1341 | + " mHaveEglContext: " + mHaveEglContext 1342 | + " mHaveEglSurface: " + mHaveEglSurface 1343 | + " mPaused: " + mPaused 1344 | + " mHasSurface: " + mHasSurface 1345 | + " mSurfaceIsBad: " + mSurfaceIsBad 1346 | + " mWaitingForSurface: " + mWaitingForSurface 1347 | + " mWidth: " + mWidth 1348 | + " mHeight: " + mHeight 1349 | + " mRequestRender: " + mRequestRender 1350 | + " mRenderMode: " + mRenderMode); 1351 | } 1352 | sGLThreadManager.wait(); 1353 | } 1354 | } // end of synchronized(sGLThreadManager) 1355 | 1356 | if (event != null) { 1357 | event.run(); 1358 | event = null; 1359 | continue; 1360 | } 1361 | 1362 | if (createEglSurface) { 1363 | if (LOG_SURFACE) { 1364 | Log.w("GLThread", "egl createSurface"); 1365 | } 1366 | if (!mEglHelper.createSurface()) { 1367 | synchronized (sGLThreadManager) { 1368 | mSurfaceIsBad = true; 1369 | sGLThreadManager.notifyAll(); 1370 | } 1371 | continue; 1372 | } 1373 | createEglSurface = false; 1374 | } 1375 | 1376 | if (createGlInterface) { 1377 | gl = (GL10) mEglHelper.createGL(); 1378 | 1379 | sGLThreadManager.checkGLDriver(gl); 1380 | createGlInterface = false; 1381 | } 1382 | 1383 | if (createEglContext) { 1384 | if (LOG_RENDERER) { 1385 | Log.w("GLThread", "onSurfaceCreated"); 1386 | } 1387 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1388 | if (view != null) { 1389 | view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 1390 | } 1391 | createEglContext = false; 1392 | } 1393 | 1394 | if (sizeChanged) { 1395 | if (LOG_RENDERER) { 1396 | Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); 1397 | } 1398 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1399 | if (view != null) { 1400 | view.mRenderer.onSurfaceChanged(gl, w, h); 1401 | } 1402 | sizeChanged = false; 1403 | } 1404 | 1405 | if (LOG_RENDERER_DRAW_FRAME) { 1406 | Log.w("GLThread", "onDrawFrame tid=" + getId()); 1407 | } 1408 | { 1409 | GLTextureView view = mGLSurfaceViewWeakRef.get(); 1410 | if (view != null) { 1411 | view.mRenderer.onDrawFrame(gl); 1412 | } 1413 | } 1414 | int swapError = mEglHelper.swap(); 1415 | switch (swapError) { 1416 | case EGL10.EGL_SUCCESS: 1417 | break; 1418 | case EGL11.EGL_CONTEXT_LOST: 1419 | if (LOG_SURFACE) { 1420 | Log.i("GLThread", "egl context lost tid=" + getId()); 1421 | } 1422 | lostEglContext = true; 1423 | break; 1424 | default: 1425 | // Other errors typically mean that the current surface is bad, 1426 | // probably because the SurfaceView surface has been destroyed, 1427 | // but we haven't been notified yet. 1428 | // Log the error to help developers understand why rendering stopped. 1429 | EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); 1430 | 1431 | synchronized (sGLThreadManager) { 1432 | mSurfaceIsBad = true; 1433 | sGLThreadManager.notifyAll(); 1434 | } 1435 | break; 1436 | } 1437 | 1438 | if (wantRenderNotification) { 1439 | doRenderNotification = true; 1440 | } 1441 | } 1442 | 1443 | } finally { 1444 | /* 1445 | * clean-up everything... 1446 | */ 1447 | synchronized (sGLThreadManager) { 1448 | stopEglSurfaceLocked(); 1449 | stopEglContextLocked(); 1450 | } 1451 | } 1452 | } 1453 | 1454 | public boolean ableToDraw() { 1455 | return mHaveEglContext && mHaveEglSurface && readyToDraw(); 1456 | } 1457 | 1458 | private boolean readyToDraw() { 1459 | return (!mPaused) && mHasSurface && (!mSurfaceIsBad) 1460 | && (mWidth > 0) && (mHeight > 0) 1461 | && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); 1462 | } 1463 | 1464 | public void setRenderMode(int renderMode) { 1465 | if (!((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY))) { 1466 | throw new IllegalArgumentException("renderMode"); 1467 | } 1468 | synchronized (sGLThreadManager) { 1469 | mRenderMode = renderMode; 1470 | sGLThreadManager.notifyAll(); 1471 | } 1472 | } 1473 | 1474 | public int getRenderMode() { 1475 | synchronized (sGLThreadManager) { 1476 | return mRenderMode; 1477 | } 1478 | } 1479 | 1480 | public void requestRender() { 1481 | synchronized (sGLThreadManager) { 1482 | mRequestRender = true; 1483 | sGLThreadManager.notifyAll(); 1484 | } 1485 | } 1486 | 1487 | public void surfaceCreated() { 1488 | synchronized (sGLThreadManager) { 1489 | if (LOG_THREADS) { 1490 | Log.i("GLThread", "surfaceCreated tid=" + getId()); 1491 | } 1492 | mHasSurface = true; 1493 | sGLThreadManager.notifyAll(); 1494 | while ((mWaitingForSurface) && (!mExited)) { 1495 | try { 1496 | sGLThreadManager.wait(); 1497 | } catch (InterruptedException e) { 1498 | Thread.currentThread().interrupt(); 1499 | } 1500 | } 1501 | } 1502 | } 1503 | 1504 | public void surfaceDestroyed() { 1505 | synchronized (sGLThreadManager) { 1506 | if (LOG_THREADS) { 1507 | Log.i("GLThread", "surfaceDestroyed tid=" + getId()); 1508 | } 1509 | mHasSurface = false; 1510 | sGLThreadManager.notifyAll(); 1511 | while ((!mWaitingForSurface) && (!mExited)) { 1512 | try { 1513 | sGLThreadManager.wait(); 1514 | } catch (InterruptedException e) { 1515 | Thread.currentThread().interrupt(); 1516 | } 1517 | } 1518 | } 1519 | } 1520 | 1521 | public void onPause() { 1522 | synchronized (sGLThreadManager) { 1523 | if (LOG_PAUSE_RESUME) { 1524 | Log.i("GLThread", "onPause tid=" + getId()); 1525 | } 1526 | mRequestPaused = true; 1527 | sGLThreadManager.notifyAll(); 1528 | while ((!mExited) && (!mPaused)) { 1529 | if (LOG_PAUSE_RESUME) { 1530 | Log.i("Main thread", "onPause waiting for mPaused."); 1531 | } 1532 | try { 1533 | sGLThreadManager.wait(); 1534 | } catch (InterruptedException ex) { 1535 | Thread.currentThread().interrupt(); 1536 | } 1537 | } 1538 | } 1539 | } 1540 | 1541 | public void onResume() { 1542 | synchronized (sGLThreadManager) { 1543 | if (LOG_PAUSE_RESUME) { 1544 | Log.i("GLThread", "onResume tid=" + getId()); 1545 | } 1546 | mRequestPaused = false; 1547 | mRequestRender = true; 1548 | mRenderComplete = false; 1549 | sGLThreadManager.notifyAll(); 1550 | while ((!mExited) && mPaused && (!mRenderComplete)) { 1551 | if (LOG_PAUSE_RESUME) { 1552 | Log.i("Main thread", "onResume waiting for !mPaused."); 1553 | } 1554 | try { 1555 | sGLThreadManager.wait(); 1556 | } catch (InterruptedException ex) { 1557 | Thread.currentThread().interrupt(); 1558 | } 1559 | } 1560 | } 1561 | } 1562 | 1563 | public void onWindowResize(int w, int h) { 1564 | synchronized (sGLThreadManager) { 1565 | mWidth = w; 1566 | mHeight = h; 1567 | mSizeChanged = true; 1568 | mRequestRender = true; 1569 | mRenderComplete = false; 1570 | sGLThreadManager.notifyAll(); 1571 | 1572 | // Wait for thread to react to resize and render a frame 1573 | while (!mExited && !mPaused && !mRenderComplete 1574 | && ableToDraw()) { 1575 | if (LOG_SURFACE) { 1576 | Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId()); 1577 | } 1578 | try { 1579 | sGLThreadManager.wait(); 1580 | } catch (InterruptedException ex) { 1581 | Thread.currentThread().interrupt(); 1582 | } 1583 | } 1584 | } 1585 | } 1586 | 1587 | public void requestExitAndWait() { 1588 | // don't call this from GLThread thread or it is a guaranteed 1589 | // deadlock! 1590 | synchronized (sGLThreadManager) { 1591 | mShouldExit = true; 1592 | sGLThreadManager.notifyAll(); 1593 | while (!mExited) { 1594 | try { 1595 | sGLThreadManager.wait(); 1596 | } catch (InterruptedException ex) { 1597 | Thread.currentThread().interrupt(); 1598 | } 1599 | } 1600 | } 1601 | } 1602 | 1603 | public void requestReleaseEglContextLocked() { 1604 | mShouldReleaseEglContext = true; 1605 | sGLThreadManager.notifyAll(); 1606 | } 1607 | 1608 | /** 1609 | * Queue an "event" to be run on the GL rendering thread. 1610 | * 1611 | * @param r the runnable to be run on the GL rendering thread. 1612 | */ 1613 | public void queueEvent(Runnable r) { 1614 | if (r == null) { 1615 | throw new IllegalArgumentException("r must not be null"); 1616 | } 1617 | synchronized (sGLThreadManager) { 1618 | mEventQueue.add(r); 1619 | sGLThreadManager.notifyAll(); 1620 | } 1621 | } 1622 | 1623 | // Once the thread is started, all accesses to the following member 1624 | // variables are protected by the sGLThreadManager monitor 1625 | private boolean mShouldExit; 1626 | private boolean mExited; 1627 | private boolean mRequestPaused; 1628 | private boolean mPaused; 1629 | private boolean mHasSurface; 1630 | private boolean mSurfaceIsBad; 1631 | private boolean mWaitingForSurface; 1632 | private boolean mHaveEglContext; 1633 | private boolean mHaveEglSurface; 1634 | private boolean mShouldReleaseEglContext; 1635 | private int mWidth; 1636 | private int mHeight; 1637 | private int mRenderMode; 1638 | private boolean mRequestRender; 1639 | private boolean mRenderComplete; 1640 | private ArrayList mEventQueue = new ArrayList(); 1641 | private boolean mSizeChanged = true; 1642 | 1643 | // End of member variables protected by the sGLThreadManager monitor. 1644 | 1645 | private EglHelper mEglHelper; 1646 | 1647 | /** 1648 | * Set once at thread construction time, nulled out when the parent view is garbage 1649 | * called. This weak reference allows the GLTextureView to be garbage collected while 1650 | * the GLThread is still alive. 1651 | */ 1652 | private WeakReference mGLSurfaceViewWeakRef; 1653 | 1654 | } 1655 | 1656 | static class LogWriter extends Writer { 1657 | 1658 | @Override 1659 | public void close() { 1660 | flushBuilder(); 1661 | } 1662 | 1663 | @Override 1664 | public void flush() { 1665 | flushBuilder(); 1666 | } 1667 | 1668 | @Override 1669 | public void write(char[] buf, int offset, int count) { 1670 | for (int i = 0; i < count; i++) { 1671 | char c = buf[offset + i]; 1672 | if (c == '\n') { 1673 | flushBuilder(); 1674 | } else { 1675 | mBuilder.append(c); 1676 | } 1677 | } 1678 | } 1679 | 1680 | private void flushBuilder() { 1681 | if (mBuilder.length() > 0) { 1682 | Log.v("GLTextureView", mBuilder.toString()); 1683 | mBuilder.delete(0, mBuilder.length()); 1684 | } 1685 | } 1686 | 1687 | private StringBuilder mBuilder = new StringBuilder(); 1688 | } 1689 | 1690 | 1691 | private void checkRenderThreadState() { 1692 | if (mGLThread != null) { 1693 | throw new IllegalStateException( 1694 | "setRenderer has already been called for this instance."); 1695 | } 1696 | } 1697 | 1698 | private static class GLThreadManager { 1699 | private static String TAG = "GLThreadManager"; 1700 | 1701 | public synchronized void threadExiting(GLThread thread) { 1702 | if (LOG_THREADS) { 1703 | Log.i("GLThread", "exiting tid=" + thread.getId()); 1704 | } 1705 | thread.mExited = true; 1706 | if (mEglOwner == thread) { 1707 | mEglOwner = null; 1708 | } 1709 | notifyAll(); 1710 | } 1711 | 1712 | /* 1713 | * Tries once to acquire the right to use an EGL 1714 | * context. Does not block. Requires that we are already 1715 | * in the sGLThreadManager monitor when this is called. 1716 | * 1717 | * @return true if the right to use an EGL context was acquired. 1718 | */ 1719 | public boolean tryAcquireEglContextLocked(GLThread thread) { 1720 | if (mEglOwner == thread || mEglOwner == null) { 1721 | mEglOwner = thread; 1722 | notifyAll(); 1723 | return true; 1724 | } 1725 | checkGLESVersion(); 1726 | if (mMultipleGLESContextsAllowed) { 1727 | return true; 1728 | } 1729 | // Notify the owning thread that it should release the context. 1730 | // TODO: implement a fairness policy. Currently 1731 | // if the owning thread is drawing continuously it will just 1732 | // reacquire the EGL context. 1733 | if (mEglOwner != null) { 1734 | mEglOwner.requestReleaseEglContextLocked(); 1735 | } 1736 | return false; 1737 | } 1738 | 1739 | /* 1740 | * Releases the EGL context. Requires that we are already in the 1741 | * sGLThreadManager monitor when this is called. 1742 | */ 1743 | public void releaseEglContextLocked(GLThread thread) { 1744 | if (mEglOwner == thread) { 1745 | mEglOwner = null; 1746 | } 1747 | notifyAll(); 1748 | } 1749 | 1750 | public synchronized boolean shouldReleaseEGLContextWhenPausing() { 1751 | // Release the EGL context when pausing even if 1752 | // the hardware supports multiple EGL contexts. 1753 | // Otherwise the device could run out of EGL contexts. 1754 | return mLimitedGLESContexts; 1755 | } 1756 | 1757 | public synchronized boolean shouldTerminateEGLWhenPausing() { 1758 | checkGLESVersion(); 1759 | return !mMultipleGLESContextsAllowed; 1760 | } 1761 | 1762 | public synchronized void checkGLDriver(GL10 gl) { 1763 | if (!mGLESDriverCheckComplete) { 1764 | checkGLESVersion(); 1765 | String renderer = gl.glGetString(GL10.GL_RENDERER); 1766 | if (mGLESVersion < kGLES_20) { 1767 | mMultipleGLESContextsAllowed = 1768 | !renderer.startsWith(kMSM7K_RENDERER_PREFIX); 1769 | notifyAll(); 1770 | } 1771 | mLimitedGLESContexts = !mMultipleGLESContextsAllowed; 1772 | if (LOG_SURFACE) { 1773 | Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = " 1774 | + mMultipleGLESContextsAllowed 1775 | + " mLimitedGLESContexts = " + mLimitedGLESContexts); 1776 | } 1777 | mGLESDriverCheckComplete = true; 1778 | } 1779 | } 1780 | 1781 | private void checkGLESVersion() { 1782 | if (!mGLESVersionCheckComplete) { 1783 | // mGLESVersion = SystemProperties.getInt( 1784 | // "ro.opengles.version", 1785 | // ConfigurationInfo.GL_ES_VERSION_UNDEFINED); 1786 | // if (mGLESVersion >= kGLES_20) { 1787 | // mMultipleGLESContextsAllowed = true; 1788 | // } 1789 | // if (LOG_SURFACE) { 1790 | // Log.w(TAG, "checkGLESVersion mGLESVersion =" + 1791 | // " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed); 1792 | // } 1793 | mGLESVersionCheckComplete = true; 1794 | } 1795 | } 1796 | 1797 | /** 1798 | * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides 1799 | * support for hardware-accelerated views, therefore multiple EGL contexts are 1800 | * supported on all Android 3.0+ EGL drivers. 1801 | */ 1802 | private boolean mGLESVersionCheckComplete; 1803 | private int mGLESVersion; 1804 | private boolean mGLESDriverCheckComplete; 1805 | private boolean mMultipleGLESContextsAllowed; 1806 | private boolean mLimitedGLESContexts; 1807 | private static final int kGLES_20 = 0x20000; 1808 | private static final String kMSM7K_RENDERER_PREFIX = 1809 | "Q3Dimension MSM7500 "; 1810 | private GLThread mEglOwner; 1811 | } 1812 | 1813 | private static final GLThreadManager sGLThreadManager = new GLThreadManager(); 1814 | 1815 | private final WeakReference mThisWeakRef = 1816 | new WeakReference(this); 1817 | private GLThread mGLThread; 1818 | private Renderer mRenderer; 1819 | private boolean mDetached; 1820 | private EGLConfigChooser mEGLConfigChooser; 1821 | private EGLContextFactory mEGLContextFactory; 1822 | private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; 1823 | private GLWrapper mGLWrapper; 1824 | private int mDebugFlags; 1825 | private int mEGLContextClientVersion; 1826 | private boolean mPreserveEGLContextOnPause; 1827 | } --------------------------------------------------------------------------------