Skip to content

Commit

Permalink
Various improvements (#14)
Browse files Browse the repository at this point in the history
* Uniform method names for GlRect and GlRoundRect

* Split GlTextureProgram and GlSimpleTextureProgram

* Refactor texture programs

* Create GlTexture class

* Make texture variable

* Refactoring and new features

* Fix GlTexture constructors

* Create GlDrawable.vertexArrayVersion for optimizations

* Create Gl2dMesh

* Improve texture program

* Create geometry package

* Small changes
  • Loading branch information
natario1 authored Jan 25, 2020
1 parent babec70 commit 3bfc62f
Show file tree
Hide file tree
Showing 24 changed files with 757 additions and 216 deletions.
62 changes: 43 additions & 19 deletions demo/src/main/java/com/otaliastudios/opengl/demo/VideoActivity.kt
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
package com.otaliastudios.opengl.demo

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.PointF
import android.graphics.RectF
import android.graphics.SurfaceTexture
import android.net.Uri
import android.opengl.GLES11Ext
import android.opengl.GLES20
import android.opengl.GLSurfaceView
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.view.*
import android.widget.Toast
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.exoplayer2.ExoPlayerFactory
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import com.google.android.exoplayer2.video.VideoListener
import com.otaliastudios.opengl.core.EglConfigChooser
import com.otaliastudios.opengl.core.EglContextFactory
import com.otaliastudios.opengl.core.EglCore
import com.otaliastudios.opengl.draw.*
import com.otaliastudios.opengl.program.GlFlatProgram
import com.otaliastudios.opengl.extensions.makeIdentity
import com.otaliastudios.opengl.extensions.scaleX
import com.otaliastudios.opengl.extensions.scaleY
import com.otaliastudios.opengl.program.GlTextureProgram
import com.otaliastudios.opengl.scene.GlScene
import com.otaliastudios.opengl.surface.EglWindowSurface
import com.otaliastudios.opengl.texture.GlTexture
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
import kotlin.math.roundToInt

class VideoActivity : AppCompatActivity(), GLSurfaceView.Renderer {

Expand All @@ -45,7 +42,26 @@ class VideoActivity : AppCompatActivity(), GLSurfaceView.Renderer {
private val scene = GlScene()
private val glRect = GlRect()
private val glRoundRect = GlRoundRect().apply { setCornersPx(200) }
private var glDrawable: GlDrawable = glRect
private val glMesh = Gl2dMesh().also {
it.setPoints(listOf(
PointF(0F, 0F),
PointF(0F, 1F),
PointF(1F, 0F),
PointF(0F, -1F),
PointF(-1F, 0F),
PointF(0.9F, 0.7F),
PointF(0.7F, 0.9F),
PointF(-0.9F, -0.7F),
PointF(-0.7F, -0.9F),
PointF(0.9F, -0.7F),
PointF(0.7F, -0.9F),
PointF(-0.9F, 0.7F),
PointF(-0.7F, 0.9F)
))
}

private val glDrawables = listOf(glRect, glRoundRect, glMesh)
private var glDrawable = 0
private lateinit var player: SimpleExoPlayer

private var videoWidth = -1
Expand Down Expand Up @@ -96,18 +112,23 @@ class VideoActivity : AppCompatActivity(), GLSurfaceView.Renderer {

// On touch, change the current drawable.
glSurfaceView.setOnTouchListener { v, event ->
glDrawable = if (glDrawable == glRect) glRoundRect else glRect
glDrawable++
if (glDrawable > glDrawables.lastIndex) {
glDrawable = 0
}
false
}
Toast.makeText(this, "Touch the screen to change shape.", Toast.LENGTH_LONG).show()
}

override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
// Configure GL
val glTexture = GlTexture()
glTextureProgram = GlTextureProgram()
glTextureProgram!!.texture = glTexture

// Configure the player
surfaceTexture = SurfaceTexture(glTextureProgram!!.textureId)
surfaceTexture = SurfaceTexture(glTexture.id)
surfaceTexture!!.setOnFrameAvailableListener {
glSurfaceView.requestRender()
}
Expand All @@ -131,7 +152,7 @@ class VideoActivity : AppCompatActivity(), GLSurfaceView.Renderer {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
texture.updateTexImage()
texture.getTransformMatrix(program.textureTransform)
scene.draw(program, glDrawable)
scene.draw(program, glDrawables[glDrawable])
}

private fun onSurfaceDestroyed() {
Expand All @@ -149,24 +170,27 @@ class VideoActivity : AppCompatActivity(), GLSurfaceView.Renderer {
if (surfaceWidth == -1 || surfaceHeight == -1) return
val videoRatio = videoWidth.toFloat() / videoHeight
val surfaceRatio = surfaceWidth.toFloat() / surfaceHeight
val rect: RectF
val viewport: RectF
if (videoRatio > surfaceRatio) {
// Video is wider. We should collapse height.
val surfaceRealHeight = surfaceWidth / videoRatio
val surfaceRealHeightEgloo = surfaceRealHeight / surfaceHeight * 2
rect = RectF(-1F, surfaceRealHeightEgloo / 2F,
viewport = RectF(-1F, surfaceRealHeightEgloo / 2F,
1F, -surfaceRealHeightEgloo / 2F)
} else if (videoRatio < surfaceRatio) {
// Video is taller. We should collapse width
val surfaceRealWidth = surfaceHeight * videoRatio
val surfaceRealWidthEgloo = surfaceRealWidth / surfaceWidth * 2
rect = RectF(-surfaceRealWidthEgloo / 2F, 1F,
viewport = RectF(-surfaceRealWidthEgloo / 2F, 1F,
surfaceRealWidthEgloo / 2F, -1F)
} else {
rect = RectF(-1F, 1F, 1F, -1F)
viewport = RectF(-1F, 1F, 1F, -1F)
}
glRect.setVertexArray(rect)
glRoundRect.setRect(rect)
glRect.setRect(viewport)
glRoundRect.setRect(viewport)
glMesh.modelMatrix.makeIdentity()
glMesh.modelMatrix.scaleX(viewport.width() / 2F)
glMesh.modelMatrix.scaleY(-viewport.height() / 2F)
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
Expand Down
40 changes: 34 additions & 6 deletions docs/_docs/programs.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,51 @@ program.draw(drawable2)

### Texture program

The `GlTextureProgram` program can be used to render texture frames. It will automatically generate
a `textureId` for you, which is meant to be used to create `SurfaceTexture`s. This means that it
uses the `GLES11Ext.GL_TEXTURE_EXTERNAL_OES` texture target.
The `GlTextureProgram` program can be used to render textures. To use it, you will need to create
a `GlTexture` first and call `program.texture = texture`: this will make sure that texture is
correctly bound before rendering. See [textures](textures) to learn about this object.

The texture program has built-in support for:
- 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.
- Apply a matrix transformation to the texture by modifying `GlTextureProgram.textureTransform`

See the sample below:

```kotlin
val texture = GlTexture()
val program = GlTextureProgram()
val programTexture = SurfaceTexture(program.textureId)
program.texture = texture
val surfaceTexture = SurfaceTexture(texture.id)

// Pass this surfaceTexture to Camera, for example
val camera: android.hardware.Camera = openCamera()
camera.setPreviewTexture(programTexture)
camera.setPreviewTexture(surfaceTexture)

// Now the program texture receives the camera frames
// And we can render them using the program
val rect = GlRect() // Draw the full frame
programTexture.getTransformMatrix(program.textureTransform)
surfaceTexture.getTransformMatrix(program.textureTransform)
program.draw(rect)
```

If, for some reason, you do not want to call `program.texture = texture` (which gives the program
the ownership of the texture), you can still call `texture.bind()` and `texture.unbind()` manually:

```kotlin
// Option 1
texture.bind()
program.draw(drawable)
texture.unbind()

// Option 2
texture.use {
program.draw(drawable)
}

// Option 3
program.texture = texture
program.draw(drawable)
```

These options are equivalent. Note, however, that when passing the texture to `GlTextureProgram`,
the texture will automatically be released when you call `program.release()`.
2 changes: 1 addition & 1 deletion docs/_docs/scenes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: page
title: "Scenes"
description: "How to control the view and projection matrix"
order: 4
order: 5
disqus: 1
---

Expand Down
44 changes: 44 additions & 0 deletions docs/_docs/textures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
layout: page
title: "Textures"
description: "APIs for textures and framebuffer objects"
order: 4
disqus: 1
---

### The GlTexture object

The `GlTexture` object will generate and allocate an OpenGL texture. The texture can then be used to
read from it, render into it, attach to a framebuffer object and much more.

By default, the `GlTexture` is created with the `GLES11Ext.GL_TEXTURE_EXTERNAL_OES` texture target.
This means that it is suitable for using it as the output of a `SurfaceTexture`:

```kotlin
val texture = GlTexture()
val surfaceTexture = SurfaceTexture(texture.id)
// Anytime the surface texture is passed new data, its contents are put into our GlTexture
// For example, we can receive the stream of video frames:
videoPlayer.setOutputSurface(surfaceTexture)
videoPlayer.play()
```

However, different targets can be specified within the texture constructor. When using `GLES20.GL_TEXTURE_2D`,
you will probably want to use the constructor that accepts a `width` and `height` so that the buffer
is actually allocated.

> To render texture contents, just use a `GlTextureProgram` and pass the texture to it.
See the [programs](programs#texture-program) page for details.

### The GlFramebuffer object

The `GlFramebuffer` object will generate an OpenGL framebuffer object.
You can attach textures to it by using `GlFramebuffer.attach()`, like so:

```kotlin
val texture = GlTexture()
val fbo = GlFramebuffer()
fbo.attach(texture, GLES20.GL_COLOR_ATTACHMENT0)
```

The attached textures will now receive the framebuffer contents.
51 changes: 22 additions & 29 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ dependencies {
}

task sourcesJar(type: Jar) {
classifier = 'sources'
archiveClassifier.set('sources')
from android.sourceSets.main.java.srcDirs
}

task dokkaJar(type: Jar, dependsOn: dokka) {
classifier = 'javadoc'
archiveClassifier.set('javadoc')
from dokka.outputDirectory
}

Expand Down Expand Up @@ -85,41 +85,34 @@ install.repositories.mavenInstaller.pom.project {

def bintrayUser
def bintrayKey
def hasBintray = false
if (System.getenv('TRAVIS') == 'true') {
if (System.getenv('TRAVIS_SECURE_ENV_VARS') == 'true') {
bintrayUser = System.getenv("BINTRAY_USER")
bintrayKey = System.getenv("BINTRAY_KEY")
hasBintray = true
}
bintrayUser = System.getenv("BINTRAY_USER")
bintrayKey = System.getenv("BINTRAY_KEY")
} else {
Properties props = new Properties()
props.load(project.rootProject.file('local.properties').newDataInputStream())
bintrayUser = props.getProperty('bintray.user')
bintrayKey = props.get('bintray.key')
hasBintray = true
}

if (hasBintray) {
bintray {
// https://github.com/bintray/gradle-bintray-plugin
user = bintrayUser
key = bintrayKey
configurations = ['archives']
publish = true
override = true
pkg {
repo = 'android'
name = libName
licenses = [libLicenseName]
vcsUrl = githubGit
desc = libDescription
version {
name = libVersion
desc = libName + ' v' + libVersion
released = new Date()
vcsTag = 'v' + libVersion
}
bintray {
// https://github.com/bintray/gradle-bintray-plugin
user = bintrayUser
key = bintrayKey
configurations = ['archives']
publish = true
override = true
pkg {
repo = 'android'
name = libName
licenses = [libLicenseName]
vcsUrl = githubGit
desc = libDescription
version {
name = libVersion
desc = libName + ' v' + libVersion
released = new Date()
vcsTag = 'v' + libVersion
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ object EglContextFactory {
@JvmField
val GLES3: GLSurfaceView.EGLContextFactory = Factory(3)


private class Factory(private val version: Int) : GLSurfaceView.EGLContextFactory {
override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: EGLConfig): EGLContext {
val attributes = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object Egloo {
/**
* Identify matrix for general use.
*/
@JvmStatic
@JvmField
val IDENTITY_MATRIX = FloatArray(16).apply {
makeIdentity()
}
Expand Down
12 changes: 12 additions & 0 deletions library/src/main/java/com/otaliastudios/opengl/core/GlBindable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.otaliastudios.opengl.core

interface GlBindable {
fun bind()
fun unbind()
}

fun GlBindable.use(block: () -> Unit) {
bind()
block()
unbind()
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.otaliastudios.opengl.viewport
package com.otaliastudios.opengl.core

import android.opengl.GLES20

Expand Down
Loading

0 comments on commit 3bfc62f

Please sign in to comment.