GLFM é uma API C para desenvolvimento de aplicativos móveis com OpenGL ES. É amplamente inspirado no GLFW.
GLFM roda em iOS 9, tvOS 9, Android 4.1 (API 16) e WebGL 1.0 (via Emscripten).
Além disso, o GLFM oferece suporte Metal em iOS e tvOS.
iOS | tvOS | Android | Rede | |
---|---|---|---|---|
OpenGL ES 2, OpenGL ES 3 | ✔️ | ✔️ | ✔️ | ✔️ |
Metal | ✔️ | ✔️ | N / D | N / D |
Retina / alto DPI | ✔️ | ✔️ | ✔️ | ✔️ |
Orientação do dispositivo | ✔️ | N / D | ✔️ | |
Eventos de toque | ✔️ | ✔️ | ✔️ | ✔️ |
Eventos de passar o mouse | ✔️ 1 | ✔️ | ||
Eventos da roda do mouse | ✔️ | |||
Estilo do cursor do mouse | ✔️ 1 | ✔️ | ||
Eventos de código-chave | ✔️ 2 | ✔️ | ✔️ | ✔️ |
Principais eventos repetidos | ✔️ | ✔️ | ||
Eventos de entrada de caracteres | ✔️ | ✔️3 | ✔️ | ✔️ |
Teclado virtual | ✔️ | ✔️ | ||
Eventos de visibilidade do teclado virtual | ✔️ | ✔️ | ||
Acelerômetro, magnetômetro, giroscópio, rotação do dispositivo | ✔️ | N / D | ✔️ | |
Feedback tátil | ✔️ 4 | N / D | ✔️ | |
Área de transferência | ✔️ | N / D | ✔️ | ✔️ |
Inserções do Chrome ("área segura") | ✔️ 5 | ✔️ | ✔️ | |
Inserções do Chrome alteraram eventos | ✔️ 5 | ✔️ | ✔️ | |
Eventos de foco | ✔️ | ✔️ | ✔️ | ✔️ |
Redimensionar eventos | ✔️ | ✔️ | ✔️ | ✔️ |
Eventos de aviso de memória | ✔️ | ✔️ | ✔️ | |
Eventos de perda de contexto OpenGL (superfície destruída) | ✔️ | ✔️ | ✔️ | ✔️ |
1. Somente iPad. Requer iOS 13.4 ou mais recente
2. Requer iOS/tvOS 13.4 ou mais recente
3. Requer tvOS 13.4 ou mais recente
4. Requer iOS 13 ou mais recente
5. Requer iOS/tvOS 11 ou mais recente
Além disso, há suporte preliminar para macOS com OpenGL 3.2. A versão macOS é útil para fins de desenvolvimento, mas não tem qualidade de lançamento. Não há função para definir o tamanho da janela, por exemplo.
O GLFM tem escopo limitado e não foi projetado para fornecer tudo o que é necessário para um aplicativo. Por exemplo, o GLFM não fornece (e nunca fornecerá) o seguinte:
Em vez disso, o GLFM pode ser usado com outras bibliotecas multiplataforma que fornecem o que um aplicativo precisa.
Um arquivo CMakeLists.txt
é fornecido por conveniência, embora o CMake não seja obrigatório.
Sem CMake:
include
e src
) ao seu projeto.void glfmMain(GLFMDisplay *display)
em um arquivo C/C++. Para compilações de versão, defina NDEBUG
para remover instruções de log supérfluas. NDEBUG
é definido automaticamente para versões de lançamento no Android Studio, mas não no Xcode.
Este exemplo inicializa a exibição em glfmMain()
e desenha um triângulo em onDraw()
. Um exemplo mais detalhado está disponível aqui.
#include "glfm.h"
static GLint program = 0 ;
static GLuint vertexBuffer = 0 ;
static GLuint vertexArray = 0 ;
static void onDraw ( GLFMDisplay * display );
static void onSurfaceDestroyed ( GLFMDisplay * display );
void glfmMain ( GLFMDisplay * display ) {
glfmSetDisplayConfig ( display ,
GLFMRenderingAPIOpenGLES2 ,
GLFMColorFormatRGBA8888 ,
GLFMDepthFormatNone ,
GLFMStencilFormatNone ,
GLFMMultisampleNone );
glfmSetRenderFunc ( display , onDraw );
glfmSetSurfaceDestroyedFunc ( display , onSurfaceDestroyed );
}
static void onSurfaceDestroyed ( GLFMDisplay * display ) {
// When the surface is destroyed, all existing GL resources are no longer valid.
program = 0 ;
vertexBuffer = 0 ;
vertexArray = 0 ;
}
static GLuint compileShader ( const GLenum type , const GLchar * shaderString , GLint shaderLength ) {
GLuint shader = glCreateShader ( type );
glShaderSource ( shader , 1 , & shaderString , & shaderLength );
glCompileShader ( shader );
return shader ;
}
static void onDraw ( GLFMDisplay * display ) {
if ( program == 0 ) {
const GLchar vertexShader [] =
"#version 100n"
"attribute highp vec4 position;n"
"void main() {n"
" gl_Position = position;n"
"}" ;
const GLchar fragmentShader [] =
"#version 100n"
"void main() {n"
" gl_FragColor = vec4(0.85, 0.80, 0.75, 1.0);n"
"}" ;
program = glCreateProgram ();
GLuint vertShader = compileShader ( GL_VERTEX_SHADER , vertexShader , sizeof ( vertexShader ) - 1 );
GLuint fragShader = compileShader ( GL_FRAGMENT_SHADER , fragmentShader , sizeof ( fragmentShader ) - 1 );
glAttachShader ( program , vertShader );
glAttachShader ( program , fragShader );
glLinkProgram ( program );
glDeleteShader ( vertShader );
glDeleteShader ( fragShader );
}
if ( vertexBuffer == 0 ) {
const GLfloat vertices [] = {
0.0 , 0.5 , 0.0 ,
-0.5 , -0.5 , 0.0 ,
0.5 , -0.5 , 0.0 ,
};
glGenBuffers ( 1 , & vertexBuffer );
glBindBuffer ( GL_ARRAY_BUFFER , vertexBuffer );
glBufferData ( GL_ARRAY_BUFFER , sizeof ( vertices ), vertices , GL_STATIC_DRAW );
}
int width , height ;
glfmGetDisplaySize ( display , & width , & height );
glViewport ( 0 , 0 , width , height );
glClearColor ( 0.08f , 0.07f , 0.07f , 1.0f );
glClear ( GL_COLOR_BUFFER_BIT );
#if defined( GL_VERSION_3_0 ) && GL_VERSION_3_0
if ( vertexArray == 0 ) {
glGenVertexArrays ( 1 , & vertexArray );
}
glBindVertexArray ( vertexArray );
#endif
glUseProgram ( program );
glBindBuffer ( GL_ARRAY_BUFFER , vertexBuffer );
glEnableVertexAttribArray ( 0 );
glVertexAttribPointer ( 0 , 3 , GL_FLOAT , GL_FALSE , 0 , 0 );
glDrawArrays ( GL_TRIANGLES , 0 , 3 );
glfmSwapBuffers ( display );
}
Veja glfm.h
Use cmake
para gerar um projeto Xcode:
cmake -D GLFM_BUILD_EXAMPLES=ON -B build/apple -G Xcode
open build/apple/GLFM.xcodeproj
No Xcode, mude para o destino glfm_touch
e execute em um simulador ou dispositivo.
Use emcmake
para definir variáveis ambientais para cmake
e, em seguida, crie:
emcmake cmake -D GLFM_BUILD_EXAMPLES=ON -B build/emscripten && cmake --build build/emscripten
Em seguida, execute localmente:
emrun build/emscripten/examples
Ou execute um exemplo específico:
emrun build/emscripten/examples/glfm_touch.html
Não há gerador CMake para projetos do Android Studio, mas você pode incluir CMakeLists.txt
em um projeto novo ou existente.
[path/to/glfm]/build/android
e pressione "Concluir".AndroidManifest.xml
, adicione o main
assim: xml version = " 1.0 " encoding = " utf-8 " ?>
< manifest xmlns : android = " http://schemas.android.com/apk/res/android " >
< uses-feature android : glEsVersion = " 0x00020000 " android : required = " true " />
< application
android : allowBackup = " true "
android : icon = " @mipmap/ic_launcher "
android : label = " @string/app_name "
android : supportsRtl = " true " >
< activity android : name = " android.app.NativeActivity "
android : exported = " true "
android : configChanges = " orientation|screenLayout|screenSize|keyboardHidden|keyboard " >
< meta-data
android : name = " android.app.lib_name "
android : value = " glfm_touch " />
< intent-filter >
< action android : name = " android.intent.action.MAIN " />
< category android : name = " android.intent.category.LAUNCHER " />
intent-filter >
activity >
application >
manifest >
app/build.gradle
, adicione as seções externalNativeBuild
e sourceSets.main
assim: apply plugin : ' com.android.application '
android {
compileSdkVersion 34
ndkVersion ' 23.2.8568313 ' // Last version to support API 16, 17, 18
defaultConfig {
applicationId " com.brackeen.glfmexample "
minSdkVersion 16
targetSdkVersion 34
versionCode 1
versionName " 1.0 "
// Add externalNativeBuild in defaultConfig (1/2)
externalNativeBuild {
cmake {
arguments " -DGLFM_BUILD_EXAMPLES=ON "
}
}
}
// Add sourceSets.main and externalNativeBuild (2/2)
sourceSets . main {
assets . srcDirs = [ " ../../../examples/assets " ]
}
externalNativeBuild {
cmake {
path " ../../../CMakeLists.txt "
}
}
namespace ' com.brackeen.glfmexample '
}
glfmMain
ou das funções de retorno de chamada). Qual IDE devo usar? Por que não há implementação de desktop? Use Xcode ou Android Studio. Para desktop, use GLFW com o IDE de sua preferência.
Se você preferir não usar simuladores móveis para o desenvolvimento diário, uma boa solução é usar o GLFW e depois portar seu aplicativo para o GLFM. Nem todas as chamadas OpenGL serão portadas perfeitamente para OpenGL ES, mas para máxima portabilidade OpenGL, use OpenGL 3.2 Core Profile em desktop e OpenGL ES 2.0 em dispositivos móveis.
Por que o ponto de entrada é glfmMain()
e não main()
?
Caso contrário, não funcionaria no iOS. Para inicializar o ambiente Objective-C, a função main()
deve criar um pool de autorelease e chamar a função UIApplicationMain()
, que nunca retorna . No iOS, o GLFM não chama glfmMain()
até que UIApplicationDelegate
e UIViewController
sejam inicializados.
Por que o GLFM é orientado a eventos? Por que o GLFM assume o loop principal?
Caso contrário, não funcionaria no iOS (veja acima) ou no HTML5, que é orientado a eventos.