├── .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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.run/deploySonatypeSnapshots.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.run/publishAllBintray.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 | [](https://github.com/natario1/Egloo/actions)
2 | [](https://github.com/natario1/Egloo/releases)
3 | [](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 | Please enable JavaScript to view the comments powered by Disqus.
13 |
--------------------------------------------------------------------------------
/docs/_includes/footer.html:
--------------------------------------------------------------------------------
1 |
2 | View on GitHub or become a sponsor !
3 | Made by {{ site.author.name }}
4 |
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 |
12 |
13 |
14 |
15 |
16 |
latest: v{{ site.github_version }}
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/docs/_includes/navigation.html:
--------------------------------------------------------------------------------
1 |
2 | Home
3 | {% for collection in site.collections %}
4 | {% if collection.label != "posts" %}
5 | {{ collection.name }}
6 |
7 | {% assign docs = (collection.docs | sort: "order") %}
8 | {% for doc in docs %}
9 |
10 | {{ doc.title }}
11 |
12 | {% endfor %}
13 |
14 |
15 | {% endif %}
16 | {% endfor %}
17 |
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 |
22 |
23 |
24 | {% assign col = 12 | divided_by: site.screenshots.size %}
25 | {% for screenshot in site.screenshots %}
26 |
27 |
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 |
--------------------------------------------------------------------------------