├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── deploy.yml │ └── snapshot.yml ├── .gitignore ├── .run ├── deploySonatypeReleases.run.xml ├── deploySonatypeSnapshots.run.xml └── publishAllBintray.run.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── demo ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── otaliastudios │ │ └── opengl │ │ └── demo │ │ ├── ShapesActivity.kt │ │ └── VideoActivity.kt │ ├── logo-web.png │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_shapes.xml │ └── activity_video.xml │ ├── menu │ ├── activity_shapes.xml │ └── activity_video.xml │ ├── mipmap-anydpi-v26 │ └── logo.xml │ ├── mipmap-hdpi │ ├── logo.png │ ├── logo_background.png │ └── logo_foreground.png │ ├── mipmap-mdpi │ ├── logo.png │ ├── logo_background.png │ └── logo_foreground.png │ ├── mipmap-xhdpi │ ├── logo.png │ ├── logo_background.png │ └── logo_foreground.png │ ├── mipmap-xxhdpi │ ├── logo.png │ ├── logo_background.png │ └── logo_foreground.png │ ├── mipmap-xxxhdpi │ ├── logo.png │ ├── logo_background.png │ └── logo_foreground.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── docs ├── .gitignore ├── Gemfile ├── README.md ├── _about │ ├── changelog.md │ ├── getting-started.md │ └── install.md ├── _config.yml ├── _docs │ ├── drawables.md │ ├── egl-management.md │ ├── programs.md │ ├── scenes.md │ └── textures.md ├── _extra │ ├── contact.md │ ├── contributing.md │ └── donate.md ├── _includes │ ├── disqus.html │ ├── footer.html │ ├── google_analytics.html │ ├── head.html │ ├── header.html │ └── navigation.html ├── _layouts │ ├── landing.html │ ├── main.html │ └── page.html ├── css │ ├── colors.css │ ├── fonts.css │ ├── fonts_responsive.css │ ├── landing.css │ ├── main.css │ └── syntax.css ├── home.md ├── icons │ ├── github.svg │ └── menu.svg ├── index.md ├── script │ └── launch └── static │ ├── banner.png │ ├── icon_foreground.png │ ├── screenshot-1.png │ ├── screenshot-2.png │ └── screenshot-3.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidJvmMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── otaliastudios │ │ └── opengl │ │ ├── core │ │ ├── EglConfigChooser.kt │ │ ├── EglContextFactory.kt │ │ └── EglCore.kt │ │ ├── geometry │ │ ├── PointF.kt │ │ └── RectF.kt │ │ ├── internal │ │ ├── annotations.kt │ │ ├── egl.kt │ │ ├── gl.kt │ │ └── misc.kt │ │ ├── program │ │ └── GlFlatProgram.kt │ │ ├── surface │ │ ├── EglSurface.kt │ │ └── EglWindowSurface.kt │ │ └── types │ │ └── buffers.kt │ ├── androidNative32BitMain │ └── kotlin │ │ └── com │ │ └── otaliastudios │ │ └── opengl │ │ └── internal │ │ └── gl.kt │ ├── androidNative64BitMain │ └── kotlin │ │ └── com │ │ └── otaliastudios │ │ └── opengl │ │ └── internal │ │ └── gl.kt │ ├── androidNativeMain │ └── kotlin │ │ └── com │ │ └── otaliastudios │ │ └── opengl │ │ ├── cinterop.kt │ │ ├── core │ │ └── EglCore.kt │ │ ├── geometry │ │ ├── PointF.kt │ │ └── RectF.kt │ │ ├── internal │ │ ├── egl.kt │ │ ├── gl.kt │ │ └── misc.kt │ │ ├── program │ │ └── GlFlatProgram.kt │ │ ├── surface │ │ ├── EglSurface.kt │ │ └── EglWindowSurface.kt │ │ └── types │ │ └── buffers.kt │ └── commonMain │ └── kotlin │ └── com │ └── otaliastudios │ └── opengl │ ├── buffer │ ├── GlBuffer.kt │ └── GlShaderStorageBuffer.kt │ ├── core │ ├── EglNativeConfigChooser.kt │ ├── EglNativeCore.kt │ ├── Egloo.kt │ ├── GlBindable.kt │ └── GlViewportAware.kt │ ├── draw │ ├── Gl2dDrawable.kt │ ├── Gl2dMesh.kt │ ├── Gl3dDrawable.kt │ ├── GlCircle.kt │ ├── GlDrawable.kt │ ├── GlPolygon.kt │ ├── GlRect.kt │ ├── GlRoundRect.kt │ ├── GlSquare.kt │ └── GlTriangle.kt │ ├── extensions │ ├── Buffers.kt │ └── Matrix.kt │ ├── geometry │ ├── IndexedPointF.kt │ ├── IndexedSegmentF.kt │ ├── PointF.kt │ ├── RectF.kt │ └── SegmentF.kt │ ├── internal │ ├── annotations.kt │ ├── egl.kt │ ├── gl.kt │ └── misc.kt │ ├── program │ ├── GlFlatProgram.kt │ ├── GlProgram.kt │ ├── GlProgramLocation.kt │ ├── GlShader.kt │ └── GlTextureProgram.kt │ ├── scene │ └── GlScene.kt │ ├── surface │ ├── EglOffscreenSurface.kt │ ├── EglSurface.kt │ └── EglWindowSurface.kt │ ├── texture │ ├── GlFramebuffer.kt │ └── GlTexture.kt │ └── types │ └── buffers.kt └── settings.gradle /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mat.iavarone@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing guidelines are [hosted here](https://natario1.github.io/Egloo/extra/contributing). -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [natario1] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 2 | # Renaming ? Change the README badge. 3 | name: Build 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | jobs: 10 | ANDROID_BASE_CHECKS: 11 | name: Base Checks 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-java@v1 16 | with: 17 | java-version: 1.8 18 | - name: Perform base checks 19 | run: ./gradlew demo:assembleDebug library:deployLocally -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 2 | name: Deploy 3 | on: 4 | release: 5 | types: [published] 6 | jobs: 7 | MAVEN_UPLOAD: 8 | name: Maven Upload 9 | runs-on: ubuntu-latest 10 | env: 11 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 12 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} 13 | SONATYPE_USER: ${{ secrets.SONATYPE_USER }} 14 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-java@v1 18 | with: 19 | java-version: 1.8 20 | - name: Perform maven upload 21 | run: ./gradlew library:deploySonatypeReleases -------------------------------------------------------------------------------- /.github/workflows/snapshot.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 2 | # Renaming ? Change the README badge. 3 | name: Snapshot 4 | on: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | SNAPSHOT: 10 | name: Publish Snapshots 11 | runs-on: ubuntu-latest 12 | env: 13 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 14 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} 15 | SONATYPE_USER: ${{ secrets.SONATYPE_USER }} 16 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-java@v1 20 | with: 21 | java-version: 1.8 22 | - name: Publish sonatype snapshots 23 | run: ./gradlew library:deploySonatypeSnapshots -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea 4 | .DS_Store 5 | /build 6 | *.iml 7 | /prebuilt -------------------------------------------------------------------------------- /.run/deploySonatypeReleases.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /.run/deploySonatypeSnapshots.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /.run/publishAllBintray.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v0.3.1 2 | 3 | - Fixes a bug in texture program ([#9][9]) 4 | 5 | https://github.com/natario1/Egloo/compare/v0.3.0...v0.3.1 6 | 7 | ## v0.3.0 8 | 9 | - Support for non-rect shapes in textures ([#8][8]) 10 | 11 | https://github.com/natario1/Egloo/compare/v0.2.4...v0.3.0 12 | 13 | ### v0.2.4 14 | 15 | - Added GlRoundRect ([#7][7]) 16 | 17 | https://github.com/natario1/Egloo/compare/v0.2.3...v0.2.4 18 | 19 | ### v0.2.3 20 | 21 | - Fix a bug with GlRect ([#6][6]) 22 | 23 | https://github.com/natario1/Egloo/compare/v0.2.2...v0.2.3 24 | 25 | ### v0.2.2 26 | 27 | - Fix a bug with GlTextureProgram implementation ([#5][5]) 28 | 29 | https://github.com/natario1/Egloo/compare/v0.2.1...v0.2.2 30 | 31 | ### v0.2.1 32 | 33 | - New: GlDrawables can adjust their scale automatically based on viewport size. You should call glScene.setViewportSize or glDrawable.setViewportSize ([#3][3]) 34 | - Improvement: better GlSquare default values for rotation and radius ([#3][3]) 35 | 36 | [3]: https://github.com/natario1/Egloo/pull/3 37 | [5]: https://github.com/natario1/Egloo/pull/5 38 | [7]: https://github.com/natario1/Egloo/pull/7 39 | [8]: https://github.com/natario1/Egloo/pull/8 40 | [9]: https://github.com/natario1/Egloo/pull/9 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mattia Iavarone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/natario1/Egloo/workflows/Build/badge.svg?event=push)](https://github.com/natario1/Egloo/actions) 2 | [![Release](https://img.shields.io/github/release/natario1/Egloo.svg)](https://github.com/natario1/Egloo/releases) 3 | [![Issues](https://img.shields.io/github/issues-raw/natario1/Egloo.svg)](https://github.com/natario1/Egloo/issues) 4 | 5 | ⠀ 6 | 7 |

8 | 9 |

10 | 11 | *Looking for a powerful camera library? Take a look at our [CameraView](https://github.com/natario1/CameraView).* 12 | 13 | *Transcoding videos with Egloo? Take a look at our [Transcoder](https://github.com/natario1/Transcoder).* 14 | 15 | *Need support, consulting, or have any other business-related question? Feel free to get in touch.* 16 | 17 | *Like the project, make profit from it, or simply want to thank back? Please consider [sponsoring me](https://github.com/sponsors/natario1)!* 18 | 19 | # Egloo 20 | 21 | Egloo is a simple and lightweight multiplatform framework for OpenGL ES drawing and EGL management 22 | that uses object-oriented components - hence the name Egl**oo**. It can serve as a basis for 23 | complex drawing operations, but is mostly designed for helping in making common tasks simpler, 24 | even for people that do not have any OpenGL experience. 25 | 26 | Approaching OpenGL from high-level languages can be hard because of the deep differences in the OpenGL API design 27 | with respect to a typical object-oriented context. Egloo tries to take some of these difficulties away 28 | by creating a **thin**, flexible layer of abstraction around EGL and GLES calls. 29 | 30 | You can take a look at the demo app or see Egloo in action in some popular Android projects: 31 | 32 | - for camera preview and real-time filters: see [CameraView](https://github.com/natario1/CameraView) 33 | - in a zoomable Surface: see [ZoomLayout](https://github.com/natario1/ZoomLayout) 34 | - for transcoding videos: see [Transcoder](https://github.com/natario1/Transcoder) 35 | 36 | Starting from 0.5.0, Egloo can run on native targets. We provide an implementation for Android native libraries, 37 | but other targets like iOS can probably be added easily. 38 | 39 | ```kotlin 40 | // Regular Android projects 41 | implementation("com.otaliastudios.opengl:egloo:0.6.1") 42 | 43 | // Kotlin Multiplatform projects: add egloo-multiplatform to your common source set. 44 | implementation("com.otaliastudios.opengl:egloo-multiplatform:0.6.1") 45 | 46 | // Kotlin Multiplatform projects: or use the granular dependencies: 47 | implementation("com.otaliastudios.opengl:egloo-android:0.6.1") // Android AAR 48 | implementation("com.otaliastudios.opengl:egloo-androidnativex86:0.6.1") // Android Native KLib 49 | implementation("com.otaliastudios.opengl:egloo-androidnativex64:0.6.1") // Android Native KLib 50 | implementation("com.otaliastudios.opengl:egloo-androidnativearm32:0.6.1") // Android Native KLib 51 | implementation("com.otaliastudios.opengl:egloo-androidnativearm64:0.6.1") // Android Native KLib 52 | ``` 53 | 54 | ## Features 55 | 56 | - EGL setup and management [[docs]](https://natario1.github.io/docs/egl-management) 57 | - GLSurfaceView utilities [[docs]](https://natario1.github.io/docs/egl-management#glsurfaceview-utilities) 58 | - Drawables abstraction [[docs]](https://natario1.github.io/docs/drawables) 59 | - Programs abstraction [[docs]](https://natario1.github.io/docs/programs) 60 | - Scenes to hold view and projection matrix [[docs]](https://natario1.github.io/docs/scenes) 61 | 62 | 63 | ⠀ 64 | 65 |

66 | 67 |

68 | 69 | 70 | ⠀ 71 | 72 | 73 | ## Support 74 | 75 | If you like the project, make profit from it, or simply want to thank back, please consider 76 | [sponsoring me](https://github.com/sponsors/natario1) through the GitHub Sponsors program! 77 | You can have your company logo here, get private support hours or simply help me push this forward. 78 | 79 | Feel free to contact me for support, consulting or any 80 | other business-related question. 81 | 82 | ## Setup 83 | 84 | Please read the [official website](https://natario1.github.io/Egloo) for setup instructions and documentation. 85 | You might also be interested in our [changelog](https://natario1.github.io/Egloo/about/changelog). 86 | Using Egloo is very simple. The function below will create a context, draw a red triangle and release: 87 | 88 | ```kotlin 89 | // Configure an EGL context and window 90 | val core = EglCore() 91 | val window = EglWindowSurface(core, outputSurface) 92 | window.makeCurrent() 93 | 94 | // Draw 95 | val drawable = GlTriangle() // GlDrawable: what to draw 96 | val program = GlFlatProgram() // GlProgram: how to draw 97 | program.setColor(Color.RED) 98 | program.draw(drawable) 99 | 100 | // Publish what we have drawn 101 | // The outputSurface will receive our frame 102 | window.swapBuffers() 103 | 104 | // Release 105 | program.release() 106 | window.release() 107 | core.release() 108 | ``` 109 | 110 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | extra["androidMinSdkVersion"] = 18 3 | extra["androidCompileSdkVersion"] = 30 4 | extra["androidTargetSdkVersion"] = 30 5 | 6 | repositories { 7 | google() 8 | mavenCentral() 9 | mavenLocal() 10 | maven("../MavenPublisher/publisher/build/prebuilt") 11 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") 12 | } 13 | 14 | configurations.configureEach { 15 | resolutionStrategy { 16 | cacheChangingModulesFor(0, TimeUnit.SECONDS) 17 | } 18 | } 19 | 20 | dependencies { 21 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20") 22 | classpath("com.android.tools.build:gradle:4.2.2") 23 | classpath("io.deepmedia.tools:publisher:0.6.0") 24 | } 25 | } 26 | 27 | allprojects { 28 | repositories { 29 | google() 30 | mavenCentral() 31 | } 32 | } 33 | 34 | tasks.register("clean", Delete::class) { 35 | delete(buildDir) 36 | } -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | id("kotlin-android-extensions") 5 | } 6 | 7 | android { 8 | setCompileSdkVersion(property("androidCompileSdkVersion") as Int) 9 | 10 | defaultConfig { 11 | applicationId = "com.otaliastudios.opengl.demo" 12 | setMinSdkVersion(property("androidMinSdkVersion") as Int) 13 | setTargetSdkVersion(property("androidTargetSdkVersion") as Int) 14 | versionCode = 1 15 | versionName = "1.0" 16 | } 17 | 18 | // required by ExoPlayer 19 | compileOptions { 20 | targetCompatibility = JavaVersion.VERSION_1_8 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation("androidx.appcompat:appcompat:1.3.0") 26 | implementation("androidx.core:core-ktx:1.6.0") 27 | implementation("com.google.android.exoplayer:exoplayer-core:2.14.1") 28 | implementation("com.google.android.exoplayer:exoplayer-ui:2.14.1") 29 | implementation(project(":library")) 30 | 31 | // For testing, instead of the project dependency: 32 | // implementation("com.otaliastudios.opengl:egloo:0.5.1-rc1") 33 | // implementation("com.otaliastudios.opengl:egloo-android:0.5.1-rc1") 34 | } 35 | -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /demo/src/main/java/com/otaliastudios/opengl/demo/ShapesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.demo 2 | 3 | import android.animation.ValueAnimator 4 | import android.content.Intent 5 | import android.graphics.Color 6 | import android.graphics.PixelFormat 7 | import android.graphics.RectF 8 | import android.opengl.GLES20 9 | import androidx.appcompat.app.AppCompatActivity 10 | import android.os.Bundle 11 | import android.util.Log 12 | import android.view.Menu 13 | import android.view.MenuItem 14 | import android.view.SurfaceHolder 15 | import android.view.SurfaceView 16 | import androidx.annotation.ColorInt 17 | import androidx.core.content.ContextCompat 18 | import androidx.interpolator.view.animation.FastOutSlowInInterpolator 19 | import com.otaliastudios.opengl.core.EglCore 20 | import com.otaliastudios.opengl.draw.* 21 | import com.otaliastudios.opengl.program.GlFlatProgram 22 | import com.otaliastudios.opengl.scene.GlScene 23 | import com.otaliastudios.opengl.surface.EglWindowSurface 24 | import kotlin.math.roundToInt 25 | 26 | class ShapesActivity : AppCompatActivity() { 27 | 28 | private lateinit var surfaceView: SurfaceView 29 | private var eglCore: EglCore? = null 30 | private var eglSurface: EglWindowSurface? = null 31 | private var flatProgram: GlFlatProgram? = null 32 | 33 | private val scene = GlScene() 34 | private val roundRect = GlRoundRect() 35 | private val triangle = GlTriangle() 36 | private val circle = GlCircle() 37 | 38 | private val rectF = RectF() 39 | 40 | private val drawAnimator = ValueAnimator.ofFloat(0F, 1F).also { 41 | it.repeatCount = ValueAnimator.INFINITE 42 | it.repeatMode = ValueAnimator.REVERSE 43 | it.duration = 1200 44 | it.interpolator = FastOutSlowInInterpolator() 45 | it.addUpdateListener { draw() } 46 | } 47 | 48 | override fun onCreate(savedInstanceState: Bundle?) { 49 | super.onCreate(savedInstanceState) 50 | setContentView(R.layout.activity_shapes) 51 | 52 | surfaceView = findViewById(R.id.surface_view) 53 | surfaceView.setZOrderOnTop(true) 54 | surfaceView.holder.setFormat(PixelFormat.RGBA_8888) 55 | surfaceView.holder.addCallback(object : SurfaceHolder.Callback { 56 | override fun surfaceCreated(holder: SurfaceHolder) { 57 | onSurfaceCreated() 58 | } 59 | 60 | override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { 61 | GLES20.glViewport(0, 0, width, height) 62 | scene.setViewportSize(width, height) 63 | } 64 | 65 | override fun surfaceDestroyed(holder: SurfaceHolder) { 66 | onSurfaceDestroyed() 67 | } 68 | }) 69 | } 70 | 71 | private fun onSurfaceCreated() { 72 | Log.e("SHAPES", "CREATED.") 73 | eglCore = EglCore() 74 | eglSurface = EglWindowSurface(eglCore!!, surfaceView.holder.surface!!) 75 | eglSurface!!.makeCurrent() 76 | flatProgram = GlFlatProgram() 77 | drawAnimator.start() 78 | } 79 | 80 | private fun onSurfaceDestroyed() { 81 | Log.e("SHAPES", "DESTROYING.") 82 | drawAnimator.cancel() 83 | flatProgram?.release() 84 | eglSurface?.release() 85 | eglCore?.release() 86 | flatProgram = null 87 | eglSurface = null 88 | eglCore = null 89 | } 90 | 91 | private fun draw() { 92 | Log.w("SHAPES", "drawing.") 93 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT) 94 | 95 | // Animate the background rect 96 | rectF.bottom = floatValue(-0.4F, -1F) 97 | rectF.left = floatValue(-0.4F, -1F) 98 | rectF.top = floatValue(0.4F, 1F) 99 | rectF.right = floatValue(0.4F, 1F) 100 | roundRect.setRect(rectF) 101 | roundRect.setCornersPx(intValue(100, 0)) 102 | // Animate the color 103 | val roundRectStart = ContextCompat.getColor(this, R.color.roundRectStart) 104 | val roundRectEnd = ContextCompat.getColor(this, R.color.roundRectEnd) 105 | flatProgram!!.setColor(colorValue(roundRectStart, roundRectEnd)) 106 | // Draw 107 | scene.draw(flatProgram!!, roundRect) 108 | 109 | // Draw the triangle. 110 | val triangleColor = ContextCompat.getColor(this, R.color.triangle) 111 | flatProgram!!.setColor(triangleColor) 112 | triangle.rotation += 3 113 | triangle.radius = floatValue(0.15F, 0.6F) 114 | scene.draw(flatProgram!!, triangle) 115 | 116 | // Draw the circle. 117 | val circleColor = ContextCompat.getColor(this, R.color.circle) 118 | flatProgram!!.setColor(circleColor) 119 | circle.radius = floatValue(0.15F, 0F) 120 | scene.draw(flatProgram!!, circle) 121 | 122 | // Publish. 123 | eglSurface!!.swapBuffers() 124 | } 125 | 126 | @ColorInt 127 | private fun colorValue(@ColorInt start: Int, @ColorInt end: Int): Int { 128 | return Color.rgb( 129 | intValue(Color.red(start), Color.red(end)), 130 | intValue(Color.green(start), Color.green(end)), 131 | intValue(Color.blue(start), Color.blue(end)) 132 | ) 133 | } 134 | 135 | private fun intValue(start: Int, end: Int): Int { 136 | return floatValue(start.toFloat(), end.toFloat()).roundToInt() 137 | } 138 | 139 | private fun floatValue(start: Float, end: Float): Float { 140 | return start + drawAnimator.animatedFraction * (end - start) 141 | } 142 | 143 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 144 | menuInflater.inflate(R.menu.activity_shapes, menu) 145 | return true 146 | } 147 | 148 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 149 | startActivity(Intent(this, VideoActivity::class.java)) 150 | onSurfaceDestroyed() 151 | finish() 152 | return true 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /demo/src/main/logo-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/logo-web.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_shapes.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/activity_shapes.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/activity_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-anydpi-v26/logo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-hdpi/logo.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/logo_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-hdpi/logo_background.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/logo_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-hdpi/logo_foreground.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-mdpi/logo.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/logo_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-mdpi/logo_background.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/logo_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-mdpi/logo_foreground.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xhdpi/logo.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/logo_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xhdpi/logo_background.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/logo_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xhdpi/logo_foreground.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xxhdpi/logo.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/logo_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xxhdpi/logo_background.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/logo_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xxhdpi/logo_foreground.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xxxhdpi/logo.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/logo_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xxxhdpi/logo_background.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/logo_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/demo/src/main/res/mipmap-xxxhdpi/logo_foreground.png -------------------------------------------------------------------------------- /demo/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #9E113C 4 | #6D0B27 5 | 6 | #E66B29 7 | #2D96BE 8 | 9 | #30569E 10 | #FAB43C 11 | 12 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Egloo 3 | 4 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | _pages 3 | *.sw? 4 | .sass-cache 5 | .jekyll-metadata 6 | Gemfile.lock -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages', group: :jekyll_plugins 3 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Read the docs at https://natario1.github.io/Egloo . 2 | -------------------------------------------------------------------------------- /docs/_about/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Changelog" 4 | order: 3 5 | --- 6 | 7 | New versions are released through GitHub, so the reference page is the [GitHub Releases](https://github.com/natario1/Egloo/releases) page. 8 | 9 | Starting from v0.3.1, you can [support development](https://github.com/sponsors/natario1) through the GitHub Sponsors program. 10 | Companies can share a tiny part of their revenue and get private support hours in return. Thanks! 11 | 12 | ### v0.6.1 13 | 14 | - Upgrade to Kotlin 1.5.20 ([#37][37]) 15 | - Enable snapshot releases ([#37][37]) 16 | 17 | ### v0.6.0 18 | 19 | - Upgrade to Kotlin 1.4.31 ([#33][33]) 20 | - Publish to Maven Central ([#33][33]) 21 | 22 | ### v0.5.4 23 | 24 | - Upgrade to Kotlin 1.4.21 ([#31][31]) 25 | 26 | ### v0.5.3 27 | 28 | - New: Upgrade to Kotlin 1.4 ([#27][27]) 29 | - New: Add a few getters to GlTexture and GlSharedStorageBuffer ([#27][27]) 30 | 31 | 32 | 33 | ### v0.5.2 34 | 35 | - Fix: fixed a bug with the Android/JVM implementation ([#23][23]) 36 | 37 | 38 | 39 | ### v0.5.1 40 | 41 | Native targets are now published to JCenter and can be added as dependencies from Kotlin Multiplatform 42 | projects ([#22][22]). You can add the granular dependencies: 43 | 44 | ```kotlin 45 | implementation("com.otaliastudios.opengl:egloo-android:0.5.1") // android 46 | implementation("com.otaliastudios.opengl:egloo-androidnativex86:0.5.1") // android native 47 | implementation("com.otaliastudios.opengl:egloo-androidnativex64:0.5.1") // android native 48 | implementation("com.otaliastudios.opengl:egloo-androidnativearm32:0.5.1") // android native 49 | implementation("com.otaliastudios.opengl:egloo-androidnativearm64:0.5.1") // android native 50 | ``` 51 | 52 | Or simply add the common dependency for your Kotlin Multiplatform common source set: 53 | 54 | ```kotlin 55 | // This will include the correct artifact into your targets 56 | implementation("com.otaliastudios.opengl:egloo-multiplatform:0.5.1") 57 | ``` 58 | 59 | 60 | 61 | ### v0.5.0 62 | 63 | This release adds support for native targets. We provide an implementation for Android native libraries, 64 | but other targets like iOS can probably be added easily. These artifacts are not currently published 65 | but can be built using `./gradlew :library:publishLocal` ([#20][20]). 66 | 67 | Other changes: 68 | 69 | - New: `EglCore.makeCurrent()` to make the context current with no surfaces ([#18][18]) 70 | - New: `GlBuffer` base class, and `GlShaderStorageBuffer` implementation for SSBOs 71 | - New: `GlShader` abstraction for `GlProgram`s 72 | 73 | 74 | 75 | ### v0.4.0 76 | 77 | - New: `GlTexture` class to create textures ([#14][14]) 78 | - New: `GlFramebuffer` class to create framebuffers ([#14][14]) 79 | - New: `Gl2dMesh` drawable ([#14][14]) 80 | 81 | 82 | 83 | ### v0.3.1 84 | 85 | First versioned release. 86 | 87 | [natario1]: https://github.com/natario1 88 | 89 | [14]: https://github.com/natario1/Egloo/pull/14 90 | [18]: https://github.com/natario1/Egloo/pull/18 91 | [20]: https://github.com/natario1/Egloo/pull/20 92 | [22]: https://github.com/natario1/Egloo/pull/22 93 | [23]: https://github.com/natario1/Egloo/pull/23 94 | [31]: https://github.com/natario1/Egloo/pull/31 95 | [33]: https://github.com/natario1/Egloo/pull/33 96 | [37]: https://github.com/natario1/Egloo/pull/37 97 | -------------------------------------------------------------------------------- /docs/_about/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Getting Started" 4 | description: "Getting started with Egloo" 5 | order: 2 6 | disqus: 1 7 | --- 8 | 9 | Using Egloo is very easy even for those who do not have GLES experience at all. 10 | The example below will create a context, draw a red triangle and release. 11 | 12 | First, configure an EGL context and window: 13 | 14 | ```kotlin 15 | // Configure an EGL context and window 16 | val core = EglCore() 17 | val window = EglWindowSurface(core, outputSurface) 18 | window.makeCurrent() 19 | ``` 20 | 21 | Then draw our triangle: 22 | 23 | ```kotlin 24 | val drawable = GlTriangle() // GlDrawable: what to draw 25 | val program = GlFlatProgram() // GlProgram: how to draw 26 | program.setColor(Color.RED) 27 | program.draw(drawable) 28 | ``` 29 | 30 | Then publish what we have drawn. The `outputSurface` defined above will receive our frame: 31 | 32 | ```kotlin 33 | window.swapBuffers() 34 | ``` 35 | 36 | Finally, release everything: 37 | 38 | ```kotlin 39 | program.release() 40 | window.release() 41 | core.release() 42 | ``` 43 | 44 | Please keep reading the in-depth documentation for all the APIs and features we offer. -------------------------------------------------------------------------------- /docs/_about/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Install" 4 | description: "Integrate in your project" 5 | order: 1 6 | --- 7 | 8 | Egloo is publicly hosted on Maven Central repository, where you 9 | can download the AAR package and other artifacts. To fetch with Gradle, make sure you add the 10 | JCenter repository in your root projects `build.gradle` file: 11 | 12 | ```groovy 13 | allprojects { 14 | repositories { 15 | mavenCentral() 16 | } 17 | } 18 | ``` 19 | 20 | Then simply download the latest version. For regular Android projects users: 21 | 22 | ```kotlin 23 | implementation("com.otaliastudios.opengl:egloo:{{ site.github_version }}") 24 | ``` 25 | 26 | For Kotlin Multiplatform projects: 27 | 28 | ```kotlin 29 | // Add a single dependency into your common Kotlin Multiplatform sourceset. 30 | // This will include the correct artifact for each target that you want to support. 31 | implementation("com.otaliastudios.opengl:egloo-multiplatform:{{ site.github_version }}") 32 | 33 | // Or use granular dependencies: 34 | implementation("com.otaliastudios.opengl:egloo-android:{{ site.github_version }}") // Android AAR 35 | implementation("com.otaliastudios.opengl:egloo-androidnativex86:{{ site.github_version }}") // Android Native KLib 36 | implementation("com.otaliastudios.opengl:egloo-androidnativex64:{{ site.github_version }}") // Android Native KLib 37 | implementation("com.otaliastudios.opengl:egloo-androidnativearm32:{{ site.github_version }}") // Android Native KLib 38 | implementation("com.otaliastudios.opengl:egloo-androidnativearm64:{{ site.github_version }}") // Android Native KLib 39 | ``` 40 | 41 | > The Android version works on API 18+, which is the only requirement and should be met by many projects nowadays. 42 | 43 | ### Snapshots 44 | 45 | We deploy snapshots on each push to the main branch. If you want to use the latest, unreleased features, 46 | you can do so (at your own risk) by adding the snapshot repository: 47 | 48 | ```groovy 49 | allprojects { 50 | repositories { 51 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") 52 | } 53 | } 54 | ``` 55 | 56 | and changing the library version from `{{ site.github_version }}` to `latest-SNAPSHOT`. 57 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Glide: https://github.com/bumptech/glide/blob/gh-pages/_config.yml 2 | # Source repo: https://github.com/bruth/jekyll-docs-template 3 | # Source site: http://bruth.github.io/jekyll-docs-template/ 4 | # Ref guide: https://visualstudiomagazine.com/Articles/2015/03/01/GitHub-Pages.aspx?Page=2 5 | 6 | # Used by us 7 | title: Egloo 8 | color: '#f8f8f8' 9 | description: A simple and lightweight Kotlin multiplatform framework for OpenGL ES drawing and EGL management in Android based on object-oriented components, inspired by Google's Grafika. # used by ourselves and by seo tag. 10 | disqus_shortname: 'egloo-android' 11 | google_analytics_id: 'UA-155077779-3' 12 | google_site_verification: '4x49i17ABIrSvUl52SeL0-t0341aTnWWaC62-FYCRT4' 13 | github: [metadata] # TODO What's this? 14 | github_repo: Egloo 15 | github_version: 0.6.1 16 | github_branch: master 17 | baseurl: '/Egloo' # Keep as an empty string if served up at the root 18 | collections: 19 | about: 20 | name: Overview 21 | output: true 22 | docs: 23 | name: Documentation 24 | output: true 25 | extra: 26 | name: More 27 | output: true 28 | screenshots: 29 | - 'screenshot-1.png' 30 | - 'screenshot-2.png' 31 | - 'screenshot-3.png' 32 | 33 | # Jekyll specific stuff 34 | author: 35 | name: Mattia Iavarone # Should appear in . 36 | email: mat.iavarone@gmail.com 37 | github: natario1 38 | website: https://natario.dev 39 | plugins: 40 | - jekyll-seo-tag # Add SEO tags 41 | permalink: /:categories/:title # Ensure permalinks have no date nor extension 42 | exclude: ['script', 'README.md'] # Exclude non-site files 43 | highlighter: rouge # Syntax highlighting 44 | markdown: kramdown # Use the kramdown Markdown renderer 45 | kramdown: 46 | input: GFM # Use Github Flavored Markdown 47 | -------------------------------------------------------------------------------- /docs/_docs/drawables.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Drawables" 4 | description: "Egloo drawables are the shape to be drawn" 5 | order: 2 6 | disqus: 1 7 | --- 8 | 9 | In the Egloo drawing pipeline, the `GlDrawable` class controls **what to draw**. 10 | In GLES terms, each drawable contains: 11 | 12 | - its vertex array, containing the position of each vertex 13 | - its **model matrix**, to control scale, rotation, translation, and so on 14 | - its `glDrawArrays` logic, for example for using `GL_TRIANGLE_FAN` or `GL_TRIANGLE_STRIP` 15 | 16 | Drawables are very easy to implement. We offer a few implementations: 17 | 18 | |Name|Description| 19 | |----|-----------| 20 | |`Gl2dDrawable`|Base class for 2D drawables, that have 2 coordinates per vertex.| 21 | |`Gl3dDrawable`|Base class for 2D drawables, that have 3 coordinates per vertex.| 22 | |`GlRect`|A 2D drawable made of four vertices. By default, it covers the entire viewport and is typically used for textures.| 23 | |`GlPolygon`|A regular 2D polygon. For example: `pentagon = GlPolygon(5)`.| 24 | |`GlTriangle`|A regular 2D triangle, extending `GlPolygon`.| 25 | |`GlSquare`|A 2D square, extending `GlPolygon`.| 26 | |`GlCircle`|A 2D circle, implemented as a `GlPolygon` with 360 sides.| 27 | |`GlRoundRect`|A 2D rounded rect, with customizable corners.| 28 | 29 | Each drawable can have different methods to customize its appearance and behavior. 30 | -------------------------------------------------------------------------------- /docs/_docs/egl-management.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "EGL Management" 4 | description: "Create and manage the EGL context" 5 | order: 1 6 | disqus: 1 7 | --- 8 | 9 | Creating and managing an EGL context and surface is mandatory to perform GLES drawing and 10 | is very easy with Egloo. Classes that help in this start with the `Egl` prefix. 11 | 12 | ### EGL context 13 | 14 | The first thing to do is creating an EGL context. This can be done through the `EglCore` class: 15 | 16 | ```kotlin 17 | val core = EglCore() 18 | // At the end... 19 | core.release() 20 | ``` 21 | 22 | The core object will configure a GLES 2 or GLES 3 compatible EGL context, based on the presence 23 | of the `EglCore.FLAG_TRY_GLES3` class. When you are done, the core should always be released. 24 | 25 | The core object can also accept a shared context in the constructor, so that the new EGL context 26 | will share data with the old one. 27 | 28 | After creation, `EglCore` can be used to create [EGL surfaces](#egl-surfaces). 29 | 30 | ### EGL surfaces 31 | 32 | Each `EglCore` object can be used to create one or more `EglSurface`, which represent the output 33 | where our GLES rendered data will be drawn. Egloo supports two types of surfaces. 34 | 35 | After usage, all surfaces should be released with `surface.release()`. 36 | 37 | ##### EglWindowSurface 38 | 39 | The `EglWindowSurface` uses a `android.view.Surface` or `SurfaceTexture` as output, two objects that 40 | can be considered system windows in Android. Anything drawn on this window will be passed to the 41 | given `Surface` or `SurfaceTexture`, for display or processing. 42 | 43 | ```kotlin 44 | // Create window and make it the current EGL surface 45 | val window = EglWindowSurface(core, output) 46 | window.makeCurrent() 47 | 48 | // Draw something 49 | // ... 50 | 51 | // Publish drawn content into output 52 | window.swapBuffers() 53 | ``` 54 | 55 | ##### EglOffscreenSurface 56 | 57 | The `EglOffscreenSurface` requires a `width` and a `height` in the constructor and corresponds to 58 | an EGL pixel buffer surface which does not depend on any platform window. 59 | 60 | ```kotlin 61 | // Create pbuffer and make it the current EGL surface 62 | val pbuffer = EglOffscreenSurface(core, 100, 100) 63 | pbuffer.makeCurrent() 64 | 65 | // Draw something 66 | // ... 67 | 68 | // Offscreen surfaces are single buffered, so 69 | // you don't need to swapBuffers() to publish 70 | ``` 71 | 72 | ### GLSurfaceView utilities 73 | 74 | When using the `android.opengl.GLSurfaceView` class, you can use two methods to control the EGL context 75 | initialization. Egloo provides static implementations of these: 76 | 77 | ```kotlin 78 | // For GLES2... 79 | glSurfaceView.setEGLContextFactory(EglContextFactory.GLES2) 80 | glSurfaceView.setEGLConfigChooser(EglContextFactory.GLES2) 81 | 82 | // For GLES3... 83 | glSurfaceView.setEGLContextFactory(EglContextFactory.GLES3) 84 | glSurfaceView.setEGLConfigChooser(EglContextFactory.GLES3) 85 | ``` 86 | -------------------------------------------------------------------------------- /docs/_docs/programs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Programs" 4 | description: "Egloo programs draw inside the drawable shape" 5 | order: 3 6 | disqus: 1 7 | --- 8 | 9 | After you know what to draw through drawables, Egloo needs a `GlProgram` implementation to control 10 | **how to draw** them. In Android terms, you can think of this as the `Paint` object that's used to 11 | draw on canvas. 12 | 13 | In GLES terms, a `GlProgram` is exactly an OpenGL program. It accepts input shaders in the constructor 14 | and manages the program itself. 15 | 16 | A `GlProgram` can be used to draw one or more drawables using the draw method: 17 | 18 | ```kotlin 19 | program.draw(glDrawable1) 20 | program.draw(glDrawable2, mvpMatrix) // Optional 21 | ``` 22 | 23 | If not present, the model-view-projection matrix will be considered equal to the drawable's model 24 | matrix. As with most other components, after usage, all programs should be released: 25 | 26 | ```kotlin 27 | program.release() 28 | ``` 29 | 30 | Egloo offers two base program implementations. 31 | 32 | ### Flat program 33 | 34 | The simplest program is one that draws the `GlDrawable` with a flat color. This program is called 35 | `GlFlatProgram`: 36 | 37 | ```kotlin 38 | val program = GlFlatProgram() 39 | program.setColor(Color.RED) 40 | program.draw(drawable1) 41 | program.setColor(Color.GREEN) 42 | program.draw(drawable2) 43 | ``` 44 | 45 | ### Texture program 46 | 47 | The `GlTextureProgram` program can be used to render textures. To use it, you will need to create 48 | a `GlTexture` first and call `program.texture = texture`: this will make sure that texture is 49 | correctly bound before rendering. See [textures](textures) to learn about this object. 50 | 51 | The texture program has built-in support for: 52 | - Adapting the texture to the `GlDrawable` it is being drawn into. This means that the drawable and the texture should have the same aspect ratio to avoid distortion. 53 | - Apply a matrix transformation to the texture by modifying `GlTextureProgram.textureTransform` 54 | 55 | See the sample below: 56 | 57 | ```kotlin 58 | val texture = GlTexture() 59 | val program = GlTextureProgram() 60 | program.texture = texture 61 | val surfaceTexture = SurfaceTexture(texture.id) 62 | 63 | // Pass this surfaceTexture to Camera, for example 64 | val camera: android.hardware.Camera = openCamera() 65 | camera.setPreviewTexture(surfaceTexture) 66 | 67 | // Now the program texture receives the camera frames 68 | // And we can render them using the program 69 | val rect = GlRect() // Draw the full frame 70 | surfaceTexture.getTransformMatrix(program.textureTransform) 71 | program.draw(rect) 72 | ``` 73 | 74 | If, for some reason, you do not want to call `program.texture = texture` (which gives the program 75 | the ownership of the texture), you can still call `texture.bind()` and `texture.unbind()` manually: 76 | 77 | ```kotlin 78 | // Option 1 79 | texture.bind() 80 | program.draw(drawable) 81 | texture.unbind() 82 | 83 | // Option 2 84 | texture.use { 85 | program.draw(drawable) 86 | } 87 | 88 | // Option 3 89 | program.texture = texture 90 | program.draw(drawable) 91 | ``` 92 | 93 | These options are equivalent. Note, however, that when passing the texture to `GlTextureProgram`, 94 | the texture will automatically be released when you call `program.release()`. -------------------------------------------------------------------------------- /docs/_docs/scenes.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Scenes" 4 | description: "How to control the view and projection matrix" 5 | order: 5 6 | disqus: 1 7 | --- 8 | 9 | When drawing different drawables, using different programs or different textures that should have 10 | common (or separate) view and projection matrices, it can be useful to use a `GlScene`. 11 | 12 | The `GlScene` object contains and holds: 13 | 14 | - the view matrix 15 | - the projection matrix 16 | 17 | The scene can combine these with each drawable's model matrix, to create the famous model-view-projection 18 | matrix. In GLES terms, you can think of a scene as a simple matrix holder. 19 | 20 | When using scenes, drawing should be performed through the `GlScene` itself: 21 | 22 | ```kotlin 23 | val scene = GlScene() 24 | 25 | // Set common view and projection matrix in the scene object. 26 | setProjection(scene.projectionMatrix) 27 | setView(scene.viewMatrix) 28 | 29 | // Draw with common parameters 30 | scene.draw(program, drawable1) 31 | scene.draw(program, drawable2) 32 | scene.draw(program, drawable3) 33 | ``` -------------------------------------------------------------------------------- /docs/_docs/textures.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Textures" 4 | description: "APIs for textures and framebuffer objects" 5 | order: 4 6 | disqus: 1 7 | --- 8 | 9 | ### The GlTexture object 10 | 11 | The `GlTexture` object will generate and allocate an OpenGL texture. The texture can then be used to 12 | read from it, render into it, attach to a framebuffer object and much more. 13 | 14 | By default, the `GlTexture` is created with the `GLES11Ext.GL_TEXTURE_EXTERNAL_OES` texture target. 15 | This means that it is suitable for using it as the output of a `SurfaceTexture`: 16 | 17 | ```kotlin 18 | val texture = GlTexture() 19 | val surfaceTexture = SurfaceTexture(texture.id) 20 | // Anytime the surface texture is passed new data, its contents are put into our GlTexture 21 | // For example, we can receive the stream of video frames: 22 | videoPlayer.setOutputSurface(surfaceTexture) 23 | videoPlayer.play() 24 | ``` 25 | 26 | However, different targets can be specified within the texture constructor. When using `GLES20.GL_TEXTURE_2D`, 27 | you will probably want to use the constructor that accepts a `width` and `height` so that the buffer 28 | is actually allocated. 29 | 30 | > To render texture contents, just use a `GlTextureProgram` and pass the texture to it. 31 | See the [programs](programs#texture-program) page for details. 32 | 33 | ### The GlFramebuffer object 34 | 35 | The `GlFramebuffer` object will generate an OpenGL framebuffer object. 36 | You can attach textures to it by using `GlFramebuffer.attach()`, like so: 37 | 38 | ```kotlin 39 | val texture = GlTexture() 40 | val fbo = GlFramebuffer() 41 | fbo.attach(texture, GLES20.GL_COLOR_ATTACHMENT0) 42 | ``` 43 | 44 | The attached textures will now receive the framebuffer contents. -------------------------------------------------------------------------------- /docs/_extra/contact.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Contact" 4 | order: 3 5 | --- 6 | 7 | This library is maintained by [Mattia Iavarone](https://github.com/natario1) (@natario1). 8 | Feel free to contact me privately by sending an email, 9 | for support, consulting, or have any other business-related question. 10 | 11 | To report issues, please use the [project GitHub page](https://github.com/natario1/Egloo). 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/_extra/contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Contributing & License" 4 | order: 1 5 | --- 6 | 7 | Everyone is welcome to contribute with suggestions or pull requests, as the library is under active development, 8 | although it has reached a high level of stability. 9 | 10 | We are grateful to anyone who has contributed with fixes, features or feature requests. If you don't 11 | want to get involved but still want to support the project, please [consider donating](donate). 12 | 13 | ### Bug reports 14 | 15 | Please make sure to fill the bug report issue template on GitHub. 16 | We highly recommend to try to reproduce the bug in the demo app, as this helps a lot in debugging 17 | and excludes programming errors from your side. 18 | 19 | Make sure to include: 20 | 21 | - A clear and concise description of what the bug is 22 | - Egloo version, device type, Android API level 23 | - Exact steps to reproduce the issue 24 | - Description of the expected behavior 25 | 26 | Recommended extras: 27 | 28 | - Screenshots 29 | - LogCat logs 30 | - Link to a GitHub repo where the bug is reproducible 31 | 32 | ### Pull Requests 33 | 34 | Please open an issue first. 35 | 36 | Unless your PR is a simple fix (typos, documentation, bugs with obvious solution), opening an issue 37 | will let us discuss the problem, take design decisions and have a reference to the issue description. 38 | 39 | ### License 40 | 41 | Egloo is licensed under the [MIT](https://github.com/natario1/Egloo/blob/master/LICENSE) license. -------------------------------------------------------------------------------- /docs/_extra/donate.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Support" 4 | order: 2 5 | --- 6 | 7 | Egloo is maintained and, for the most part, developed by Mattia Iavarone ([contact me!](contact)). 8 | If you like the project, use it with profit, or simply want to thank back, please consider 9 | [sponsoring me](https://github.com/sponsors/natario1) through the GitHub Sponsors program! 10 | 11 | I offer private support hours through the sponsorship program and I'm open to help your 12 | company with features that are not currently supported by the open source project. 13 | 14 | Thank you for any contribution! 15 | 16 | -------------------------------------------------------------------------------- /docs/_includes/disqus.html: -------------------------------------------------------------------------------- 1 |
2 | 12 | 13 | -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /docs/_includes/google_analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /docs/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% seo %} 18 | 19 | {% if site.google_analytics_id != "" %} 20 | {% include google_analytics.html %} 21 | {% endif %} -------------------------------------------------------------------------------- /docs/_includes/header.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | {{ site.title }} 5 | 6 | 7 |
8 | Menu toggle 12 |
13 | 14 | 15 |
16 | latest: v{{ site.github_version }} 17 | 18 | GitHub 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /docs/_includes/navigation.html: -------------------------------------------------------------------------------- 1 | 18 | 28 | -------------------------------------------------------------------------------- /docs/_layouts/landing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | {{ site.title }} 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

{{ site.title }}

14 | {{ content }} 15 |
16 |
17 | Documentation 18 | Changelog 19 | GitHub 20 | Support 21 |
22 |
23 |
24 | {% assign col = 12 | divided_by: site.screenshots.size %} 25 | {% for screenshot in site.screenshots %} 26 |
27 | Screenshot 28 |
29 | {% endfor %} 30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/_layouts/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | {{ site.title }}{% if page.title %} | {{ page.title }}{% endif %} 6 | 7 | 8 | 9 | 10 | {% include header.html %} 11 |
12 |
13 |
14 | {% include navigation.html %} 15 |
16 |
17 |
18 | {{ content }} 19 |
20 | {% if page.disqus == 1 %} 21 |
22 | {% include disqus.html %} 23 |
24 | {% endif %} 25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 | {% include footer.html %} 33 |
34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /docs/_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: main 3 | --- 4 | 5 | 10 |
11 | {{ content }} 12 |
13 | -------------------------------------------------------------------------------- /docs/css/colors.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-primary: #9E113C; 3 | --color-primary-active: #8E112C; 4 | --color-primary-hover: #7E011C; 5 | --color-secondary: #3D0913; 6 | --color-accent: #0e95e3; 7 | --color-accent-light: #f5fcff; 8 | --color-accent-dark: #0e3375; 9 | --color-background: #FFFFFF; 10 | --color-code: var(--color-primary); 11 | --color-code-background: #f8f8f8; 12 | --color-text-muted: #6c757d; 13 | } 14 | 15 | body { 16 | background-color: var(--color-background); 17 | } 18 | 19 | a { 20 | color: var(--color-primary); 21 | } 22 | 23 | code, pre { 24 | background-color: var(--color-code-background); 25 | } 26 | 27 | :not(pre) > code { 28 | color: var(--color-code); 29 | } 30 | 31 | a:hover { 32 | color: var(--color-primary-hover) !important; 33 | } 34 | 35 | .btn-primary { 36 | background-color: var(--color-primary) !important; 37 | border-color: var(--color-primary) !important; 38 | color: white !important; 39 | } 40 | 41 | .btn-primary.active, .btn-primary:active { 42 | background-color: var(--color-primary-active) !important; 43 | border-color: var(--color-primary-active) !important; 44 | } 45 | 46 | .btn-primary:hover { 47 | background-color: var(--color-primary-hover) !important; 48 | border-color: var(--color-primary-hover) !important; 49 | color: white !important; 50 | } 51 | 52 | .btn-outline-primary { 53 | border-color: var(--color-primary) !important; 54 | } 55 | 56 | .btn-outline-primary.active, .btn-outline-primary:active { 57 | background-color: var(--color-primary-active) !important; 58 | border-color: var(--color-primary-active) !important; 59 | } 60 | 61 | .btn-outline-primary:hover { 62 | border-color: var(--color-primary-hover) !important; 63 | background-color: var(--color-primary-hover) !important; 64 | color: white !important; 65 | } 66 | -------------------------------------------------------------------------------- /docs/css/fonts.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lobster+Two:400i,700i|Roboto+Mono|Source+Sans+Pro:400,700&display=swap'); 2 | @import "fonts_responsive.css"; 3 | 4 | :root { 5 | --font-mono: 'Roboto Mono'; 6 | --font-sans: 'Source Sans Pro'; 7 | --font-display: 'Lobster Two'; 8 | } 9 | 10 | * { 11 | font-family: var(--font-sans), sans-serif; 12 | font-weight: 400; 13 | } 14 | 15 | h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { 16 | font-family: var(--font-display), cursive; 17 | font-style: italic; 18 | font-weight: 700 !important; 19 | } 20 | 21 | h4, .h4, h5, .h5, h6, .h6 { 22 | font-weight: 400; 23 | } 24 | 25 | button, .btn { 26 | font-family: var(--font-display), cursive !important; 27 | font-style: italic !important; 28 | font-weight: 700 !important; 29 | } 30 | 31 | code, code * { 32 | font-family: var(--font-mono) !important; 33 | } -------------------------------------------------------------------------------- /docs/css/fonts_responsive.css: -------------------------------------------------------------------------------- 1 | /* https://christianoliff.com/blog/bootstrap-with-rfs */ 2 | /* either apply after everything else or add !important here */ 3 | @media (max-width: 1200px) { 4 | legend { 5 | font-size: calc(1.275rem + 0.3vw); 6 | } 7 | h1, .h1 { 8 | font-size: calc(1.375rem + 1.5vw); 9 | } 10 | h2, .h2 { 11 | font-size: calc(1.325rem + 0.9vw); 12 | } 13 | h3, .h3 { 14 | font-size: calc(1.3rem + 0.6vw); 15 | } 16 | h4, .h4 { 17 | font-size: calc(1.275rem + 0.3vw); 18 | } 19 | .display-1 { 20 | font-size: calc(1.725rem + 5.7vw); 21 | } 22 | .display-2 { 23 | font-size: calc(1.675rem + 5.1vw); 24 | } 25 | .display-3 { 26 | font-size: calc(1.575rem + 3.9vw); 27 | } 28 | .display-4 { 29 | font-size: calc(1.475rem + 2.7vw); 30 | } 31 | .close { 32 | font-size: calc(1.275rem + 0.3vw); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/css/landing.css: -------------------------------------------------------------------------------- 1 | @import "fonts.css"; 2 | @import "colors.css"; 3 | 4 | :root { 5 | --color-gradient-1: var(--color-secondary); 6 | --color-gradient-2: black; 7 | } 8 | 9 | html { 10 | width: 100%; 11 | height: 100%; 12 | margin: 0; 13 | } 14 | 15 | body { 16 | background: radial-gradient(ellipse, var(--color-gradient-1), var(--color-gradient-2)) fixed !important; 17 | } 18 | 19 | #logo { 20 | width: 45%; 21 | max-width: 340px; 22 | } 23 | 24 | h1 { 25 | color: white; 26 | } 27 | 28 | p { 29 | color: rgba(255, 255, 255, 0.7); 30 | font-size: 1.2em; 31 | line-height: 100%; 32 | } 33 | 34 | .btn { 35 | color: white !important; 36 | background-color: rgba(240, 240, 240, 0.25); 37 | font-size: 1.3em; 38 | } 39 | 40 | .btn:hover { 41 | color: white !important; 42 | background-color: rgba(240, 240, 240, 0.4); 43 | } -------------------------------------------------------------------------------- /docs/css/syntax.css: -------------------------------------------------------------------------------- 1 | /* https://github.com/richleland/pygments-css/ */ 2 | @import "colors.css"; 3 | :root { 4 | --syntax-muted: #999999; 5 | --syntax-annotations: #a49848; 6 | --syntax-keyword: #007020; 7 | --syntax-operators: #606060; 8 | --syntax-numbers: var(--syntax-keyword); 9 | --syntax-xml-tags: var(--syntax-keyword); 10 | } 11 | 12 | .highlight .c { color: var(--syntax-muted); font-style: italic } /* Comment */ 13 | .highlight .ch { color: var(--syntax-muted); font-style: italic } /* Comment.Hashbang */ 14 | .highlight .cm { color: var(--syntax-muted); font-style: italic } /* Comment.Multiline */ 15 | .highlight .cp { color: var(--syntax-muted); } /* Comment.Preproc */ 16 | .highlight .cpf { color: var(--syntax-muted); font-style: italic } /* Comment.PreprocFile */ 17 | .highlight .c1 { color: var(--syntax-muted); font-style: italic } /* Comment.Single */ 18 | .highlight .cs { color: var(--syntax-muted); background-color: #fff0f0 } /* Comment.Special */ 19 | 20 | .highlight .nt { color: var(--syntax-xml-tags); font-weight: bold } /* Name.Tag */ 21 | .highlight .na { color: inherit; /* var(--color-accent) */ } /* Name.Attribute */ 22 | .highlight .nf { color: inherit; /* var(--color-accent) */ } /* Name.Function */ 23 | 24 | .highlight .mb { color: var(--syntax-numbers) } /* Literal.Number.Bin */ 25 | .highlight .mf { color: var(--syntax-numbers) } /* Literal.Number.Float */ 26 | .highlight .mh { color: var(--syntax-numbers) } /* Literal.Number.Hex */ 27 | .highlight .mi { color: var(--syntax-numbers) } /* Literal.Number.Integer */ 28 | .highlight .mo { color: var(--syntax-numbers) } /* Literal.Number.Oct */ 29 | 30 | .highlight .nd { color: var(--syntax-annotations); } /* Name.Decorator */ 31 | 32 | .highlight .k { color: var(--syntax-keyword); font-weight: bold } /* Keyword */ 33 | .highlight .kd { color: var(--syntax-keyword); font-weight: bold } /* Keyword.Declaration */ 34 | .highlight .kt { color: var(--syntax-keyword); font-weight: bold } /* Keyword.Type */ 35 | .highlight .kc { color: var(--syntax-keyword); font-weight: bold } /* Keyword.Constant */ 36 | .highlight .kn { color: var(--syntax-keyword); font-weight: bold } /* Keyword.Namespace */ 37 | .highlight .kp { color: var(--syntax-keyword); font-weight: bold } /* Keyword.Pseudo */ 38 | .highlight .kr { color: var(--syntax-keyword); font-weight: bold } /* Keyword.Reserved */ 39 | 40 | .highlight .s { color: var(--color-accent-dark); background-color: var(--color-accent-light); } /* Literal.String */ 41 | .highlight .s1 { color: var(--color-accent-dark); background-color: var(--color-accent-light); } /* Literal.String.Single */ 42 | .highlight .sa { color: var(--color-accent-dark) } /* Literal.String.Affix */ 43 | .highlight .sb { color: var(--color-accent-dark) } /* Literal.String.Backtick */ 44 | .highlight .sc { color: var(--color-accent-dark) } /* Literal.String.Char */ 45 | .highlight .dl { color: var(--color-accent-dark) } /* Literal.String.Delimiter */ 46 | .highlight .sd { color: var(--color-accent-dark); font-style: italic } /* Literal.String.Doc */ 47 | .highlight .s2 { color: var(--color-accent-dark) } /* Literal.String.Double */ 48 | .highlight .se { color: var(--color-accent-dark); font-weight: bold } /* Literal.String.Escape */ 49 | .highlight .sh { color: var(--color-accent-dark) } /* Literal.String.Heredoc */ 50 | .highlight .si { color: var(--color-accent-dark); font-style: italic } /* Literal.String.Interpol */ 51 | .highlight .sx { color: var(--color-accent-dark) } /* Literal.String.Other */ 52 | .highlight .sr { color: var(--color-accent-dark) } /* Literal.String.Regex */ 53 | .highlight .ss { color: var(--color-accent-dark) } /* Literal.String.Symbol */ 54 | 55 | .highlight .o { color: var(--syntax-operators) } /* Operator */ 56 | 57 | .highlight .hll { background-color: #ffffcc } 58 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 59 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 60 | .highlight .ge { font-style: italic } /* Generic.Emph */ 61 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 62 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 63 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 64 | .highlight .go { color: #888888 } /* Generic.Output */ 65 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 66 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 67 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 68 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 69 | .highlight .m { color: #40a070 } /* Literal.Number */ 70 | .highlight .nb { color: #007020 } /* Name.Builtin */ 71 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 72 | .highlight .no { color: #60add5 } /* Name.Constant */ 73 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 74 | .highlight .ne { color: #007020 } /* Name.Exception */ 75 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 76 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 77 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 78 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 79 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 80 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 81 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 82 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 83 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 84 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 85 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 86 | .highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/home.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: main 3 | title: "Egloo" 4 | --- 5 | 6 | # Egloo 7 | 8 | Egloo is a simple and lightweight multiplatform framework for OpenGL ES drawing and EGL management 9 | that uses object-oriented components - hence the name Egl**oo**. It can serve as a basis for 10 | complex drawing operations, but is mostly designed for helping in making common tasks simpler, 11 | even for people that do not have any OpenGL experience. 12 | 13 | Approaching OpenGL from high-level languages can be hard because of the deep differences in the OpenGL API design 14 | with respect to a typical object-oriented context. Egloo tries to take some of these difficulties away 15 | by creating a **thin**, flexible layer of abstraction around EGL and GLES calls. 16 | 17 | You can take a look at the demo app or see Egloo in action in some popular Android projects: 18 | 19 | - for camera preview and real-time filters: see [CameraView](https://github.com/natario1/CameraView) 20 | - in a zoomable Surface: see [ZoomLayout](https://github.com/natario1/ZoomLayout) 21 | - for transcoding videos: see [Transcoder](https://github.com/natario1/Transcoder) 22 | 23 |

24 | 25 |

26 | 27 | ### Features 28 | 29 | - EGL setup and management [[docs]](docs/egl-management) 30 | - GLSurfaceView utilities [[docs]](docs/egl-management#glsurfaceview-utilities) 31 | - Drawables abstraction [[docs]](docs/drawables) 32 | - Programs abstraction [[docs]](docs/programs) 33 | - Scenes to hold view and projection matrix [[docs]](docs/scenes) 34 | 35 | > Starting from 0.5.0, Egloo can run on native targets. We provide an implementation for Android native libraries, 36 | but other targets like iOS can probably be added easily. See [install info](about/install). 37 | 38 | ### Get started 39 | 40 | Get started with [install info](about/install), [quick setup](about/getting-started), or 41 | start reading the in-depth [documentation](docs/egl-management). 42 | 43 | ### Support 44 | 45 | If you like the project, use it with profit, and want to thank back, please consider [donating or 46 | becoming a supporter](extra/donate). 47 | 48 | -------------------------------------------------------------------------------- /docs/icons/github.svg: -------------------------------------------------------------------------------- 1 | 5 | 10 | -------------------------------------------------------------------------------- /docs/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: landing 3 | title: "Egloo" 4 | --- 5 | 6 | A simple and lightweight multiplatform framework for OpenGL ES and EGL management 7 | based on object-oriented components, written in Kotlin and inspired by Google's Grafika. -------------------------------------------------------------------------------- /docs/script/launch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Run a local instance of the site. 4 | bundle exec jekyll serve 5 | -------------------------------------------------------------------------------- /docs/static/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/docs/static/banner.png -------------------------------------------------------------------------------- /docs/static/icon_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/docs/static/icon_foreground.png -------------------------------------------------------------------------------- /docs/static/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/docs/static/screenshot-1.png -------------------------------------------------------------------------------- /docs/static/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/docs/static/screenshot-2.png -------------------------------------------------------------------------------- /docs/static/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/docs/static/screenshot-3.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 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 | org.gradle.jvmargs=-Xmx4096M 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | 21 | android.enableJetifier=true 22 | android.useAndroidX=true 23 | 24 | # https://kotlinlang.org/docs/reference/mpp-share-on-platforms.html#share-code-on-similar-platforms 25 | # https://kotlinlang.slack.com/archives/C0KLZSCHF/p1591547498046000 26 | kotlin.mpp.enableGranularSourceSetsMetadata=true 27 | kotlin.native.enableDependencyPropagation=false 28 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natario1/Egloo/2c13ab28cad9ad143f23d488595d74b020ef78ef/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jun 08 12:40:11 IST 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-6.8-all.zip 7 | -------------------------------------------------------------------------------- /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 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /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 /Users/yuya.tanaka/devel/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/src/androidJvmMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/core/EglConfigChooser.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | import android.opengl.EGL14 4 | import android.opengl.GLSurfaceView 5 | import com.otaliastudios.opengl.internal.EglDisplay 6 | import javax.microedition.khronos.egl.EGL10 7 | import javax.microedition.khronos.egl.EGLConfig 8 | import javax.microedition.khronos.egl.EGLDisplay 9 | 10 | 11 | /** 12 | * Helper for [GLSurfaceView.setEGLConfigChooser], plus 13 | * some handy methods for configs. 14 | */ 15 | public object EglConfigChooser : EglNativeConfigChooser() { 16 | @Suppress("unused") 17 | @JvmField 18 | public val GLES2: GLSurfaceView.EGLConfigChooser = Chooser(2) 19 | 20 | @Suppress("unused") 21 | @JvmField 22 | public val GLES3: GLSurfaceView.EGLConfigChooser = Chooser(3) 23 | 24 | /** 25 | * Finds a suitable EGLConfig by querying [EGL14]. 26 | */ 27 | @Suppress("unused") 28 | @JvmStatic 29 | public fun getConfig(display: android.opengl.EGLDisplay, version: Int, recordable: Boolean): android.opengl.EGLConfig? { 30 | return super.getConfig(EglDisplay(display), version, recordable)?.native 31 | } 32 | 33 | private class Chooser(private val version: Int) : GLSurfaceView.EGLConfigChooser { 34 | 35 | // https://github.com/MasayukiSuda/ExoPlayerFilter/blob/master/epf/src/main/java/com/daasuu/epf/chooser/EConfigChooser.java 36 | override fun chooseConfig(egl: EGL10, display: EGLDisplay): EGLConfig { 37 | val configSizeArray = IntArray(1) 38 | val configSpec = getConfigSpec(version, true) 39 | if (!egl.eglChooseConfig(display, configSpec, null, 0, configSizeArray)) { 40 | throw IllegalArgumentException("eglChooseConfig failed") 41 | } 42 | val configSize = configSizeArray[0] 43 | if (configSize <= 0) throw IllegalArgumentException("No configs match configSpec") 44 | 45 | val configs = arrayOfNulls(configSize) 46 | if (!egl.eglChooseConfig(display, configSpec, configs, configSize, configSizeArray)) { 47 | throw IllegalArgumentException("eglChooseConfig#2 failed") 48 | } 49 | return chooseConfig(egl, display, configs.filterNotNull().toTypedArray()) 50 | ?: throw IllegalArgumentException("No config chosen") 51 | } 52 | 53 | 54 | // https://github.com/MasayukiSuda/ExoPlayerFilter/blob/master/epf/src/main/java/com/daasuu/epf/chooser/EConfigChooser.java 55 | private fun chooseConfig(egl: EGL10, display: EGLDisplay, configs: Array): EGLConfig? { 56 | for (config in configs) { 57 | val d = egl.findConfigAttrib(display, config, EGL10.EGL_DEPTH_SIZE, 0) 58 | val s = egl.findConfigAttrib(display, config, EGL10.EGL_STENCIL_SIZE, 0) 59 | if (d >= 0 && s >= 0) { 60 | val r = egl.findConfigAttrib(display, config, EGL10.EGL_RED_SIZE, 0) 61 | val g = egl.findConfigAttrib(display, config, EGL10.EGL_GREEN_SIZE, 0) 62 | val b = egl.findConfigAttrib(display, config, EGL10.EGL_BLUE_SIZE, 0) 63 | val a = egl.findConfigAttrib(display, config, EGL10.EGL_ALPHA_SIZE, 0) 64 | if (r == 8 && g == 8 && b == 8 && a == 8) { 65 | return config 66 | } 67 | } 68 | } 69 | return null 70 | } 71 | 72 | private fun EGL10.findConfigAttrib(display: EGLDisplay, config: EGLConfig, attribute: Int, defaultValue: Int): Int { 73 | val value = IntArray(1) 74 | return if (eglGetConfigAttrib(display, config, attribute, value)) { 75 | value[0] 76 | } else defaultValue 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/core/EglContextFactory.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | import android.opengl.EGL14 4 | import android.opengl.GLSurfaceView 5 | import android.util.Log 6 | import javax.microedition.khronos.egl.EGL10 7 | import javax.microedition.khronos.egl.EGLConfig 8 | import javax.microedition.khronos.egl.EGLContext 9 | import javax.microedition.khronos.egl.EGLDisplay 10 | 11 | /** 12 | * Helper for [GLSurfaceView.setEGLContextFactory]. 13 | */ 14 | @Suppress("unused") 15 | public object EglContextFactory { 16 | private val TAG = EglContextFactory::class.java.simpleName 17 | 18 | @Suppress("unused") 19 | @JvmField 20 | public val GLES2: GLSurfaceView.EGLContextFactory = Factory(2) 21 | 22 | @Suppress("unused") 23 | @JvmField 24 | public val GLES3: GLSurfaceView.EGLContextFactory = Factory(3) 25 | 26 | private class Factory(private val version: Int) : GLSurfaceView.EGLContextFactory { 27 | override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: EGLConfig): EGLContext { 28 | val attributes = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE) 29 | return egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attributes) 30 | } 31 | 32 | override fun destroyContext(egl: EGL10, display: EGLDisplay, context: EGLContext) { 33 | if (!egl.eglDestroyContext(display, context)) { 34 | Log.e(TAG, "display:$display context:$context") 35 | throw RuntimeException("eglDestroyContex" + egl.eglGetError()) 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/core/EglCore.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | import android.opengl.* 4 | import com.otaliastudios.opengl.internal.EglContext 5 | 6 | /** 7 | * Core EGL state (display, context, config). 8 | * The EGLContext must only be attached to one thread at a time. 9 | * This class is not thread-safe. 10 | * 11 | * @param sharedContext The context to share, or null if sharing is not desired. 12 | * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE. 13 | */ 14 | public actual class EglCore @JvmOverloads constructor( 15 | sharedContext: EGLContext? = EGL14.EGL_NO_CONTEXT, 16 | flags: Int = 0 17 | ) : EglNativeCore(EglContext(sharedContext), flags) { 18 | 19 | /** 20 | * Discards all resources held by this class, notably the EGL context. This must be 21 | * called from the thread where the context was created. 22 | * On completion, no context will be current. 23 | */ 24 | public override fun release() { 25 | super.release() 26 | } 27 | 28 | /** 29 | * Makes this context current, with no read / write surfaces. 30 | */ 31 | public override fun makeCurrent() { 32 | super.makeCurrent() 33 | } 34 | 35 | // Kotlin has no finalize, but simply declaring it works, 36 | // as stated in official documentation. 37 | protected fun finalize() { 38 | release() 39 | } 40 | 41 | @Suppress("unused") 42 | public companion object { 43 | 44 | /** 45 | * Constructor flag: surface must be recordable. This discourages EGL from using a 46 | * pixel format that cannot be converted efficiently to something usable by the video 47 | * encoder. 48 | */ 49 | public const val FLAG_RECORDABLE: Int = EglNativeCore.FLAG_RECORDABLE 50 | 51 | /** 52 | * Constructor flag: ask for GLES3, fall back to GLES2 if not available. Without this 53 | * flag, GLES2 is used. 54 | */ 55 | public const val FLAG_TRY_GLES3: Int = EglNativeCore.FLAG_TRY_GLES3 56 | } 57 | } -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/geometry/PointF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public actual typealias PointF = android.graphics.PointF -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/geometry/RectF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public actual typealias RectF = android.graphics.RectF -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/internal/annotations.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.internal 2 | 3 | import androidx.annotation.RequiresApi 4 | 5 | public actual typealias AndroidJvmRequiresApi = RequiresApi -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/internal/egl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package com.otaliastudios.opengl.internal 4 | 5 | import android.opengl.* 6 | 7 | // Not using the native types directly (as actual typealias) for visibility - android.opengl.* stuff 8 | // is not internal, so we would not be able to make our typealiases internal as well. 9 | // Use data class to have equals(). 10 | internal actual data class EglSurface(val native: EGLSurface?) 11 | internal actual data class EglDisplay(val native: EGLDisplay?) 12 | internal actual data class EglContext(val native: EGLContext?) 13 | internal actual data class EglConfig(val native: EGLConfig) 14 | 15 | internal actual val EGL_NO_CONTEXT = EglContext(EGL14.EGL_NO_CONTEXT) 16 | internal actual val EGL_NO_DISPLAY = EglDisplay(EGL14.EGL_NO_DISPLAY) 17 | internal actual val EGL_NO_SURFACE = EglSurface(EGL14.EGL_NO_SURFACE) 18 | internal actual val EGL_SUCCESS = EGL14.EGL_SUCCESS 19 | internal actual val EGL_NONE = EGL14.EGL_NONE 20 | internal actual val EGL_WIDTH = EGL14.EGL_WIDTH 21 | internal actual val EGL_HEIGHT = EGL14.EGL_HEIGHT 22 | internal actual val EGL_READ = EGL14.EGL_READ 23 | internal actual val EGL_DRAW = EGL14.EGL_DRAW 24 | internal actual val EGL_CONTEXT_CLIENT_VERSION = EGL14.EGL_CONTEXT_CLIENT_VERSION 25 | internal actual val EGL_OPENGL_ES2_BIT = EGL14.EGL_OPENGL_ES2_BIT 26 | internal actual val EGL_OPENGL_ES3_BIT_KHR = EGLExt.EGL_OPENGL_ES3_BIT_KHR 27 | internal actual val EGL_RED_SIZE = EGL14.EGL_RED_SIZE 28 | internal actual val EGL_GREEN_SIZE = EGL14.EGL_GREEN_SIZE 29 | internal actual val EGL_BLUE_SIZE = EGL14.EGL_BLUE_SIZE 30 | internal actual val EGL_ALPHA_SIZE = EGL14.EGL_ALPHA_SIZE 31 | internal actual val EGL_SURFACE_TYPE = EGL14.EGL_SURFACE_TYPE 32 | internal actual val EGL_WINDOW_BIT = EGL14.EGL_WINDOW_BIT 33 | internal actual val EGL_PBUFFER_BIT = EGL14.EGL_PBUFFER_BIT 34 | internal actual val EGL_RENDERABLE_TYPE = EGL14.EGL_RENDERABLE_TYPE 35 | 36 | internal actual inline fun eglChooseConfig(display: EglDisplay, attributes: IntArray, configs: Array, configsSize: Int, numConfigs: IntArray): Boolean { 37 | val nativeConfigs = arrayOfNulls(configs.size) 38 | val result = EGL14.eglChooseConfig(display.native, attributes, 0, nativeConfigs, 0, configsSize, numConfigs, 0) 39 | if (result) configs.indices.forEach { configs[it] = nativeConfigs[it]?.let { EglConfig(it) } } 40 | return result 41 | } 42 | internal actual inline fun eglInitialize(display: EglDisplay, major: IntArray, minor: IntArray) 43 | = EGL14.eglInitialize(display.native, major, 0, minor, 0) 44 | internal actual inline fun eglCreateContext(display: EglDisplay, config: EglConfig, sharedContext: EglContext, attributes: IntArray) 45 | = EglContext(EGL14.eglCreateContext(display.native, config.native, sharedContext.native, attributes, 0)) 46 | internal actual inline fun eglGetDefaultDisplay() = EglDisplay(EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) 47 | internal actual inline fun eglGetCurrentContext() = EglContext(EGL14.eglGetCurrentContext()) 48 | internal actual inline fun eglGetCurrentDisplay() = EglDisplay(EGL14.eglGetCurrentDisplay()) 49 | internal actual inline fun eglGetCurrentSurface(which: Int) = EglSurface(EGL14.eglGetCurrentSurface(which)) 50 | internal actual inline fun eglQuerySurface(display: EglDisplay, surface: EglSurface, attribute: Int, out: IntArray) 51 | = EGL14.eglQuerySurface(display.native, surface.native, attribute, out, 0) 52 | internal actual inline fun eglCreateWindowSurface(display: EglDisplay, config: EglConfig, surface: Any, attributes: IntArray) 53 | = com.otaliastudios.opengl.internal.EglSurface(EGL14.eglCreateWindowSurface(display.native, config.native, surface, attributes, 0)) 54 | internal actual inline fun eglCreatePbufferSurface(display: EglDisplay, config: EglConfig, attributes: IntArray) 55 | = com.otaliastudios.opengl.internal.EglSurface(EGL14.eglCreatePbufferSurface(display.native, config.native, attributes, 0)) 56 | internal actual inline fun eglMakeCurrent(display: EglDisplay, draw: EglSurface, read: EglSurface, context: EglContext) 57 | = EGL14.eglMakeCurrent(display.native, draw.native, read.native, context.native) 58 | internal actual inline fun eglSwapBuffers(display: EglDisplay, surface: EglSurface) 59 | = EGL14.eglSwapBuffers(display.native, surface.native) 60 | internal actual inline fun eglPresentationTime(display: EglDisplay, surface: EglSurface, nanoseconds: Long) 61 | = EGLExt.eglPresentationTimeANDROID(display.native, surface.native, nanoseconds) 62 | internal actual inline fun eglDestroyContext(display: EglDisplay, context: EglContext) 63 | = EGL14.eglDestroyContext(display.native, context.native) 64 | internal actual inline fun eglDestroySurface(display: EglDisplay, surface: EglSurface) 65 | = EGL14.eglDestroySurface(display.native, surface.native) 66 | internal actual inline fun eglReleaseThread() = EGL14.eglReleaseThread() 67 | internal actual inline fun eglTerminate(display: EglDisplay) = EGL14.eglTerminate(display.native) 68 | internal actual inline fun eglGetError() = EGL14.eglGetError() -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/internal/misc.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package com.otaliastudios.opengl.internal 4 | 5 | import android.opengl.GLU 6 | import android.opengl.Matrix 7 | import android.util.Log 8 | import androidx.annotation.RequiresApi 9 | 10 | internal actual inline fun logv(tag: String, message: String) { Log.v(tag, message) } 11 | internal actual inline fun logi(tag: String, message: String) { Log.i(tag, message) } 12 | internal actual inline fun logw(tag: String, message: String) { Log.w(tag, message) } 13 | internal actual inline fun loge(tag: String, message: String) { Log.e(tag, message) } 14 | 15 | internal actual fun intToHexString(value: Int): String = Integer.toHexString(value) 16 | internal actual fun gluErrorString(value: Int): String = GLU.gluErrorString(value) 17 | 18 | internal actual fun matrixMakeIdentity(matrix: FloatArray) = Matrix.setIdentityM(matrix, 0) 19 | internal actual fun matrixTranslate(matrix: FloatArray, x: Float, y: Float, z: Float) = Matrix.translateM(matrix, 0, x, y, z) 20 | internal actual fun matrixScale(matrix: FloatArray, x: Float, y: Float, z: Float) = Matrix.scaleM(matrix, 0, x, y, z) 21 | internal actual fun matrixRotate(matrix: FloatArray, angle: Float, x: Float, y: Float, z: Float) = Matrix.rotateM(matrix, 0, angle, x, y, z) 22 | internal actual fun matrixClone(matrix: FloatArray) = matrix.clone() 23 | internal actual fun matrixMultiply(result: FloatArray, left: FloatArray, right: FloatArray) = Matrix.multiplyMM(result, 0, left, 0 , right, 0) -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/program/GlFlatProgram.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.program 2 | 3 | import android.graphics.Color 4 | import androidx.annotation.ColorInt 5 | 6 | public actual class GlFlatProgram : GlNativeFlatProgram() { 7 | 8 | public fun setColor(@ColorInt color: Int) { 9 | this.color = floatArrayOf( 10 | Color.red(color) / 255F, 11 | Color.green(color) / 255F, 12 | Color.blue(color) / 255F, 13 | Color.alpha(color) / 255F 14 | ) 15 | } 16 | } -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/surface/EglSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | 4 | import android.graphics.Bitmap 5 | import android.opengl.EGLSurface 6 | import android.opengl.GLES20 7 | import com.otaliastudios.opengl.core.Egloo 8 | import com.otaliastudios.opengl.core.EglCore 9 | import com.otaliastudios.opengl.internal.EglSurface 10 | import java.io.* 11 | import java.nio.ByteBuffer 12 | import java.nio.ByteOrder 13 | import kotlin.jvm.Throws 14 | 15 | /** 16 | * Common base class for EGL surfaces. 17 | * There can be multiple base surfaces associated with a single [EglCore] object. 18 | */ 19 | public actual abstract class EglSurface internal actual constructor( 20 | eglCore: EglCore, 21 | eglSurface: EglSurface 22 | ) : EglNativeSurface(eglCore, eglSurface) { 23 | 24 | @Suppress("unused") 25 | protected constructor(eglCore: EglCore, eglSurface: EGLSurface) 26 | : this(eglCore, EglSurface(eglSurface)) 27 | 28 | /** 29 | * Saves the EGL surface to the given output stream. 30 | * Expects that this object's EGL surface is current. 31 | */ 32 | @Suppress("MemberVisibilityCanBePrivate") 33 | public fun toOutputStream(stream: OutputStream, format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG) { 34 | if (!isCurrent()) throw RuntimeException("Expected EGL context/surface is not current") 35 | // glReadPixels fills in a "direct" ByteBuffer with what is essentially big-endian RGBA 36 | // data (i.e. a byte of red, followed by a byte of green...). While the Bitmap 37 | // constructor that takes an int[] wants little-endian ARGB (blue/red swapped), the 38 | // Bitmap "copy pixels" method wants the same format GL provides. 39 | // 40 | // Making this even more interesting is the upside-down nature of GL, which means 41 | // our output will look upside down relative to what appears on screen if the 42 | // typical GL conventions are used. 43 | val width = getWidth() 44 | val height = getHeight() 45 | val buf = ByteBuffer.allocateDirect(width * height * 4) 46 | buf.order(ByteOrder.LITTLE_ENDIAN) 47 | GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf) 48 | Egloo.checkGlError("glReadPixels") 49 | buf.rewind() 50 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 51 | bitmap.copyPixelsFromBuffer(buf) 52 | bitmap.compress(format, 90, stream) 53 | bitmap.recycle() 54 | } 55 | 56 | /** 57 | * Saves the EGL surface to a file. 58 | * Expects that this object's EGL surface is current. 59 | */ 60 | @Suppress("unused") 61 | @Throws(IOException::class) 62 | public fun toFile(file: File, format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG) { 63 | var stream: BufferedOutputStream? = null 64 | try { 65 | stream = BufferedOutputStream(FileOutputStream(file.toString())) 66 | toOutputStream(stream, format) 67 | } finally { 68 | stream?.close() 69 | } 70 | } 71 | 72 | /** 73 | * Saves the EGL surface to given format. 74 | * Expects that this object's EGL surface is current. 75 | */ 76 | @Suppress("unused") 77 | public fun toByteArray(format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG): ByteArray { 78 | val stream = ByteArrayOutputStream() 79 | stream.use { 80 | toOutputStream(it, format) 81 | return it.toByteArray() 82 | } 83 | } 84 | 85 | public companion object { 86 | @Suppress("HasPlatformType", "unused") 87 | protected val TAG: String = EglSurface::class.java.simpleName 88 | } 89 | } -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/surface/EglWindowSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | 4 | import android.graphics.SurfaceTexture 5 | import android.view.Surface 6 | import com.otaliastudios.opengl.core.EglCore 7 | 8 | 9 | /** 10 | * Recordable EGL window surface. 11 | * It's good practice to explicitly release() the surface, preferably from a finally block. 12 | */ 13 | @Suppress("unused") 14 | public actual open class EglWindowSurface : EglNativeWindowSurface { 15 | private var surface: Surface? = null 16 | private var releaseSurface = false 17 | 18 | /** 19 | * Set releaseSurface to true if you want the Surface to be released when release() is 20 | * called. This is convenient, but can interfere with framework classes that expect to 21 | * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the 22 | * surfaceDestroyed() callback won't fire). 23 | */ 24 | @Suppress("unused") 25 | @JvmOverloads 26 | public constructor(eglCore: EglCore, surface: Surface, releaseSurface: Boolean = false) 27 | : super(eglCore, eglCore.createWindowSurface(surface)) { 28 | this.surface = surface 29 | this.releaseSurface = releaseSurface 30 | } 31 | 32 | /** 33 | * Associates an EGL surface with the SurfaceTexture. 34 | */ 35 | @Suppress("unused") 36 | public constructor(eglCore: EglCore, surfaceTexture: SurfaceTexture) 37 | : super(eglCore, eglCore.createWindowSurface(surfaceTexture)) 38 | 39 | /** 40 | * Releases any resources associated with the EGL surface. 41 | * Does not require that the surface's EGL context be current. 42 | */ 43 | override fun release() { 44 | super.release() 45 | if (releaseSurface) { 46 | surface?.release() 47 | surface = null 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /library/src/androidJvmMain/kotlin/com/otaliastudios/opengl/types/buffers.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("BuffersJvmKt") 2 | package com.otaliastudios.opengl.types 3 | 4 | import com.otaliastudios.opengl.core.Egloo 5 | import java.nio.ByteOrder 6 | 7 | public actual typealias Buffer = java.nio.Buffer 8 | public actual typealias FloatBuffer = java.nio.FloatBuffer 9 | public actual typealias ByteBuffer = java.nio.ByteBuffer 10 | public actual typealias ShortBuffer = java.nio.ShortBuffer 11 | public actual typealias IntBuffer = java.nio.IntBuffer 12 | 13 | public actual fun byteBuffer(size: Int): ByteBuffer = ByteBuffer 14 | .allocateDirect(size * Egloo.SIZE_OF_BYTE) 15 | .order(ByteOrder.nativeOrder()) 16 | .also { it.limit(it.capacity()) } 17 | 18 | public actual fun shortBuffer(size: Int): ShortBuffer = byteBuffer(size * Egloo.SIZE_OF_SHORT).asShortBuffer() 19 | public actual fun floatBuffer(size: Int): FloatBuffer = byteBuffer(size * Egloo.SIZE_OF_FLOAT).asFloatBuffer() 20 | public actual fun intBuffer(size: Int): IntBuffer = byteBuffer(size * Egloo.SIZE_OF_INT).asIntBuffer() 21 | -------------------------------------------------------------------------------- /library/src/androidNative32BitMain/kotlin/com/otaliastudios/opengl/internal/gl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package com.otaliastudios.opengl.internal 4 | 5 | internal actual inline fun Int.toGLsizeiptr() = this 6 | -------------------------------------------------------------------------------- /library/src/androidNative64BitMain/kotlin/com/otaliastudios/opengl/internal/gl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package com.otaliastudios.opengl.internal 4 | 5 | internal actual inline fun Int.toGLsizeiptr() = this.toLong() 6 | 7 | -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/cinterop.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package com.otaliastudios.opengl 4 | 5 | import kotlinx.cinterop.* 6 | 7 | /** 8 | * C raw data: encapsulated in [CPointed]. It has three subclasses: 9 | * - [CFunction], representing a C function (that is, C code) 10 | * - [CVariable], representing a C variable located in memory 11 | * - [COpaque], representing something that is not a function or a variable. 12 | * 13 | * The [CVariable] is the most interesting one. It has a few direct subclasses: 14 | * - [CPrimitiveVar] for primitive types (byte, int, ...) 15 | * - [CStructVar] for C structs 16 | * - [CPointerVar] for C pointers. When a pointer is a long, this class would be similar to a [LongVar]. 17 | * It represents a pointer value located in memory (which means that we could get a pointer to the pointer). 18 | * 19 | * The [CPrimitiveVar] is the most interesting [CVariable]. It has many implementation: 20 | * [BooleanVar], [IntVar], [UIntVar], [FloatVar] and so on. So when we have a *Var, we are 21 | * managing a direct reference to that object's memory. We can simply use [CVariable.getRawPointer] 22 | * to access the raw memory address of this object. 23 | * 24 | * ---- 25 | * 26 | * C pointers: encapsulated in [CValuesRef]. As per docs, "Represents a reference to sequence of C values." 27 | * This class has 2 important direct subclasses: 28 | * - [CPointer] is simply a C pointer to a C var. In this case, to access the raw value (a [CPointed]: 29 | * that is, [CFunction], [CPrimitiveVar], [CStructVar], ...) one can simply call [CPointer.pointed]. 30 | * - Being a C Pointer, it also represents an array fo vars, so it has a "get" operator! 31 | * - Moreover, one can use the overloaded "plus" operator to move the pointer up or down. 32 | * - [CValues] is an immutable sequence of C values. 33 | * Passing CValues to native methods will make a copy of it and modifications will not be available 34 | * to the caller once the function returns. 35 | * 36 | * This means that [CPointer] is the friendly reference type, while [CValues] is less frequently used. 37 | * To obtain a [CPointer] out of a ref, one can simply use [CPointed.ptr] to create one. 38 | * On the other hand, to obtain the pointed value, as said, there is [CPointer.pointed]. 39 | * 40 | * ---- 41 | * 42 | * Anytime we have to deal with cinterop types as the ones described above, we must deal with C memory 43 | * as well. It seems to me that there are two options: 44 | * - use [memScoped] blocks: it offers allocation functions and will automatically release them 45 | * - use [nativeHeap] methods to allocate and, once done, release memory 46 | * Outside of cinterop world, I believe that Kotlin memory is automatically managed and collected 47 | * by the Kotlin Native runtime. 48 | */ 49 | private inline fun comments() = Unit -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/core/EglCore.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | import com.otaliastudios.opengl.internal.EglContext 4 | 5 | /** 6 | * Core EGL state (display, context, config). 7 | * The EGLContext must only be attached to one thread at a time. 8 | * This class is not thread-safe. 9 | * 10 | * @param sharedContext The context to share, or null if sharing is not desired. 11 | * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE. 12 | */ 13 | public actual class EglCore constructor( 14 | sharedContext: platform.egl.EGLContext? = platform.egl.EGL_NO_CONTEXT, 15 | flags: Int = 0 16 | ) : EglNativeCore(EglContext(sharedContext), flags) { 17 | 18 | public override fun makeCurrent() { 19 | super.makeCurrent() 20 | } 21 | 22 | public override fun release() { 23 | super.release() 24 | } 25 | 26 | public companion object { 27 | 28 | /** 29 | * Constructor flag: surface must be recordable. This discourages EGL from using a 30 | * pixel format that cannot be converted efficiently to something usable by the video 31 | * encoder. 32 | */ 33 | public const val FLAG_RECORDABLE: Int = EglNativeCore.FLAG_RECORDABLE 34 | 35 | /** 36 | * Constructor flag: ask for GLES3, fall back to GLES2 if not available. Without this 37 | * flag, GLES2 is used. 38 | */ 39 | public const val FLAG_TRY_GLES3: Int = EglNativeCore.FLAG_TRY_GLES3 40 | } 41 | } -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/geometry/PointF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public actual open class PointF actual constructor(public actual var x: Float, public actual var y: Float) { 4 | public actual constructor() : this(0F, 0F) 5 | } -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/geometry/RectF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public actual open class RectF actual constructor( 4 | public actual var left: Float, 5 | public actual var top: Float, 6 | public actual var right: Float, 7 | public actual var bottom: Float) { 8 | public actual constructor() : this(0F, 0F, 0F, 0F) 9 | public actual fun set(left: Float, top: Float, right: Float, bottom: Float) { 10 | this.left = left 11 | this.top = top 12 | this.right = right 13 | this.bottom = bottom 14 | } 15 | } -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/internal/misc.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package com.otaliastudios.opengl.internal 4 | 5 | import platform.android.* 6 | import kotlin.math.PI 7 | import kotlin.math.cos 8 | import kotlin.math.sin 9 | import kotlin.math.sqrt 10 | 11 | private inline fun log(priority: Int, tag: String, message: String) { 12 | __android_log_write(priority, tag, message) 13 | } 14 | 15 | internal actual inline fun logv(tag: String, message: String) { 16 | log(ANDROID_LOG_VERBOSE.toInt(), tag, message) 17 | } 18 | internal actual inline fun logi(tag: String, message: String) { 19 | log(ANDROID_LOG_INFO.toInt(), tag, message) 20 | } 21 | internal actual inline fun logw(tag: String, message: String) { 22 | log(ANDROID_LOG_WARN.toInt(), tag, message) 23 | } 24 | internal actual inline fun loge(tag: String, message: String) { 25 | log(ANDROID_LOG_ERROR.toInt(), tag, message) 26 | } 27 | 28 | internal actual fun intToHexString(value: Int) = value.toUInt().toString(16) 29 | internal actual fun gluErrorString(value: Int) = "" 30 | 31 | internal actual fun matrixMakeIdentity(matrix: FloatArray) { 32 | for (i in 0 until 16) { 33 | matrix[i] = if (i % 5 == 0) 1F else 0F 34 | } 35 | } 36 | internal actual fun matrixTranslate(matrix: FloatArray, x: Float, y: Float, z: Float) { 37 | for (i in 0 until 4) { 38 | matrix[12 + i] += matrix[i] * x + matrix[4 + i] * y + matrix[8 + i] * z 39 | } 40 | } 41 | internal actual fun matrixScale(matrix: FloatArray, x: Float, y: Float, z: Float) { 42 | for (i in 0 until 4) { 43 | matrix[i] *= x 44 | matrix[4 + i] *= y 45 | matrix[8 + i] *= z 46 | } 47 | } 48 | private val tempMatrix1 = FloatArray(16) 49 | private val tempMatrix2 = FloatArray(16) 50 | private fun matrixSetRotate(matrix: FloatArray, angle: Float, x: Float, y: Float, z: Float) { 51 | matrix[3] = 0F 52 | matrix[7] = 0F 53 | matrix[11] = 0F 54 | matrix[12] = 0F 55 | matrix[13] = 0F 56 | matrix[14] = 0F 57 | matrix[15] = 1F 58 | val a = (angle * PI / 180F).toFloat() 59 | val s = sin(a) 60 | val c = cos(a) 61 | if (1.0f == x && 0.0f == y && 0.0f == z) { 62 | matrix[5] = c 63 | matrix[10] = c 64 | matrix[6] = s 65 | matrix[9] = -s 66 | matrix[1] = 0F 67 | matrix[2] = 0F 68 | matrix[4] = 0F 69 | matrix[8] = 0F 70 | matrix[0] = 1F 71 | } else if (0.0f == x && 1.0f == y && 0.0f == z) { 72 | matrix[0] = c 73 | matrix[10] = c 74 | matrix[8] = s 75 | matrix[2] = -s 76 | matrix[1] = 0F 77 | matrix[4] = 0F 78 | matrix[6] = 0F 79 | matrix[9] = 0F 80 | matrix[5] = 1F 81 | } else if (0.0f == x && 0.0f == y && 1.0f == z) { 82 | matrix[0] = c 83 | matrix[5] = c 84 | matrix[1] = s 85 | matrix[4] = -s 86 | matrix[2] = 0F 87 | matrix[6] = 0F 88 | matrix[8] = 0F 89 | matrix[9] = 0F 90 | matrix[10] = 1F 91 | } else { 92 | val normx: Float 93 | val normy: Float 94 | val normz: Float 95 | val length = sqrt((x * x) + (y * y) + (z * z)) 96 | if (length == 1F) { 97 | normx = x 98 | normy = y 99 | normz = z 100 | } else { 101 | normx = x / length 102 | normy = y / length 103 | normz = z / length 104 | } 105 | val nc = 1.0F - c 106 | val xy = normx * normy 107 | val yz = normy * normz 108 | val zx = normz * normx 109 | val xs = normx * s 110 | val ys = normy * s 111 | val zs = normz * s 112 | matrix[0] = normx * normx * nc + c 113 | matrix[4] = xy * nc - zs 114 | matrix[8] = zx * nc + ys 115 | matrix[1] = xy * nc + zs 116 | matrix[5] = normy * normy * nc + c 117 | matrix[9] = yz * nc - xs 118 | matrix[2] = zx * nc - ys 119 | matrix[6] = yz * nc + xs 120 | matrix[10] = normz * normz * nc + c 121 | } 122 | } 123 | internal actual fun matrixRotate(matrix: FloatArray, angle: Float, x: Float, y: Float, z: Float) { 124 | matrixSetRotate(tempMatrix1, angle, x, y, z) 125 | matrixMultiply(tempMatrix2, matrix, tempMatrix1) 126 | tempMatrix2.copyInto(matrix) 127 | } 128 | internal actual fun matrixClone(matrix: FloatArray) = FloatArray(matrix.size) { matrix[it] } 129 | // https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/jni/android/opengl/util.cpp 130 | private inline fun I(x: Int, y: Int) = y + 4*x 131 | internal actual fun matrixMultiply(result: FloatArray, left: FloatArray, right: FloatArray) { 132 | for (i in 0 until 4) { 133 | val rhs_i0 = right[ I(i,0) ] 134 | var ri0 = left[ I(0, 0) ] * rhs_i0 135 | var ri1 = left[ I(0, 1) ] * rhs_i0 136 | var ri2 = left[ I(0, 2) ] * rhs_i0 137 | var ri3 = left[ I(0, 3) ] * rhs_i0 138 | for (j in 1 until 4) { 139 | val rhs_ij = right[I(i,j)] 140 | ri0 += left[ I(j,0) ] * rhs_ij 141 | ri1 += left[ I(j,1) ] * rhs_ij 142 | ri2 += left[ I(j,2) ] * rhs_ij 143 | ri3 += left[ I(j,3) ] * rhs_ij 144 | } 145 | result[ I(i,0) ] = ri0 146 | result[ I(i,1) ] = ri1 147 | result[ I(i,2) ] = ri2 148 | result[ I(i,3) ] = ri3 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/program/GlFlatProgram.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.program 2 | 3 | public actual class GlFlatProgram : GlNativeFlatProgram() -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/surface/EglSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | import com.otaliastudios.opengl.core.EglCore 4 | import com.otaliastudios.opengl.internal.EglSurface 5 | import platform.egl.EGLSurface 6 | 7 | /** 8 | * Common base class for EGL surfaces. 9 | * There can be multiple base surfaces associated with a single [EglCore] object. 10 | */ 11 | public actual abstract class EglSurface internal actual constructor(eglCore: EglCore, eglSurface: EglSurface) 12 | : EglNativeSurface(eglCore, eglSurface) -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/surface/EglWindowSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | import com.otaliastudios.opengl.core.EglCore 4 | import platform.egl.EGLNativeWindowType 5 | 6 | 7 | /** 8 | * Recordable EGL window surface. 9 | * It's good practice to explicitly release() the surface, preferably from a finally block. 10 | */ 11 | @Suppress("unused") 12 | public actual open class EglWindowSurface(eglCore: EglCore, nativeWindow: EGLNativeWindowType) 13 | : EglNativeWindowSurface(eglCore, eglCore.createWindowSurface(nativeWindow)) 14 | -------------------------------------------------------------------------------- /library/src/androidNativeMain/kotlin/com/otaliastudios/opengl/types/buffers.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.types 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import kotlinx.cinterop.* 5 | 6 | public actual abstract class Buffer { 7 | 8 | protected abstract val capacity: Int 9 | protected var position: Int = 0 10 | protected abstract var limit: Int 11 | 12 | public actual fun remaining(): Int = limit - position 13 | public actual fun hasRemaining(): Boolean = remaining() > 0 14 | public actual fun capacity(): Int = capacity 15 | public actual fun position(): Int = position 16 | public actual fun limit(): Int = limit 17 | 18 | public actual fun position(position: Int): Buffer { 19 | this.position = position 20 | return this 21 | } 22 | 23 | public actual fun limit(limit: Int): Buffer { 24 | this.limit = limit 25 | return this 26 | } 27 | 28 | public actual fun clear(): Buffer { 29 | position = 0 30 | limit = capacity 31 | return this 32 | } 33 | 34 | public actual fun rewind(): Buffer { 35 | position = 0 36 | return this 37 | } 38 | 39 | public actual fun flip(): Buffer { 40 | limit = position 41 | position = 0 42 | return this 43 | } 44 | 45 | public abstract fun pointer(): CPointer<*> 46 | } 47 | 48 | public actual abstract class FloatBuffer : Buffer() { 49 | public actual abstract fun get(): Float 50 | public actual abstract fun get(index: Int): Float 51 | public actual abstract fun put(value: Float): FloatBuffer 52 | public actual fun put(values: FloatArray): FloatBuffer { 53 | values.forEach { put(it) } 54 | return this 55 | } 56 | } 57 | 58 | public actual abstract class ByteBuffer : Buffer() { 59 | public actual abstract fun get(): Byte 60 | public actual abstract fun get(index: Int): Byte 61 | public actual abstract fun put(value: Byte): ByteBuffer 62 | public actual fun put(values: ByteArray): ByteBuffer { 63 | values.forEach { put(it) } 64 | return this 65 | } 66 | } 67 | 68 | public actual abstract class ShortBuffer : Buffer() { 69 | public actual abstract fun get(): Short 70 | public actual abstract fun get(index: Int): Short 71 | public actual abstract fun put(value: Short): ShortBuffer 72 | public actual fun put(values: ShortArray): ShortBuffer { 73 | values.forEach { put(it) } 74 | return this 75 | } 76 | } 77 | 78 | public actual abstract class IntBuffer : Buffer() { 79 | public actual abstract fun get(): Int 80 | public actual abstract fun get(index: Int): Int 81 | public actual abstract fun put(value: Int): IntBuffer 82 | public actual fun put(values: IntArray): IntBuffer { 83 | values.forEach { put(it) } 84 | return this 85 | } 86 | } 87 | 88 | private class BufferImpl(capacity: Int, size: Int) : Disposable { 89 | private val base = nativeHeap.allocArray(capacity * size) 90 | private val baseInt = base.reinterpret() 91 | private val baseShort = base.reinterpret() 92 | private val baseFloat = base.reinterpret() 93 | fun getByte(position: Int) = base[position] 94 | fun getInt(position: Int) = baseInt[position] 95 | fun getShort(position: Int) = baseShort[position] 96 | fun getFloat(position: Int) = baseFloat[position] 97 | fun putByte(position: Int, value: Byte) { base[position] = value } 98 | fun putInt(position: Int, value: Int) { baseInt[position] = value } 99 | fun putShort(position: Int, value: Short) { baseShort[position] = value } 100 | fun putFloat(position: Int, value: Float) { baseFloat[position] = value } 101 | fun getBytePointer(position: Int) = base + position 102 | fun getIntPointer(position: Int) = baseInt + position 103 | fun getShortPointer(position: Int) = baseShort + position 104 | fun getFloatPointer(position: Int) = baseFloat + position 105 | override fun dispose() { 106 | nativeHeap.free(base) 107 | } 108 | } 109 | 110 | public actual fun floatBuffer(size: Int): FloatBuffer = object: FloatBuffer() { 111 | override val capacity = size 112 | override var limit = size 113 | private val impl = BufferImpl(size, Egloo.SIZE_OF_FLOAT) 114 | override fun pointer() = impl.getFloatPointer(position) as CPointer<*> 115 | override fun get() = impl.getFloat(position++) 116 | override fun get(index: Int) = impl.getFloat(index) 117 | override fun put(value: Float): FloatBuffer { 118 | impl.putFloat(position++, value) 119 | return this 120 | } 121 | } 122 | 123 | public actual fun shortBuffer(size: Int): ShortBuffer = object: ShortBuffer() { 124 | override val capacity = size 125 | override var limit = size 126 | private val impl = BufferImpl(size, Egloo.SIZE_OF_SHORT) 127 | override fun pointer() = impl.getShortPointer(position) as CPointer<*> 128 | override fun get() = impl.getShort(position++) 129 | override fun get(index: Int) = impl.getShort(index) 130 | override fun put(value: Short): ShortBuffer { 131 | impl.putShort(position++, value) 132 | return this 133 | } 134 | } 135 | 136 | public actual fun intBuffer(size: Int): IntBuffer = object: IntBuffer() { 137 | override val capacity = size 138 | override var limit = size 139 | private val impl = BufferImpl(size, Egloo.SIZE_OF_INT) 140 | override fun pointer() = impl.getIntPointer(position) as CPointer<*> 141 | override fun get() = impl.getInt(position++) 142 | override fun get(index: Int) = impl.getInt(index) 143 | override fun put(value: Int): IntBuffer { 144 | impl.putInt(position++, value) 145 | return this 146 | } 147 | } 148 | 149 | public actual fun byteBuffer(size: Int): ByteBuffer = object: ByteBuffer() { 150 | override val capacity = size 151 | override var limit = size 152 | private val impl = BufferImpl(size, Egloo.SIZE_OF_BYTE) 153 | override fun pointer() = impl.getBytePointer(position) as CPointer<*> 154 | override fun get() = impl.getByte(position++) 155 | override fun get(index: Int) = impl.getByte(index) 156 | override fun put(value: Byte): ByteBuffer { 157 | impl.putByte(position++, value) 158 | return this 159 | } 160 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/buffer/GlBuffer.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package com.otaliastudios.opengl.buffer 4 | 5 | import com.otaliastudios.opengl.core.GlBindable 6 | import com.otaliastudios.opengl.core.Egloo 7 | import com.otaliastudios.opengl.internal.glBindBuffer 8 | import com.otaliastudios.opengl.internal.glDeleteBuffers 9 | import com.otaliastudios.opengl.internal.glGenBuffers 10 | 11 | public open class GlBuffer(public val target: Int, id: Int? = null) : GlBindable { 12 | 13 | public val id: Int = id ?: run { 14 | val array = UIntArray(1) 15 | glGenBuffers(1, array) 16 | Egloo.checkGlError("glGenBuffers") 17 | array[0].toInt() 18 | } 19 | 20 | override fun bind() { 21 | glBindBuffer(target.toUInt(), id.toUInt()) 22 | } 23 | 24 | override fun unbind() { 25 | glBindBuffer(target.toUInt(), 0U) 26 | } 27 | 28 | public fun release() { 29 | glDeleteBuffers(1, uintArrayOf(id.toUInt())) 30 | } 31 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/buffer/GlShaderStorageBuffer.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.buffer 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.core.use 5 | import com.otaliastudios.opengl.internal.AndroidJvmRequiresApi 6 | import com.otaliastudios.opengl.internal.GL_SHADER_STORAGE_BUFFER 7 | import com.otaliastudios.opengl.internal.glBindBufferBase 8 | import com.otaliastudios.opengl.internal.glBufferData 9 | 10 | @AndroidJvmRequiresApi(21, 21) 11 | @Suppress("unused") 12 | public class GlShaderStorageBuffer(public val size: Int, public val usage: Int) 13 | : GlBuffer(target = GL_SHADER_STORAGE_BUFFER.toInt()) { 14 | 15 | init { 16 | use { 17 | glBufferData(target.toUInt(), size, usage.toUInt()) 18 | Egloo.checkGlError("glBufferData") 19 | } 20 | } 21 | 22 | public fun bind(index: Int) { 23 | // Note: a third option, glBindBufferRange, will only bind a subrange of the SSBO. 24 | // https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBindBuffer.xhtml 25 | // https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBindBufferBase.xhtml 26 | // https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBindBufferRange.xhtml 27 | glBindBufferBase(target.toUInt(), index.toUInt(), id.toUInt()) 28 | Egloo.checkGlError("glBindBufferBase") 29 | } 30 | 31 | // Can create an interface like GlBindable for indexed targets like GL_SHADER_STORAGE_BUFFER 32 | public fun use(index: Int, block: () -> Unit) { 33 | bind(index) 34 | block() 35 | unbind() 36 | } 37 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/core/EglNativeConfigChooser.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | import com.otaliastudios.opengl.internal.EGL_ALPHA_SIZE 4 | import com.otaliastudios.opengl.internal.EGL_BLUE_SIZE 5 | import com.otaliastudios.opengl.internal.EGL_GREEN_SIZE 6 | import com.otaliastudios.opengl.internal.EGL_NONE 7 | import com.otaliastudios.opengl.internal.EGL_OPENGL_ES2_BIT 8 | import com.otaliastudios.opengl.internal.EGL_OPENGL_ES3_BIT_KHR 9 | import com.otaliastudios.opengl.internal.EGL_PBUFFER_BIT 10 | import com.otaliastudios.opengl.internal.EGL_RED_SIZE 11 | import com.otaliastudios.opengl.internal.EGL_RENDERABLE_TYPE 12 | import com.otaliastudios.opengl.internal.EGL_SURFACE_TYPE 13 | import com.otaliastudios.opengl.internal.EGL_WINDOW_BIT 14 | import com.otaliastudios.opengl.internal.EglConfig 15 | import com.otaliastudios.opengl.internal.EglDisplay 16 | import com.otaliastudios.opengl.internal.eglChooseConfig 17 | import com.otaliastudios.opengl.internal.logw 18 | 19 | public open class EglNativeConfigChooser { 20 | 21 | public companion object { 22 | private const val EGL_RECORDABLE_ANDROID = 0x3142 // Android-specific extension. 23 | } 24 | 25 | internal fun getConfig(display: EglDisplay, version: Int, recordable: Boolean): EglConfig? { 26 | val attributes = getConfigSpec(version, recordable) 27 | val configs = arrayOfNulls(1) 28 | val numConfigs = IntArray(1) 29 | if (!eglChooseConfig(display, attributes, configs, 1, numConfigs)) { 30 | logw("EglConfigChooser", "Unable to find RGB8888 / $version EGLConfig") 31 | return null 32 | } 33 | return configs.get(0) 34 | } 35 | 36 | /** 37 | * Finds a suitable EGLConfig with r=8, g=8, b=8, a=8. 38 | * Does not specify depth or stencil size. 39 | * 40 | * The actual drawing surface is generally RGBA or RGBX, so omitting the alpha doesn't 41 | * really help - it can also lead to huge performance hit on glReadPixels() when reading 42 | * into a GL_RGBA buffer. 43 | */ 44 | internal fun getConfigSpec(version: Int, recordable: Boolean): IntArray { 45 | val renderableType = if (version >= 3) { 46 | EGL_OPENGL_ES2_BIT or EGL_OPENGL_ES3_BIT_KHR 47 | } else { 48 | EGL_OPENGL_ES2_BIT 49 | } 50 | return intArrayOf( 51 | EGL_RED_SIZE, 8, 52 | EGL_GREEN_SIZE, 8, 53 | EGL_BLUE_SIZE, 8, 54 | EGL_ALPHA_SIZE, 8, 55 | // We can create both window surfaces and pbuffer surfaces. 56 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT or EGL_PBUFFER_BIT, 57 | EGL_RENDERABLE_TYPE, renderableType, 58 | if (recordable) EGL_RECORDABLE_ANDROID else EGL_NONE, 59 | if (recordable) 1 else 0, 60 | EGL_NONE) 61 | } 62 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/core/Egloo.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | 4 | import com.otaliastudios.opengl.extensions.makeIdentity 5 | import com.otaliastudios.opengl.internal.EGL_DRAW 6 | import com.otaliastudios.opengl.internal.EGL_SUCCESS 7 | import com.otaliastudios.opengl.internal.GL_NO_ERROR 8 | import com.otaliastudios.opengl.internal.eglGetCurrentContext 9 | import com.otaliastudios.opengl.internal.eglGetCurrentDisplay 10 | import com.otaliastudios.opengl.internal.eglGetCurrentSurface 11 | import com.otaliastudios.opengl.internal.eglGetError 12 | import com.otaliastudios.opengl.internal.glGetError 13 | import com.otaliastudios.opengl.internal.gluErrorString 14 | import com.otaliastudios.opengl.internal.intToHexString 15 | import com.otaliastudios.opengl.internal.loge 16 | import com.otaliastudios.opengl.internal.logi 17 | import kotlin.jvm.JvmField 18 | import kotlin.jvm.JvmStatic 19 | 20 | /** 21 | * Contains static utilities for EGL and GLES. 22 | */ 23 | public object Egloo { 24 | 25 | public const val SIZE_OF_FLOAT: Int = 4 26 | public const val SIZE_OF_BYTE: Int = 1 27 | public const val SIZE_OF_SHORT: Int = 2 28 | public const val SIZE_OF_INT: Int = 4 29 | 30 | /** 31 | * Identify matrix for general use. 32 | */ 33 | @JvmField 34 | public val IDENTITY_MATRIX: FloatArray = FloatArray(16).apply { 35 | makeIdentity() 36 | } 37 | 38 | /** 39 | * Checks for GLES errors. 40 | */ 41 | @JvmStatic 42 | public fun checkGlError(opName: String) { 43 | val error = glGetError().toInt() 44 | if (error != GL_NO_ERROR) { 45 | val message = "Error during $opName: glError 0x${intToHexString(error)}: ${gluErrorString(error)}" 46 | loge("Egloo", message) 47 | throw RuntimeException(message) 48 | } 49 | } 50 | 51 | /** 52 | * Checks for EGL errors. 53 | */ 54 | @JvmStatic 55 | public fun checkEglError(opName: String) { 56 | val error = eglGetError() 57 | if (error != EGL_SUCCESS) { 58 | val message = "Error during $opName: EGL error 0x${intToHexString(error)}" 59 | loge("Egloo", message) 60 | throw RuntimeException(message) 61 | } 62 | } 63 | 64 | /** 65 | * Checks for program handles. 66 | */ 67 | @JvmStatic 68 | public fun checkGlProgramLocation(location: Int, label: String) { 69 | if (location < 0) { 70 | val message = "Unable to locate $label in program" 71 | loge("Egloo", message) 72 | throw RuntimeException(message) 73 | } 74 | } 75 | 76 | /** 77 | * Writes the current display, context, and surface to the log. 78 | */ 79 | @Suppress("unused") 80 | @JvmStatic 81 | public fun logCurrent(msg: String) { 82 | val display = eglGetCurrentDisplay() 83 | val context = eglGetCurrentContext() 84 | val surface = eglGetCurrentSurface(EGL_DRAW) 85 | logi("Egloo", "Current EGL ($msg): display=$display, context=$context, surface=$surface") 86 | } 87 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/core/GlBindable.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | public interface GlBindable { 4 | public fun bind() 5 | public fun unbind() 6 | } 7 | 8 | public fun GlBindable.use(block: () -> Unit) { 9 | bind() 10 | block() 11 | unbind() 12 | } 13 | 14 | public fun use(vararg bindables: GlBindable, block: () -> Unit) { 15 | bindables.forEach { it.bind() } 16 | block() 17 | bindables.forEach { it.unbind() } 18 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/core/GlViewportAware.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.core 2 | 3 | import com.otaliastudios.opengl.internal.GL_VIEWPORT 4 | import com.otaliastudios.opengl.internal.glGetIntegerv 5 | 6 | public abstract class GlViewportAware { 7 | 8 | private val viewportArray = IntArray(4) 9 | 10 | public var viewportWidth: Int = -1 11 | protected set 12 | 13 | public var viewportHeight: Int = -1 14 | protected set 15 | 16 | public fun setViewportSize(width: Int, height: Int) { 17 | if (width != viewportWidth || height != viewportHeight) { 18 | viewportWidth = width 19 | viewportHeight = height 20 | onViewportSizeChanged() 21 | } 22 | } 23 | 24 | protected open fun onViewportSizeChanged() { 25 | // Do nothing. 26 | } 27 | 28 | protected fun ensureViewportSize() { 29 | if (viewportHeight == -1 || viewportWidth == -1) { 30 | glGetIntegerv(GL_VIEWPORT.toUInt(), viewportArray) 31 | setViewportSize(viewportArray[2], viewportArray[3]) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/Gl2dDrawable.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | import com.otaliastudios.opengl.geometry.RectF 4 | import kotlin.math.max 5 | import kotlin.math.min 6 | 7 | @Suppress("unused") 8 | public abstract class Gl2dDrawable: GlDrawable() { 9 | public final override val coordsPerVertex: Int = 2 10 | 11 | public fun getBounds(rect: RectF) { 12 | var top = -Float.MAX_VALUE // not MIN_VALUE! 13 | var bottom = Float.MAX_VALUE 14 | var left = Float.MAX_VALUE 15 | var right = -Float.MAX_VALUE 16 | var count = 0 17 | while (vertexArray.hasRemaining()) { 18 | val value = vertexArray.get() 19 | if (count % 2 == 0) { // x coordinate 20 | left = min(left, value) 21 | right = max(right, value) 22 | } else { // y coordinate 23 | top = max(top, value) 24 | bottom = min(bottom, value) 25 | } 26 | count++ 27 | } 28 | vertexArray.rewind() 29 | rect.set(left, top, right, bottom) 30 | } 31 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/Gl2dMesh.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.geometry.IndexedSegmentF 5 | import com.otaliastudios.opengl.geometry.PointF 6 | import com.otaliastudios.opengl.internal.* 7 | import com.otaliastudios.opengl.internal.GL_TRIANGLES 8 | import com.otaliastudios.opengl.types.* 9 | import kotlin.IllegalArgumentException 10 | 11 | 12 | public open class Gl2dMesh: Gl2dDrawable() { 13 | 14 | override var vertexArray: FloatBuffer = floatBuffer(6) 15 | private var vertexIndices: ByteBuffer? = null 16 | 17 | public fun setPoints(points: List) { 18 | setPoints(points.map { it.x }, points.map { it.y }) 19 | } 20 | 21 | public fun setPoints(x: List, y: List) { 22 | if (x.size != y.size) throw IllegalArgumentException("x.size != y.size") 23 | val points = x.size 24 | val coords = points * 2 25 | if (vertexArray.capacity() < coords) { 26 | vertexArray.dispose() 27 | vertexArray = floatBuffer(coords) 28 | } else { 29 | vertexArray.clear() 30 | } 31 | val segments = mutableListOf() 32 | for (i in 0 until points) { 33 | val xi = x[i] 34 | val yi = y[i] 35 | vertexArray.put(xi) 36 | vertexArray.put(yi) 37 | for (j in (i + 1) until points) { 38 | val xj = x[j] 39 | val yj = y[j] 40 | segments.add(IndexedSegmentF(i, j, xi, yi, xj, yj)) 41 | } 42 | } 43 | vertexArray.flip() 44 | notifyVertexArrayChange() 45 | 46 | // Sort segments and iterate over them to select. 47 | segments.sortBy { it.length } 48 | val accepted = mutableListOf() 49 | for (s in segments) { 50 | if (accepted.none { it.intersects(s) }) { 51 | accepted.add(s) 52 | } 53 | } 54 | computeIndicesFromIndexedSegments(accepted) 55 | } 56 | 57 | // Segments must be non intersecting and sorted by ascending length 58 | private fun computeIndicesFromIndexedSegments(segments: List) { 59 | // Now we have all segments. Each of this can participate in many triangles, 60 | // but we actually want to take at most two of them, one on each side. 61 | // NOTE: consider sorting to make the inner loop faster and to make sure 62 | // that we take the smaller triangles instead of big ones 63 | val indices = mutableListOf() 64 | for (si in segments.indices) { 65 | val s1 = segments[si] 66 | var hasPositiveTriangle = false 67 | var hasNegativeTriangle = false 68 | for (sj in (si + 1) until segments.size) { 69 | if (hasPositiveTriangle && hasNegativeTriangle) break 70 | val s2 = segments[sj] 71 | var s2UnsharedIndex: Int 72 | var s2UnsharedX: Float 73 | var s2UnsharedY: Float 74 | if (s1.hasIndex(s2.i)) { 75 | // s2i is the shared value! s2j is the other. 76 | s2UnsharedIndex = s2.j 77 | s2UnsharedX = s2.jx 78 | s2UnsharedY = s2.jy 79 | } else if (s1.hasIndex(s2.j)) { 80 | // s2j is the shared value! s2i is the other. 81 | s2UnsharedIndex = s2.i 82 | s2UnsharedX = s2.ix 83 | s2UnsharedY = s2.iy 84 | } else { 85 | // Keep searching 86 | continue 87 | } 88 | // Since we have two segments, and they are sorted by length, in theory we don't 89 | // need to search for the last one, it MUST exist. We could assume it does. 90 | // However this might create duplicate triangles when going on with the outer loop. 91 | // I don't think we should search for the last one. It MUST exist. 92 | // So we could simply create it. 93 | val orientation = s1.orientation(s2UnsharedX, s2UnsharedY) 94 | if (orientation == 0) continue 95 | if (orientation > 0 && hasPositiveTriangle) continue 96 | if (orientation < 0 && hasNegativeTriangle) continue 97 | for (sk in (sj + 1) until segments.size) { 98 | val s3 = segments[sk] 99 | if (s3.hasIndex(s2UnsharedIndex) && (s3.hasIndex(s1.i) || s3.hasIndex(s1.j))) { 100 | // Shares a point with s1, and shares the correct point with s2. 101 | indices.add(s1.i.toByte()) 102 | indices.add(s1.j.toByte()) 103 | indices.add(s2UnsharedIndex.toByte()) 104 | if (orientation > 0) hasPositiveTriangle = true 105 | if (orientation < 0) hasNegativeTriangle = true 106 | break 107 | } 108 | } 109 | } 110 | } 111 | vertexIndices?.dispose() // TODO reuse it if possible 112 | vertexIndices = byteBuffer(indices.size).also { buffer -> 113 | indices.forEach { buffer.put(it) } 114 | buffer.clear() 115 | } 116 | } 117 | 118 | override fun draw() { 119 | vertexIndices?.let { 120 | Egloo.checkGlError("glDrawElements start") 121 | glDrawElements(GL_TRIANGLES, it.limit(), GL_UNSIGNED_BYTE, it) 122 | Egloo.checkGlError("glDrawElements end") 123 | } 124 | } 125 | 126 | override fun release() { 127 | super.release() 128 | vertexIndices?.dispose() 129 | } 130 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/Gl3dDrawable.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | @Suppress("unused") 4 | public abstract class Gl3dDrawable: GlDrawable() { 5 | final override val coordsPerVertex: Int = 3 6 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/GlCircle.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | @Suppress("unused") 4 | public open class GlCircle : GlPolygon(360) -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/GlDrawable.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | 4 | import com.otaliastudios.opengl.core.Egloo 5 | import com.otaliastudios.opengl.core.GlViewportAware 6 | import com.otaliastudios.opengl.types.FloatBuffer 7 | import com.otaliastudios.opengl.types.dispose 8 | import com.otaliastudios.opengl.internal.matrixClone 9 | 10 | public abstract class GlDrawable : GlViewportAware() { 11 | 12 | /** 13 | * The model matrix for this object. Defaults to the 14 | * identity matrix, but can be accessed and modified. 15 | */ 16 | public val modelMatrix: FloatArray = matrixClone(Egloo.IDENTITY_MATRIX) 17 | 18 | /** 19 | * Returns the array of vertices. 20 | * To avoid allocations, this returns internal state. The caller must not modify it. 21 | */ 22 | // TODO if this is set, like vertexArray = ..., we won't call Buffer.dispose(). 23 | public abstract var vertexArray: FloatBuffer 24 | 25 | /** 26 | * Returns the number of position coordinates per vertex. This will be 2 or 3. 27 | */ 28 | public abstract val coordsPerVertex: Int 29 | 30 | /** 31 | * Returns the width, in bytes, of the data for each vertex. 32 | */ 33 | public open val vertexStride: Int 34 | get() = coordsPerVertex * Egloo.SIZE_OF_FLOAT 35 | 36 | /** 37 | * Returns the number of vertices stored in the vertex array. 38 | */ 39 | public open val vertexCount: Int 40 | get() = vertexArray.limit() / coordsPerVertex 41 | 42 | /** 43 | * Draws this drawable. 44 | * This function should not be called directly. 45 | * Instead, this drawable should be passed to some [GlProgram]. 46 | */ 47 | public abstract fun draw() 48 | 49 | public var vertexArrayVersion: Int = 0 50 | private set 51 | 52 | protected fun notifyVertexArrayChange() { 53 | vertexArrayVersion++ 54 | } 55 | 56 | public open fun release() { 57 | vertexArray.dispose() 58 | } 59 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/GlPolygon.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.extensions.scale 5 | import com.otaliastudios.opengl.extensions.translate 6 | import com.otaliastudios.opengl.geometry.PointF 7 | import com.otaliastudios.opengl.types.floatBuffer 8 | import com.otaliastudios.opengl.internal.GL_TRIANGLE_FAN 9 | import com.otaliastudios.opengl.internal.glDrawArrays 10 | import com.otaliastudios.opengl.types.FloatBuffer 11 | import kotlin.math.PI 12 | import kotlin.math.cos 13 | import kotlin.math.sin 14 | 15 | @Suppress("unused") 16 | public open class GlPolygon(private val sides: Int): Gl2dDrawable() { 17 | init { 18 | if (sides < 3) { 19 | throw IllegalArgumentException("Polygon should have at least 3 sides.") 20 | } 21 | } 22 | 23 | private var viewportTranslationX = 0F 24 | private var viewportTranslationY = 0F 25 | private var viewportScaleX = 1F 26 | private var viewportScaleY = 1F 27 | 28 | /** 29 | * The polygon radius. The value 1 is half the smallest viewport dimension. 30 | * For example, a [GlCircle] with radius 1 will touch the viewport borders exactly. 31 | */ 32 | public var radius: Float = 1F 33 | set(value) { 34 | field = value 35 | updateArray() 36 | } 37 | 38 | public var rotation: Float = 0F 39 | set(value) { 40 | field = value % 360 41 | updateArray() 42 | } 43 | 44 | public var centerX: Float = 0F 45 | set(value) { 46 | field = value 47 | updateArray() 48 | onViewportSizeOrCenterChanged() 49 | } 50 | 51 | public var centerY: Float = 0F 52 | set(value) { 53 | field = value 54 | updateArray() 55 | onViewportSizeOrCenterChanged() 56 | } 57 | 58 | public var center: PointF 59 | get() = PointF(centerX, centerY) 60 | set(value) { 61 | centerX = value.x 62 | centerY = value.y 63 | } 64 | 65 | override var vertexArray: FloatBuffer = floatBuffer((sides + 2) * coordsPerVertex) 66 | 67 | init { 68 | updateArray() 69 | } 70 | 71 | private fun updateArray() { 72 | val array = vertexArray 73 | array.clear() 74 | array.put(centerX) 75 | array.put(centerY) 76 | var angle = rotation * (PI / 180F).toFloat() 77 | val step = (2F * PI).toFloat() / sides 78 | repeat(sides) { 79 | array.put(centerX + radius * cos(angle)) 80 | array.put(centerY + radius * sin(angle)) 81 | angle += step 82 | } 83 | array.put(array.get(2)) // Close the fan 84 | array.put(array.get(3)) // Close the fan 85 | array.flip() 86 | notifyVertexArrayChange() 87 | } 88 | 89 | override fun onViewportSizeChanged() { 90 | super.onViewportSizeChanged() 91 | onViewportSizeOrCenterChanged() 92 | } 93 | 94 | private fun onViewportSizeOrCenterChanged() { 95 | // Undo the previous modifications. 96 | modelMatrix.scale(x = 1F / viewportScaleX, y = 1F / viewportScaleY) 97 | modelMatrix.translate(x = -viewportTranslationX, y = -viewportTranslationY) 98 | // Compute the new ones. 99 | if (viewportWidth > viewportHeight) { 100 | viewportScaleX = viewportHeight.toFloat() / viewportWidth 101 | viewportScaleY = 1F 102 | viewportTranslationX = centerX * (1 - viewportScaleX) 103 | viewportTranslationY = 0F 104 | } else if (viewportWidth < viewportHeight) { 105 | viewportScaleY = viewportWidth.toFloat() / viewportHeight 106 | viewportScaleX = 1F 107 | viewportTranslationY = centerY * (1 - viewportScaleY) 108 | viewportTranslationX = 0F 109 | } else { 110 | viewportScaleX = 1F 111 | viewportScaleY = 1F 112 | viewportTranslationX = 0F 113 | viewportTranslationY = 0F 114 | } 115 | // Apply the new ones. 116 | modelMatrix.translate(x = viewportTranslationX, y = viewportTranslationY) 117 | modelMatrix.scale(x = viewportScaleX, y = viewportScaleY) 118 | } 119 | 120 | override fun draw() { 121 | glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount) 122 | Egloo.checkGlError("glDrawArrays") 123 | } 124 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/GlRect.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.geometry.RectF 5 | import com.otaliastudios.opengl.types.floatBuffer 6 | import com.otaliastudios.opengl.internal.GL_TRIANGLE_STRIP 7 | import com.otaliastudios.opengl.internal.glDrawArrays 8 | import com.otaliastudios.opengl.types.FloatBuffer 9 | 10 | @Suppress("unused") 11 | public open class GlRect: Gl2dDrawable() { 12 | 13 | private companion object { 14 | // A full square, extending from -1 to +1 in both dimensions. 15 | // When the MVP matrix is identity, this will exactly cover the viewport. 16 | private val FULL_RECTANGLE_COORDS = floatArrayOf( 17 | -1.0f, -1.0f, // bottom left 18 | 1.0f, -1.0f, // bottom right 19 | -1.0f, 1.0f, // top left 20 | 1.0f, 1.0f) // top right 21 | } 22 | 23 | override var vertexArray: FloatBuffer = floatBuffer(FULL_RECTANGLE_COORDS.size).also { 24 | it.put(FULL_RECTANGLE_COORDS) 25 | it.clear() 26 | } 27 | 28 | @Suppress("unused") 29 | @Deprecated("Use setRect", ReplaceWith("setRect(rect)")) 30 | public open fun setVertexArray(array: FloatArray) { 31 | if (array.size != 4 * coordsPerVertex) { 32 | throw IllegalArgumentException("Vertex array should have 8 values.") 33 | } 34 | vertexArray.clear() 35 | vertexArray.put(array) 36 | vertexArray.flip() 37 | notifyVertexArrayChange() 38 | } 39 | 40 | @Deprecated("Use setRect", ReplaceWith("setRect(rect)")) 41 | public open fun setVertexArray(rect: RectF) { 42 | setRect(rect) 43 | } 44 | 45 | @Suppress("unused") 46 | public fun setRect(rect: RectF) { 47 | setRect(rect.left, rect.top, rect.right, rect.bottom) 48 | } 49 | 50 | @Suppress("MemberVisibilityCanBePrivate") 51 | public fun setRect(left: Float, top: Float, right: Float, bottom: Float) { 52 | vertexArray.clear() 53 | // 1 54 | vertexArray.put(left) 55 | vertexArray.put(bottom) 56 | // 2 57 | vertexArray.put(right) 58 | vertexArray.put(bottom) 59 | // 3 60 | vertexArray.put(left) 61 | vertexArray.put(top) 62 | // 4 63 | vertexArray.put(right) 64 | vertexArray.put(top) 65 | vertexArray.flip() 66 | notifyVertexArrayChange() 67 | } 68 | 69 | override fun draw() { 70 | Egloo.checkGlError("glDrawArrays start") 71 | glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount) 72 | Egloo.checkGlError("glDrawArrays end") 73 | } 74 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/GlSquare.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | import kotlin.math.sqrt 4 | 5 | @Suppress("unused") 6 | public open class GlSquare : GlPolygon(4) { 7 | init { 8 | // We expect a square to be horizontal, which is not what GlPolygon does. 9 | // We compensate here and compensate for the radius. 10 | rotation = 45F 11 | radius = sqrt(2F) 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/draw/GlTriangle.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.draw 2 | 3 | @Suppress("unused") 4 | public open class GlTriangle : GlPolygon(3) -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/extensions/Buffers.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.extensions 2 | 3 | import com.otaliastudios.opengl.types.ByteBuffer 4 | import com.otaliastudios.opengl.types.byteBuffer 5 | import com.otaliastudios.opengl.types.FloatBuffer 6 | import com.otaliastudios.opengl.types.floatBuffer 7 | import com.otaliastudios.opengl.types.IntBuffer 8 | import com.otaliastudios.opengl.types.intBuffer 9 | import com.otaliastudios.opengl.types.ShortBuffer 10 | import com.otaliastudios.opengl.types.shortBuffer 11 | 12 | public fun FloatArray.toBuffer(): FloatBuffer = floatBuffer(size).also { 13 | it.put(this) 14 | it.flip() 15 | } 16 | 17 | public fun ShortArray.toBuffer(): ShortBuffer = shortBuffer(size).also { 18 | it.put(this) 19 | it.flip() 20 | } 21 | 22 | public fun IntArray.toBuffer(): IntBuffer = intBuffer(size).also { 23 | it.put(this) 24 | it.flip() 25 | } 26 | 27 | public fun ByteArray.toBuffer(): ByteBuffer = byteBuffer(size).also { 28 | it.put(this) 29 | it.flip() 30 | } 31 | 32 | public fun floatBufferOf(vararg elements: Float): FloatBuffer { 33 | return floatArrayOf(*elements).toBuffer() 34 | } 35 | 36 | public fun shortBufferOf(vararg elements: Short): ShortBuffer { 37 | return shortArrayOf(*elements).toBuffer() 38 | } 39 | 40 | public fun intBufferOf(vararg elements: Int): IntBuffer { 41 | return intArrayOf(*elements).toBuffer() 42 | } 43 | 44 | public fun byteBufferOf(vararg elements: Byte): ByteBuffer { 45 | return byteArrayOf(*elements).toBuffer() 46 | } 47 | 48 | @Deprecated("Do not use this.", replaceWith = ReplaceWith("FloatBuffer(size)")) 49 | public fun floatBufferOf(size: Int): FloatBuffer = floatBuffer(size) 50 | 51 | @Deprecated("Do not use this.", replaceWith = ReplaceWith("ByteBuffer(size)")) 52 | public fun byteBufferOf(size: Int): ByteBuffer = byteBuffer(size) 53 | -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/extensions/Matrix.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.extensions 2 | 3 | import com.otaliastudios.opengl.internal.matrixMakeIdentity 4 | import com.otaliastudios.opengl.internal.matrixRotate 5 | import com.otaliastudios.opengl.internal.matrixScale 6 | import com.otaliastudios.opengl.internal.matrixTranslate 7 | 8 | private fun FloatArray.checkSize() { 9 | if (size != 16) throw RuntimeException("Need a 16 values matrix.") 10 | } 11 | 12 | public fun FloatArray.makeIdentity(): FloatArray { 13 | checkSize() 14 | matrixMakeIdentity(this) 15 | return this 16 | } 17 | 18 | public fun FloatArray.clear(): FloatArray { 19 | return makeIdentity() 20 | } 21 | 22 | public fun FloatArray.translate(x: Float = 0F, y: Float = 0F, z: Float = 0F): FloatArray { 23 | checkSize() 24 | matrixTranslate(this, x, y, z) 25 | return this 26 | } 27 | 28 | @Suppress("unused") 29 | public fun FloatArray.translateX(translation: Float): FloatArray { 30 | return translate(x = translation) 31 | } 32 | 33 | @Suppress("unused") 34 | public fun FloatArray.translateY(translation: Float): FloatArray { 35 | return translate(y = translation) 36 | } 37 | 38 | @Suppress("unused") 39 | public fun FloatArray.translateZ(translation: Float): FloatArray { 40 | return translate(z = translation) 41 | } 42 | 43 | public fun FloatArray.scale(x: Float = 1F, y: Float = 1F, z: Float = 1F): FloatArray { 44 | checkSize() 45 | matrixScale(this, x, y, z) 46 | return this 47 | } 48 | 49 | public fun FloatArray.scaleX(scale: Float): FloatArray { 50 | return scale(x = scale) 51 | } 52 | 53 | public fun FloatArray.scaleY(scale: Float): FloatArray { 54 | return scale(y = scale) 55 | } 56 | 57 | @Suppress("unused") 58 | public fun FloatArray.scaleZ(scale: Float): FloatArray { 59 | return scale(z = scale) 60 | } 61 | 62 | public fun FloatArray.rotate(angle: Float, x: Float, y: Float, z: Float): FloatArray { 63 | checkSize() 64 | matrixRotate(this, angle, x, y, z) 65 | return this 66 | } 67 | 68 | public fun FloatArray.rotateX(angle: Float): FloatArray { 69 | return rotate(angle = angle, x = 1F, y = 0F, z = 0F) 70 | } 71 | 72 | 73 | public fun FloatArray.rotateY(angle: Float): FloatArray { 74 | return rotate(angle = angle, x = 0F, y = 1F, z = 0F) 75 | } 76 | 77 | 78 | public fun FloatArray.rotateZ(angle: Float): FloatArray { 79 | return rotate(angle = angle, x = 0F, y = 0F, z = 1F) 80 | } 81 | -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/geometry/IndexedPointF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public class IndexedPointF(public val index: Int, x: Float, y: Float) : PointF(x, y) -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/geometry/IndexedSegmentF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public class IndexedSegmentF(public val i: Int, public val j: Int, ix: Float, iy: Float, jx: Float, jy: Float) 4 | : SegmentF(ix, iy, jx, jy) { 5 | 6 | @Suppress("unused") 7 | public constructor(i: IndexedPointF, j: IndexedPointF) : this(i.index, j.index, i.x, i.y, j.x, j.y) 8 | 9 | override fun intersects(other: SegmentF): Boolean { 10 | if (other is IndexedSegmentF) { 11 | if (other.hasIndex(i) && other.hasIndex(j)) return true 12 | if (other.hasIndex(i) || other.hasIndex(j)) return false 13 | } 14 | return super.intersects(other) 15 | } 16 | 17 | public fun hasIndex(index: Int): Boolean { 18 | return index == i || index == j 19 | } 20 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/geometry/PointF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public expect open class PointF { 4 | public constructor() 5 | public constructor(x: Float, y: Float) 6 | public var x: Float 7 | public var y: Float 8 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/geometry/RectF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | public expect open class RectF { 4 | public constructor() 5 | public constructor(left: Float, top: Float, right: Float, bottom: Float) 6 | public var left: Float 7 | public var top: Float 8 | public var right: Float 9 | public var bottom: Float 10 | public fun set(left: Float, top: Float, right: Float, bottom: Float) 11 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/geometry/SegmentF.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.geometry 2 | 3 | import kotlin.math.* 4 | 5 | public open class SegmentF(public val ix: Float, public val iy: Float, public val jx: Float, public val jy: Float) { 6 | 7 | @Suppress("unused") 8 | public constructor(i: PointF, j: PointF) : this(i.x, i.y, j.x, j.y) 9 | 10 | public val length: Float by lazy { 11 | sqrt((ix - jx).pow(2) + (iy - jy).pow(2)) 12 | } 13 | 14 | /** 15 | * Not an easy task. Some references: 16 | * https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustLineIntersector.java 17 | * https://stackoverflow.com/questions/3838329/how-can-i-check-if-two-segments-intersect 18 | */ 19 | public open fun intersects(other: SegmentF): Boolean { 20 | // Check the envelope for a quick fail. 21 | val thisMinX = min(ix, jx) 22 | val thisMaxX = max(ix, jx) 23 | val otherMinX = min(other.ix, other.jx) 24 | val otherMaxX = max(other.ix, other.jx) 25 | if (thisMinX > otherMaxX) return false 26 | if (thisMaxX < otherMinX) return false 27 | val thisMinY = min(iy, jy) 28 | val thisMaxY = max(iy, jy) 29 | val otherMinY = min(other.iy, other.jy) 30 | val otherMaxY = max(other.iy, other.jy) 31 | if (thisMinY > otherMaxY) return false 32 | if (thisMaxY < otherMinY) return false 33 | 34 | // Compute orientations. 35 | // Fail if relative position is the same (right - right). 36 | val thisToOtherI = orientation(other.ix, other.iy) 37 | val thisToOtherJ = orientation(other.jx, other.jy) 38 | if (thisToOtherI > 0 && thisToOtherJ > 0) return false 39 | if (thisToOtherI < 0 && thisToOtherJ < 0) return false 40 | // Same for this with respect to the other 41 | val otherToThisI = other.orientation(ix, iy) 42 | val otherToThisJ = other.orientation(jx, jy) 43 | if (otherToThisI > 0 && otherToThisJ > 0) return false 44 | if (otherToThisI < 0 && otherToThisJ < 0) return false 45 | 46 | // Check for collinear segments 47 | if (thisToOtherI == 0 && thisToOtherJ == 0 && otherToThisI == 0 && otherToThisJ == 0) { 48 | // From first check we know that the two envelopes are intersecting or touching 49 | // themselves. From the check above we know that segments are collinear. 50 | // There are 4 adjacency cases. We want to return false because it's a shared point. 51 | if (thisMinX == otherMaxX && thisMinY == otherMaxY) return false 52 | if (thisMinX == otherMaxX && thisMaxY == otherMinY) return false 53 | if (thisMaxX == otherMinX && thisMinY == otherMaxY) return false 54 | if (thisMaxX == otherMinX && thisMaxY == otherMinY) return false 55 | return true 56 | } 57 | 58 | // Envelopes intersect and relative orientations are compatible: we have a single point 59 | // intersection. However we want to return false if the point is a shared endpoint. 60 | if (ix == other.ix && iy == other.iy) return false 61 | if (jx == other.jx && jy == other.jy) return false 62 | if (ix == other.jx && iy == other.jy) return false 63 | if (jx == other.ix && jy == other.iy) return false 64 | return true 65 | } 66 | 67 | /** 68 | * Returns -1 if the point is clockwise (right) from us. 69 | * Returns +1 if the point is counterclockwise (left) from us. 70 | * 0 if the point is collinear. 71 | * https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithmsDD.java#L47-L53 72 | */ 73 | public fun orientation(x: Float, y: Float): Int { 74 | return ((jx - ix) * (y - jy) - (jy - iy) * (x - jx)).sign.toInt() 75 | } 76 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/internal/annotations.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.internal 2 | 3 | @OptionalExpectation 4 | public expect annotation class AndroidJvmRequiresApi(public val value: Int, public val api: Int) -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/internal/egl.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.internal 2 | 3 | internal expect class EglSurface 4 | internal expect class EglContext 5 | internal expect class EglDisplay 6 | internal expect class EglConfig 7 | 8 | internal expect val EGL_NO_CONTEXT: EglContext 9 | internal expect val EGL_NO_DISPLAY: EglDisplay 10 | internal expect val EGL_NO_SURFACE: EglSurface 11 | internal expect val EGL_SUCCESS: Int 12 | internal expect val EGL_NONE: Int 13 | internal expect val EGL_WIDTH: Int 14 | internal expect val EGL_HEIGHT: Int 15 | internal expect val EGL_DRAW: Int 16 | internal expect val EGL_READ: Int 17 | internal expect val EGL_CONTEXT_CLIENT_VERSION: Int 18 | internal expect val EGL_OPENGL_ES2_BIT: Int 19 | internal expect val EGL_OPENGL_ES3_BIT_KHR: Int 20 | internal expect val EGL_RED_SIZE: Int 21 | internal expect val EGL_GREEN_SIZE: Int 22 | internal expect val EGL_BLUE_SIZE: Int 23 | internal expect val EGL_ALPHA_SIZE: Int 24 | internal expect val EGL_SURFACE_TYPE: Int 25 | internal expect val EGL_WINDOW_BIT: Int 26 | internal expect val EGL_PBUFFER_BIT: Int 27 | internal expect val EGL_RENDERABLE_TYPE: Int 28 | 29 | internal expect inline fun eglChooseConfig(display: EglDisplay, attributes: IntArray, configs: Array, configsSize: Int, numConfigs: IntArray): Boolean 30 | internal expect inline fun eglInitialize(display: EglDisplay, major: IntArray, minor: IntArray): Boolean 31 | internal expect inline fun eglCreateContext(display: EglDisplay, config: EglConfig, sharedContext: EglContext, attributes: IntArray): EglContext 32 | internal expect inline fun eglGetDefaultDisplay(): EglDisplay 33 | internal expect inline fun eglGetCurrentContext(): EglContext 34 | internal expect inline fun eglGetCurrentDisplay(): EglDisplay 35 | internal expect inline fun eglGetCurrentSurface(which: Int): EglSurface 36 | internal expect inline fun eglQuerySurface(display: EglDisplay, surface: EglSurface, attribute: Int, out: IntArray): Boolean 37 | internal expect inline fun eglCreateWindowSurface(display: EglDisplay, config: EglConfig, surface: Any, attributes: IntArray): EglSurface 38 | internal expect inline fun eglCreatePbufferSurface(display: EglDisplay, config: EglConfig, attributes: IntArray): EglSurface 39 | internal expect inline fun eglMakeCurrent(display: EglDisplay, draw: EglSurface, read: EglSurface, context: EglContext): Boolean 40 | internal expect inline fun eglSwapBuffers(display: EglDisplay, surface: EglSurface): Boolean 41 | internal expect inline fun eglPresentationTime(display: EglDisplay, surface: EglSurface, nanoseconds: Long): Boolean 42 | internal expect inline fun eglDestroyContext(display: EglDisplay, context: EglContext): Boolean 43 | internal expect inline fun eglDestroySurface(display: EglDisplay, surface: EglSurface): Boolean 44 | internal expect inline fun eglReleaseThread(): Boolean 45 | internal expect inline fun eglTerminate(display: EglDisplay): Boolean 46 | internal expect inline fun eglGetError(): Int -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/internal/gl.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.internal 2 | 3 | import com.otaliastudios.opengl.types.Buffer 4 | import com.otaliastudios.opengl.types.FloatBuffer 5 | 6 | internal expect val GL_TRUE: Int 7 | internal expect val GL_SHADER_STORAGE_BUFFER: UInt 8 | internal expect val GL_VIEWPORT: Int 9 | internal expect val GL_NO_ERROR: Int 10 | internal expect val GL_UNSIGNED_BYTE: UInt 11 | internal expect val GL_FLOAT: UInt 12 | internal expect val GL_RGBA: UInt 13 | internal expect val GL_TRIANGLES: UInt 14 | internal expect val GL_TRIANGLE_FAN: UInt 15 | internal expect val GL_TRIANGLE_STRIP: UInt 16 | internal expect val GL_TEXTURE0: UInt 17 | internal expect val GL_TEXTURE_EXTERNAL_OES: UInt 18 | internal expect val GL_TEXTURE_MIN_FILTER: UInt 19 | internal expect val GL_TEXTURE_MAG_FILTER: UInt 20 | internal expect val GL_TEXTURE_WRAP_S: UInt 21 | internal expect val GL_TEXTURE_WRAP_T: UInt 22 | internal expect val GL_CLAMP_TO_EDGE: Int 23 | internal expect val GL_NEAREST: Float 24 | internal expect val GL_LINEAR: Float 25 | internal expect val GL_FRAMEBUFFER: UInt 26 | internal expect val GL_FRAMEBUFFER_COMPLETE: UInt 27 | internal expect val GL_COLOR_ATTACHMENT0: UInt 28 | internal expect val GL_COMPILE_STATUS: UInt 29 | internal expect val GL_LINK_STATUS: UInt 30 | internal expect val GL_VERTEX_SHADER: UInt 31 | internal expect val GL_FRAGMENT_SHADER: UInt 32 | 33 | internal expect inline fun glGenTextures(count: Int, array: UIntArray) 34 | internal expect inline fun glDeleteTextures(count: Int, array: UIntArray) 35 | internal expect inline fun glActiveTexture(unit: UInt) 36 | internal expect inline fun glBindTexture(target: UInt, texture: UInt) 37 | internal expect inline fun glTexParameteri(target: UInt, parameter: UInt, value: Int) 38 | internal expect inline fun glTexParameterf(target: UInt, parameter: UInt, value: Float) 39 | internal expect inline fun glTexImage2D(target: UInt, level: Int, internalFormat: Int, width: Int, height: Int, border: Int, format: UInt, type: UInt, pixels: Buffer?) 40 | 41 | internal expect inline fun glGenFramebuffers(count: Int, array: UIntArray) 42 | internal expect inline fun glDeleteFramebuffers(count: Int, array: UIntArray) 43 | internal expect inline fun glBindFramebuffer(target: UInt, framebuffer: UInt) 44 | internal expect inline fun glCheckFramebufferStatus(target: UInt): UInt 45 | internal expect inline fun glFramebufferTexture2D(target: UInt, attachment: UInt, textureTarget: UInt, texture: UInt, level: Int) 46 | 47 | internal expect inline fun glGenBuffers(count: Int, array: UIntArray) 48 | internal expect inline fun glBindBuffer(target: UInt, id: UInt) 49 | internal expect inline fun glDeleteBuffers(count: Int, array: UIntArray) 50 | internal expect inline fun glBufferData(target: UInt, size: Int, usage: UInt) 51 | internal expect inline fun glBindBufferBase(target: UInt, index: UInt, id: UInt) 52 | 53 | internal expect inline fun glCreateShader(type: UInt): UInt 54 | internal expect inline fun glShaderSource(shader: UInt, source: String) 55 | internal expect inline fun glCompileShader(shader: UInt) 56 | internal expect inline fun glDeleteShader(shader: UInt) 57 | internal expect inline fun glGetShaderInfoLog(shader: UInt): String 58 | internal expect inline fun glGetShaderiv(shader: UInt, parameter: UInt, result: IntArray) 59 | 60 | internal expect inline fun glCreateProgram(): UInt 61 | internal expect inline fun glAttachShader(program: UInt, shader: UInt) 62 | internal expect inline fun glLinkProgram(program: UInt) 63 | internal expect inline fun glUseProgram(program: UInt) 64 | internal expect inline fun glDeleteProgram(program: UInt) 65 | internal expect inline fun glGetProgramInfoLog(program: UInt): String 66 | internal expect inline fun glGetProgramiv(program: UInt, parameter: UInt, result: IntArray) 67 | 68 | internal expect inline fun glEnableVertexAttribArray(array: UInt) 69 | internal expect inline fun glDisableVertexAttribArray(array: UInt) 70 | internal expect inline fun glGetAttribLocation(program: UInt, name: String): Int 71 | internal expect inline fun glGetUniformLocation(program: UInt, name: String): Int 72 | internal expect inline fun glVertexAttribPointer(index: UInt, size: Int, type: UInt, normalized: Boolean, stride: Int, pointer: Buffer) 73 | internal expect inline fun glUniformMatrix4fv(location: Int, count: Int, transpose: Boolean, value: FloatBuffer) 74 | internal expect inline fun glUniformMatrix4fv(location: Int, count: Int, transpose: Boolean, value: FloatArray) 75 | internal expect inline fun glUniform4fv(location: Int, count: Int, value: FloatBuffer) 76 | internal expect inline fun glUniform4fv(location: Int, count: Int, value: FloatArray) 77 | 78 | internal expect inline fun glGetIntegerv(parameter: UInt, array: IntArray) 79 | internal expect inline fun glGetError(): UInt 80 | 81 | internal expect inline fun glDrawArrays(mode: UInt, first: Int, count: Int) 82 | internal expect inline fun glDrawElements(mode: UInt, count: Int, type: UInt, indices: Buffer) -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/internal/misc.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.internal 2 | 3 | internal expect inline fun logv(tag: String, message: String) 4 | internal expect inline fun logi(tag: String, message: String) 5 | internal expect inline fun logw(tag: String, message: String) 6 | internal expect inline fun loge(tag: String, message: String) 7 | 8 | internal expect fun intToHexString(value: Int): String 9 | internal expect fun gluErrorString(value: Int): String 10 | 11 | internal expect fun matrixMakeIdentity(matrix: FloatArray) 12 | internal expect fun matrixTranslate(matrix: FloatArray, x: Float, y: Float, z: Float) 13 | internal expect fun matrixScale(matrix: FloatArray, x: Float, y: Float, z: Float) 14 | internal expect fun matrixRotate(matrix: FloatArray, angle: Float, x: Float, y: Float, z: Float) 15 | internal expect fun matrixClone(matrix: FloatArray): FloatArray 16 | internal expect fun matrixMultiply(result: FloatArray, left: FloatArray, right: FloatArray) 17 | -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/program/GlFlatProgram.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.program 2 | 3 | 4 | import com.otaliastudios.opengl.core.Egloo 5 | import com.otaliastudios.opengl.draw.GlDrawable 6 | import com.otaliastudios.opengl.internal.* 7 | import com.otaliastudios.opengl.internal.GL_FLOAT 8 | import com.otaliastudios.opengl.internal.glDisableVertexAttribArray 9 | import com.otaliastudios.opengl.internal.glEnableVertexAttribArray 10 | import com.otaliastudios.opengl.internal.glVertexAttribPointer 11 | 12 | public expect class GlFlatProgram : GlNativeFlatProgram 13 | 14 | /** 15 | * An [GlProgram] that uses basic flat-shading rendering, 16 | * based on FlatShadedProgram from grafika. 17 | */ 18 | @Suppress("unused") 19 | public open class GlNativeFlatProgram internal constructor(): GlProgram(VERTEX_SHADER, FRAGMENT_SHADER) { 20 | 21 | private val vertexPositionHandle = getAttribHandle("aPosition") 22 | private val vertexMvpMatrixHandle = getUniformHandle("uMVPMatrix") 23 | private val fragmentColorHandle = getUniformHandle("uColor") 24 | 25 | @Suppress("MemberVisibilityCanBePrivate") 26 | public var color: FloatArray = floatArrayOf(1F, 1F, 1F, 1F) 27 | 28 | override fun onPreDraw(drawable: GlDrawable, modelViewProjectionMatrix: FloatArray) { 29 | super.onPreDraw(drawable, modelViewProjectionMatrix) 30 | 31 | // Copy the modelViewProjectionMatrix over. 32 | glUniformMatrix4fv(vertexMvpMatrixHandle.value, 1, false, 33 | modelViewProjectionMatrix) 34 | Egloo.checkGlError("glUniformMatrix4fv") 35 | 36 | // Copy the color vector in. 37 | glUniform4fv(fragmentColorHandle.value, 1, color) 38 | Egloo.checkGlError("glUniform4fv") 39 | 40 | // Enable the "aPosition" vertex attribute. 41 | glEnableVertexAttribArray(vertexPositionHandle.uvalue) 42 | Egloo.checkGlError("glEnableVertexAttribArray") 43 | 44 | // Connect vertexBuffer to "aPosition". 45 | glVertexAttribPointer(vertexPositionHandle.uvalue, drawable.coordsPerVertex, 46 | GL_FLOAT, false, drawable.vertexStride, drawable.vertexArray) 47 | Egloo.checkGlError("glVertexAttribPointer") 48 | } 49 | 50 | override fun onPostDraw(drawable: GlDrawable) { 51 | super.onPostDraw(drawable) 52 | glDisableVertexAttribArray(vertexPositionHandle.uvalue) 53 | } 54 | 55 | private companion object { 56 | private const val VERTEX_SHADER = 57 | "" + 58 | "uniform mat4 uMVPMatrix;\n" + 59 | "attribute vec4 aPosition;\n" + 60 | "void main() {\n" + 61 | " gl_Position = uMVPMatrix * aPosition;\n" + 62 | "}\n" 63 | 64 | private const val FRAGMENT_SHADER = 65 | "" + 66 | "precision mediump float;\n" + 67 | "uniform vec4 uColor;\n" + 68 | "void main() {\n" + 69 | " gl_FragColor = uColor;\n" + 70 | "}\n" 71 | } 72 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/program/GlProgram.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.program 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.core.GlBindable 5 | import com.otaliastudios.opengl.core.use 6 | import com.otaliastudios.opengl.draw.GlDrawable 7 | import com.otaliastudios.opengl.internal.* 8 | import com.otaliastudios.opengl.internal.GL_FRAGMENT_SHADER 9 | import com.otaliastudios.opengl.internal.GL_VERTEX_SHADER 10 | import com.otaliastudios.opengl.internal.glAttachShader 11 | import com.otaliastudios.opengl.internal.glCreateProgram 12 | import kotlin.jvm.JvmOverloads 13 | import kotlin.jvm.JvmStatic 14 | 15 | /** 16 | * Base class for a program that accepts a vertex and a fragment shader in the constructor. 17 | * The program will be created automatically and released when [release] is called. 18 | * 19 | * Subclasses are required to do two things - typically, during the [onPreDraw] callback: 20 | * 1 Inspect the [GlDrawable] properties: 21 | * - [GlDrawable.vertexArray] 22 | * - [GlDrawable.coordsPerVertex] 23 | * - [GlDrawable.vertexStride] 24 | * These should be passed to the vertex shader. 25 | * 2 Pass the MVP matrix to the vertex shader as well. 26 | * 27 | * The vertex shader should then use the two to compute the gl_Position. 28 | */ 29 | public open class GlProgram protected constructor( 30 | public val handle: Int, 31 | private val ownsHandle: Boolean, 32 | private vararg val shaders: GlShader) : GlBindable { 33 | 34 | @Suppress("unused") 35 | public constructor(handle: Int) : this(handle, false) 36 | 37 | public constructor(vertexShader: String, fragmentShader: String) : this( 38 | GlShader(GL_VERTEX_SHADER.toInt(), vertexShader), 39 | GlShader(GL_FRAGMENT_SHADER.toInt(), fragmentShader)) 40 | 41 | public constructor(vararg shaders: GlShader) 42 | : this(create(*shaders), true, *shaders) 43 | 44 | private var isReleased = false 45 | 46 | @Suppress("unused") 47 | public open fun release() { 48 | if (!isReleased) { 49 | if (ownsHandle) glDeleteProgram(handle.toUInt()) 50 | shaders.forEach { it.release() } 51 | isReleased = true 52 | } 53 | } 54 | 55 | override fun bind() { 56 | glUseProgram(handle.toUInt()) 57 | Egloo.checkGlError("glUseProgram") 58 | } 59 | 60 | override fun unbind() { 61 | glUseProgram(0u) 62 | } 63 | 64 | // TODO move draw API to GlScene or somewhere else. 65 | // I like the program as an object that manages the single shaders capabilities, 66 | // but not quite as the drawer element. It could be a compute program for instance. 67 | @JvmOverloads 68 | public fun draw(drawable: GlDrawable, 69 | modelViewProjectionMatrix: FloatArray = drawable.modelMatrix) { 70 | Egloo.checkGlError("draw start") 71 | use { 72 | onPreDraw(drawable, modelViewProjectionMatrix) 73 | onDraw(drawable) 74 | onPostDraw(drawable) 75 | } 76 | Egloo.checkGlError("draw end") 77 | } 78 | 79 | public open fun onPreDraw(drawable: GlDrawable, modelViewProjectionMatrix: FloatArray) {} 80 | 81 | public open fun onDraw(drawable: GlDrawable) { 82 | drawable.draw() 83 | } 84 | 85 | public open fun onPostDraw(drawable: GlDrawable) {} 86 | 87 | protected fun getAttribHandle(name: String): GlProgramLocation = GlProgramLocation.getAttrib(handle, name) 88 | 89 | protected fun getUniformHandle(name: String): GlProgramLocation = GlProgramLocation.getUniform(handle, name) 90 | 91 | public companion object { 92 | 93 | @Deprecated(message = "Use create(GlShader) signature.") 94 | @JvmStatic 95 | public fun create(vertexShaderSource: String, fragmentShaderSource: String): Int { 96 | return create(GlShader(GL_VERTEX_SHADER.toInt(), vertexShaderSource), 97 | GlShader(GL_FRAGMENT_SHADER.toInt(), fragmentShaderSource)) 98 | } 99 | 100 | @JvmStatic 101 | public fun create(vararg shaders: GlShader): Int { 102 | val program = glCreateProgram() 103 | Egloo.checkGlError("glCreateProgram") 104 | if (program == 0u) { 105 | throw RuntimeException("Could not create program") 106 | } 107 | shaders.forEach { 108 | glAttachShader(program, it.id.toUInt()) 109 | Egloo.checkGlError("glAttachShader") 110 | } 111 | glLinkProgram(program) 112 | val linkStatus = IntArray(1) 113 | glGetProgramiv(program, GL_LINK_STATUS, linkStatus) 114 | if (linkStatus[0] != GL_TRUE) { 115 | val message = "Could not link program: " + glGetProgramInfoLog(program) 116 | glDeleteProgram(program) 117 | throw RuntimeException(message) 118 | } 119 | return program.toInt() 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/program/GlProgramLocation.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.program 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.internal.glGetAttribLocation 5 | import com.otaliastudios.opengl.internal.glGetUniformLocation 6 | 7 | /** 8 | * A simple helper class for holding handles to program variables. 9 | */ 10 | public class GlProgramLocation private constructor( 11 | program: Int, 12 | type: Type, 13 | @Suppress("CanBeParameter") public val name: String 14 | ) { 15 | 16 | private enum class Type { ATTRIB, UNIFORM } 17 | 18 | public val value: Int 19 | init { 20 | value = when (type) { 21 | Type.ATTRIB -> glGetAttribLocation(program.toUInt(), name) 22 | Type.UNIFORM -> glGetUniformLocation(program.toUInt(), name) 23 | } 24 | Egloo.checkGlProgramLocation(value, name) 25 | } 26 | 27 | internal val uvalue = value.toUInt() 28 | 29 | public companion object { 30 | public fun getAttrib(program: Int, name: String): GlProgramLocation = GlProgramLocation(program, Type.ATTRIB, name) 31 | public fun getUniform(program: Int, name: String): GlProgramLocation = GlProgramLocation(program, Type.UNIFORM, name) 32 | } 33 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/program/GlShader.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.program 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.internal.* 5 | 6 | public class GlShader(public val type: Int, public val id: Int) { 7 | 8 | public constructor(type: Int, source: String) : this(type, compile(type, source)) 9 | 10 | public fun release() { 11 | glDeleteShader(id.toUInt()) 12 | } 13 | 14 | private companion object { 15 | private fun compile(type: Int, source: String): Int { 16 | val shader = glCreateShader(type.toUInt()) 17 | Egloo.checkGlError("glCreateShader type=$type") 18 | glShaderSource(shader, source) 19 | glCompileShader(shader) 20 | val compiled = IntArray(1) 21 | glGetShaderiv(shader, GL_COMPILE_STATUS, compiled) 22 | if (compiled[0] == 0) { 23 | val message = "Could not compile shader $type: '${glGetShaderInfoLog(shader)}' source: $source" 24 | glDeleteShader(shader) 25 | throw RuntimeException(message) 26 | } 27 | return shader.toInt() 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/scene/GlScene.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.scene 2 | 3 | import com.otaliastudios.opengl.core.Egloo 4 | import com.otaliastudios.opengl.draw.GlDrawable 5 | import com.otaliastudios.opengl.program.GlProgram 6 | import com.otaliastudios.opengl.core.GlViewportAware 7 | import com.otaliastudios.opengl.internal.matrixClone 8 | import com.otaliastudios.opengl.internal.matrixMultiply 9 | 10 | /** 11 | * Scenes can be to draw [GlDrawable]s through [GlProgram]s. 12 | * 13 | * The advantage is that they contain information about the [projectionMatrix] and the [viewMatrix], 14 | * both of which can be accessed and modified and held by this single object. 15 | * 16 | * The [GlScene] object will combine these two with the drawables [GlDrawable.modelMatrix] 17 | * and pass the resulting model-view-projection matrix to the program. 18 | */ 19 | @Suppress("unused") 20 | public open class GlScene : GlViewportAware() { 21 | 22 | @Suppress("MemberVisibilityCanBePrivate") 23 | public val projectionMatrix: FloatArray = matrixClone(Egloo.IDENTITY_MATRIX) 24 | 25 | @Suppress("MemberVisibilityCanBePrivate") 26 | public val viewMatrix: FloatArray = matrixClone(Egloo.IDENTITY_MATRIX) 27 | 28 | private val modelViewMatrix = FloatArray(16) 29 | private val modelViewProjectionMatrix = FloatArray(16) 30 | 31 | private fun computeModelViewProjectionMatrix(drawable: GlDrawable) { 32 | matrixMultiply(modelViewMatrix, viewMatrix, drawable.modelMatrix) 33 | matrixMultiply(modelViewProjectionMatrix, projectionMatrix, modelViewMatrix) 34 | } 35 | 36 | public fun draw(program: GlProgram, drawable: GlDrawable) { 37 | ensureViewportSize() 38 | drawable.setViewportSize(viewportWidth, viewportHeight) 39 | 40 | computeModelViewProjectionMatrix(drawable) 41 | program.draw(drawable, modelViewProjectionMatrix) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/surface/EglOffscreenSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | 4 | import com.otaliastudios.opengl.core.EglCore 5 | 6 | 7 | /** 8 | * A pbuffer EGL surface. 9 | */ 10 | @Suppress("unused") 11 | public open class EglOffscreenSurface(eglCore: EglCore, width: Int, height: Int) 12 | : EglSurface(eglCore, eglCore.createOffscreenSurface(width, height)) { 13 | init { 14 | // Cache this values 15 | setWidth(width) 16 | setHeight(height) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/surface/EglSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | import com.otaliastudios.opengl.core.EglCore 4 | import com.otaliastudios.opengl.internal.EGL_HEIGHT 5 | import com.otaliastudios.opengl.internal.EGL_NO_SURFACE 6 | import com.otaliastudios.opengl.internal.EGL_WIDTH 7 | import com.otaliastudios.opengl.internal.EglSurface 8 | 9 | public expect abstract class EglSurface internal constructor(eglCore: EglCore, eglSurface: EglSurface) : EglNativeSurface 10 | 11 | public open class EglNativeSurface internal constructor( 12 | internal var eglCore: EglCore, 13 | internal var eglSurface: EglSurface) { 14 | 15 | private var width = -1 16 | private var height = -1 17 | 18 | /** 19 | * Can be called by subclasses whose width is guaranteed to never change, 20 | * so we can cache this value. For window surfaces, this should not be called. 21 | */ 22 | @Suppress("unused") 23 | protected fun setWidth(width: Int) { 24 | this.width = width 25 | } 26 | 27 | /** 28 | * Can be called by subclasses whose height is guaranteed to never change, 29 | * so we can cache this value. For window surfaces, this should not be called. 30 | */ 31 | @Suppress("unused") 32 | protected fun setHeight(height: Int) { 33 | this.height = height 34 | } 35 | 36 | /** 37 | * Returns the surface's width, in pixels. 38 | * 39 | * If this is called on a window surface, and the underlying surface is in the process 40 | * of changing size, we may not see the new size right away (e.g. in the "surfaceChanged" 41 | * callback). The size should match after the next buffer swap. 42 | */ 43 | @Suppress("MemberVisibilityCanBePrivate") 44 | public fun getWidth(): Int { 45 | return if (width < 0) { 46 | eglCore.querySurface(eglSurface, EGL_WIDTH) 47 | } else { 48 | width 49 | } 50 | } 51 | 52 | /** 53 | * Returns the surface's height, in pixels. 54 | */ 55 | @Suppress("MemberVisibilityCanBePrivate") 56 | public fun getHeight(): Int { 57 | return if (height < 0) { 58 | eglCore.querySurface(eglSurface, EGL_HEIGHT) 59 | } else { 60 | height 61 | } 62 | } 63 | 64 | /** 65 | * Release the EGL surface. 66 | */ 67 | public open fun release() { 68 | eglCore.releaseSurface(eglSurface) 69 | eglSurface = EGL_NO_SURFACE 70 | height = -1 71 | width = -1 72 | } 73 | 74 | /** 75 | * Whether this surface is current on the 76 | * attached [EglCore]. 77 | */ 78 | @Suppress("MemberVisibilityCanBePrivate") 79 | public fun isCurrent(): Boolean { 80 | return eglCore.isSurfaceCurrent(eglSurface) 81 | } 82 | 83 | /** 84 | * Makes our EGL context and surface current. 85 | */ 86 | @Suppress("unused") 87 | public fun makeCurrent() { 88 | eglCore.makeSurfaceCurrent(eglSurface) 89 | } 90 | 91 | /** 92 | * Makes no surface current for the attached [eglCore]. 93 | */ 94 | @Suppress("unused") 95 | public fun makeNothingCurrent() { 96 | eglCore.makeCurrent() 97 | } 98 | 99 | /** 100 | * Sends the presentation time stamp to EGL. 101 | * [nsecs] is the timestamp in nanoseconds. 102 | */ 103 | @Suppress("unused") 104 | public fun setPresentationTime(nsecs: Long) { 105 | eglCore.setSurfacePresentationTime(eglSurface, nsecs) 106 | } 107 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/surface/EglWindowSurface.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.surface 2 | 3 | import com.otaliastudios.opengl.core.EglCore 4 | import com.otaliastudios.opengl.internal.EGL_HEIGHT 5 | import com.otaliastudios.opengl.internal.EGL_NO_SURFACE 6 | import com.otaliastudios.opengl.internal.EGL_WIDTH 7 | import com.otaliastudios.opengl.internal.EglSurface 8 | 9 | public expect open class EglWindowSurface : EglNativeWindowSurface 10 | 11 | public abstract class EglNativeWindowSurface internal constructor( 12 | eglCore: EglCore, 13 | eglSurface: EglSurface 14 | ) : com.otaliastudios.opengl.surface.EglSurface(eglCore, eglSurface) { 15 | 16 | /** 17 | * Calls eglSwapBuffers. Use this to "publish" the current frame. 18 | * Returns false on failure. 19 | */ 20 | @Suppress("unused") 21 | public fun swapBuffers(): Boolean { 22 | // This makes no sense for offscreen surfaces 23 | return eglCore.swapSurfaceBuffers(eglSurface) 24 | } 25 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/texture/GlFramebuffer.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package com.otaliastudios.opengl.texture 4 | 5 | import com.otaliastudios.opengl.core.GlBindable 6 | import com.otaliastudios.opengl.core.Egloo 7 | import com.otaliastudios.opengl.core.use 8 | import com.otaliastudios.opengl.internal.* 9 | import kotlin.jvm.JvmOverloads 10 | 11 | public class GlFramebuffer(id: Int? = null) : GlBindable { 12 | 13 | public val id: Int = id ?: run { 14 | val array = UIntArray(1) 15 | glGenFramebuffers(1, array) 16 | Egloo.checkGlError("glGenFramebuffers") 17 | array[0].toInt() 18 | } 19 | 20 | @JvmOverloads 21 | public fun attach(texture: GlTexture, attachment: Int = GL_COLOR_ATTACHMENT0.toInt()) { 22 | use { 23 | glFramebufferTexture2D(GL_FRAMEBUFFER, attachment.toUInt(), 24 | texture.target.toUInt(), texture.id.toUInt(), 0) 25 | val status = glCheckFramebufferStatus(GL_FRAMEBUFFER) 26 | if (status != GL_FRAMEBUFFER_COMPLETE) { 27 | throw RuntimeException("Invalid framebuffer generation. Error:$status") 28 | } 29 | } 30 | } 31 | 32 | override fun bind() { 33 | glBindFramebuffer(GL_FRAMEBUFFER, id.toUInt()) 34 | } 35 | 36 | override fun unbind() { 37 | glBindFramebuffer(GL_FRAMEBUFFER, 0u) 38 | } 39 | 40 | public fun release() { 41 | glDeleteFramebuffers(1, uintArrayOf(id.toUInt())) 42 | } 43 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/texture/GlTexture.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.texture 2 | 3 | import com.otaliastudios.opengl.core.GlBindable 4 | import com.otaliastudios.opengl.core.Egloo 5 | import com.otaliastudios.opengl.core.use 6 | import com.otaliastudios.opengl.internal.* 7 | import kotlin.jvm.JvmOverloads 8 | 9 | public class GlTexture private constructor( 10 | public val unit: Int, 11 | public val target: Int, 12 | id: Int?, 13 | public val width: Int?, 14 | public val height: Int?, 15 | public val format: Int?, 16 | internalFormat: Int?, 17 | public val type: Int?) : GlBindable { 18 | 19 | @JvmOverloads 20 | public constructor(unit: Int = GL_TEXTURE0.toInt(), target: Int = GL_TEXTURE_EXTERNAL_OES.toInt(), id: Int? = null) 21 | : this(unit, target, id, null, null, null, null, null) 22 | 23 | @Suppress("unused") 24 | @JvmOverloads 25 | public constructor(unit: Int, target: Int, width: Int, height: Int, 26 | format: Int = GL_RGBA.toInt(), 27 | internalFormat: Int = format, 28 | type: Int = GL_UNSIGNED_BYTE.toInt()) 29 | : this(unit, target, null, width, height, format, internalFormat, type) 30 | 31 | public val id: Int = id ?: run { 32 | val textures = UIntArray(1) 33 | glGenTextures(1, textures) 34 | Egloo.checkGlError("glGenTextures") 35 | textures[0].toInt() 36 | } 37 | 38 | init { 39 | if (id == null) { 40 | use { 41 | if (width != null && height != null 42 | && format != null 43 | && internalFormat != null 44 | && type != null) { 45 | glTexImage2D(target.toUInt(), 0, internalFormat, width, height, 46 | 0, format.toUInt(), type.toUInt(), null) 47 | } 48 | glTexParameterf(target.toUInt(), GL_TEXTURE_MIN_FILTER, GL_NEAREST) 49 | glTexParameterf(target.toUInt(), GL_TEXTURE_MAG_FILTER, GL_LINEAR) 50 | glTexParameteri(target.toUInt(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 51 | glTexParameteri(target.toUInt(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 52 | Egloo.checkGlError("glTexParameter") 53 | } 54 | } 55 | } 56 | 57 | override fun bind() { 58 | glActiveTexture(unit.toUInt()) 59 | glBindTexture(target.toUInt(), id.toUInt()) 60 | Egloo.checkGlError("bind") 61 | } 62 | 63 | override fun unbind() { 64 | glBindTexture(target.toUInt(), 0.toUInt()) 65 | glActiveTexture(GL_TEXTURE0) 66 | Egloo.checkGlError("unbind") 67 | } 68 | 69 | public fun release() { 70 | glDeleteTextures(1, uintArrayOf(id.toUInt())) 71 | } 72 | } -------------------------------------------------------------------------------- /library/src/commonMain/kotlin/com/otaliastudios/opengl/types/buffers.kt: -------------------------------------------------------------------------------- 1 | package com.otaliastudios.opengl.types 2 | 3 | public expect abstract class Buffer { 4 | public fun remaining(): Int 5 | public fun hasRemaining(): Boolean 6 | public fun capacity(): Int 7 | public fun position(): Int 8 | public fun position(position: Int): Buffer 9 | public fun limit(): Int 10 | public fun limit(limit: Int): Buffer 11 | public fun clear(): Buffer 12 | public fun rewind(): Buffer 13 | public fun flip(): Buffer 14 | } 15 | 16 | // TODO should this be public and be applied to more structures? 17 | internal interface Disposable { 18 | fun dispose() 19 | } 20 | 21 | public fun Buffer.dispose() { 22 | if (this is Disposable) this.dispose() 23 | } 24 | 25 | public expect abstract class FloatBuffer : Buffer { 26 | public abstract fun get(): Float 27 | public abstract fun get(index: Int): Float 28 | public abstract fun put(value: Float): FloatBuffer 29 | public fun put(values: FloatArray): FloatBuffer 30 | } 31 | 32 | public expect abstract class ByteBuffer : Buffer { 33 | public abstract fun get(): Byte 34 | public abstract fun get(index: Int): Byte 35 | public abstract fun put(value: Byte): ByteBuffer 36 | public fun put(values: ByteArray): ByteBuffer 37 | } 38 | 39 | public expect abstract class ShortBuffer : Buffer { 40 | public abstract fun get(): Short 41 | public abstract fun get(index: Int): Short 42 | public abstract fun put(value: Short): ShortBuffer 43 | public fun put(values: ShortArray): ShortBuffer 44 | } 45 | 46 | public expect abstract class IntBuffer : Buffer { 47 | public abstract fun get(): Int 48 | public abstract fun get(index: Int): Int 49 | public abstract fun put(value: Int): IntBuffer 50 | public fun put(values: IntArray): IntBuffer 51 | } 52 | 53 | // Would have liked this to be upper case, but compiler complains 54 | public expect fun floatBuffer(size: Int): FloatBuffer 55 | public expect fun byteBuffer(size: Int): ByteBuffer 56 | public expect fun shortBuffer(size: Int): ShortBuffer 57 | public expect fun intBuffer(size: Int): IntBuffer -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("GRADLE_METADATA") // < required by multiplatform for smart maven klib publications 2 | include ':library', ':demo' 3 | --------------------------------------------------------------------------------