| /* |
| * Copyright 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <jni.h> |
| #include <stdlib.h> |
| #include <time.h> |
| |
| #include "gles3jni.h" |
| |
| const Vertex QUAD[4] = { |
| // Square with diagonal < 2 so that it fits in a [-1 .. 1]^2 square |
| // regardless of rotation. |
| {{-0.7f, -0.7f}, {0x00, 0xFF, 0x00}}, |
| {{ 0.7f, -0.7f}, {0x00, 0x00, 0xFF}}, |
| {{-0.7f, 0.7f}, {0xFF, 0x00, 0x00}}, |
| {{ 0.7f, 0.7f}, {0xFF, 0xFF, 0xFF}}, |
| }; |
| |
| bool checkGlError(const char* funcName) { |
| GLint err = glGetError(); |
| if (err != GL_NO_ERROR) { |
| ALOGE("GL error after %s(): 0x%08x\n", funcName, err); |
| return true; |
| } |
| return false; |
| } |
| |
| GLuint createShader(GLenum shaderType, const char* src) { |
| GLuint shader = glCreateShader(shaderType); |
| if (!shader) { |
| checkGlError("glCreateShader"); |
| return 0; |
| } |
| glShaderSource(shader, 1, &src, NULL); |
| |
| GLint compiled = GL_FALSE; |
| glCompileShader(shader); |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); |
| if (!compiled) { |
| GLint infoLogLen = 0; |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen); |
| if (infoLogLen > 0) { |
| GLchar* infoLog = (GLchar*)malloc(infoLogLen); |
| if (infoLog) { |
| glGetShaderInfoLog(shader, infoLogLen, NULL, infoLog); |
| ALOGE("Could not compile %s shader:\n%s\n", |
| shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment", |
| infoLog); |
| free(infoLog); |
| } |
| } |
| glDeleteShader(shader); |
| return 0; |
| } |
| |
| return shader; |
| } |
| |
| GLuint createProgram(const char* vtxSrc, const char* fragSrc) { |
| GLuint vtxShader = 0; |
| GLuint fragShader = 0; |
| GLuint program = 0; |
| GLint linked = GL_FALSE; |
| |
| vtxShader = createShader(GL_VERTEX_SHADER, vtxSrc); |
| if (!vtxShader) |
| goto exit; |
| |
| fragShader = createShader(GL_FRAGMENT_SHADER, fragSrc); |
| if (!fragShader) |
| goto exit; |
| |
| program = glCreateProgram(); |
| if (!program) { |
| checkGlError("glCreateProgram"); |
| goto exit; |
| } |
| glAttachShader(program, vtxShader); |
| glAttachShader(program, fragShader); |
| |
| glLinkProgram(program); |
| glGetProgramiv(program, GL_LINK_STATUS, &linked); |
| if (!linked) { |
| ALOGE("Could not link program"); |
| GLint infoLogLen = 0; |
| glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen); |
| if (infoLogLen) { |
| GLchar* infoLog = (GLchar*)malloc(infoLogLen); |
| if (infoLog) { |
| glGetProgramInfoLog(program, infoLogLen, NULL, infoLog); |
| ALOGE("Could not link program:\n%s\n", infoLog); |
| free(infoLog); |
| } |
| } |
| glDeleteProgram(program); |
| program = 0; |
| } |
| |
| exit: |
| glDeleteShader(vtxShader); |
| glDeleteShader(fragShader); |
| return program; |
| } |
| |
| static void printGlString(const char* name, GLenum s) { |
| const char* v = (const char*)glGetString(s); |
| ALOGV("GL %s: %s\n", name, v); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| Renderer::Renderer() |
| : mNumInstances(0), |
| mLastFrameNs(0) |
| { |
| memset(mScale, 0, sizeof(mScale)); |
| memset(mAngularVelocity, 0, sizeof(mAngularVelocity)); |
| memset(mAngles, 0, sizeof(mAngles)); |
| } |
| |
| Renderer::~Renderer() { |
| } |
| |
| void Renderer::resize(int w, int h) { |
| float* offsets = mapOffsetBuf(); |
| calcSceneParams(w, h, offsets); |
| unmapOffsetBuf(); |
| |
| for (unsigned int i = 0; i < mNumInstances; i++) { |
| mAngles[i] = drand48() * TWO_PI; |
| mAngularVelocity[i] = MAX_ROT_SPEED * (2.0*drand48() - 1.0); |
| } |
| |
| mLastFrameNs = 0; |
| |
| glViewport(0, 0, w, h); |
| } |
| |
| void Renderer::calcSceneParams(unsigned int w, unsigned int h, |
| float* offsets) { |
| // number of cells along the larger screen dimension |
| const float NCELLS_MAJOR = MAX_INSTANCES_PER_SIDE; |
| // cell size in scene space |
| const float CELL_SIZE = 2.0f / NCELLS_MAJOR; |
| |
| // Calculations are done in "landscape", i.e. assuming dim[0] >= dim[1]. |
| // Only at the end are values put in the opposite order if h > w. |
| const float dim[2] = {fmaxf(w,h), fminf(w,h)}; |
| const float aspect[2] = {dim[0] / dim[1], dim[1] / dim[0]}; |
| const float scene2clip[2] = {1.0f, aspect[0]}; |
| const int ncells[2] = { |
| NCELLS_MAJOR, |
| (int)floorf(NCELLS_MAJOR * aspect[1]) |
| }; |
| |
| float centers[2][MAX_INSTANCES_PER_SIDE]; |
| for (int d = 0; d < 2; d++) { |
| float offset = -ncells[d] / NCELLS_MAJOR; // -1.0 for d=0 |
| for (int i = 0; i < ncells[d]; i++) { |
| centers[d][i] = scene2clip[d] * (CELL_SIZE*(i + 0.5f) + offset); |
| } |
| } |
| |
| int major = w >= h ? 0 : 1; |
| int minor = w >= h ? 1 : 0; |
| // outer product of centers[0] and centers[1] |
| for (int i = 0; i < ncells[0]; i++) { |
| for (int j = 0; j < ncells[1]; j++) { |
| int idx = i*ncells[1] + j; |
| offsets[2*idx + major] = centers[0][i]; |
| offsets[2*idx + minor] = centers[1][j]; |
| } |
| } |
| |
| mNumInstances = ncells[0] * ncells[1]; |
| mScale[major] = 0.5f * CELL_SIZE * scene2clip[0]; |
| mScale[minor] = 0.5f * CELL_SIZE * scene2clip[1]; |
| } |
| |
| void Renderer::step() { |
| timespec now; |
| clock_gettime(CLOCK_MONOTONIC, &now); |
| uint64_t nowNs = now.tv_sec*1000000000ull + now.tv_nsec; |
| |
| if (mLastFrameNs > 0) { |
| float dt = float(nowNs - mLastFrameNs) * 0.000000001f; |
| |
| for (unsigned int i = 0; i < mNumInstances; i++) { |
| mAngles[i] += mAngularVelocity[i] * dt; |
| if (mAngles[i] >= TWO_PI) { |
| mAngles[i] -= TWO_PI; |
| } else if (mAngles[i] <= -TWO_PI) { |
| mAngles[i] += TWO_PI; |
| } |
| } |
| |
| float* transforms = mapTransformBuf(); |
| for (unsigned int i = 0; i < mNumInstances; i++) { |
| float s = sinf(mAngles[i]); |
| float c = cosf(mAngles[i]); |
| transforms[4*i + 0] = c * mScale[0]; |
| transforms[4*i + 1] = s * mScale[1]; |
| transforms[4*i + 2] = -s * mScale[0]; |
| transforms[4*i + 3] = c * mScale[1]; |
| } |
| unmapTransformBuf(); |
| } |
| |
| mLastFrameNs = nowNs; |
| } |
| |
| void Renderer::render() { |
| step(); |
| |
| glClearColor(0.2f, 0.2f, 0.3f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| draw(mNumInstances); |
| checkGlError("Renderer::render"); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static Renderer* g_renderer = NULL; |
| |
| extern "C" { |
| JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_init(JNIEnv* env, jobject obj); |
| JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_resize(JNIEnv* env, jobject obj, jint width, jint height); |
| JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_step(JNIEnv* env, jobject obj); |
| }; |
| |
| #if !defined(DYNAMIC_ES3) |
| static GLboolean gl3stubInit() { |
| return GL_TRUE; |
| } |
| #endif |
| |
| JNIEXPORT void JNICALL |
| Java_com_android_gles3jni_GLES3JNILib_init(JNIEnv* env, jobject obj) { |
| if (g_renderer) { |
| delete g_renderer; |
| g_renderer = NULL; |
| } |
| |
| printGlString("Version", GL_VERSION); |
| printGlString("Vendor", GL_VENDOR); |
| printGlString("Renderer", GL_RENDERER); |
| printGlString("Extensions", GL_EXTENSIONS); |
| |
| const char* versionStr = (const char*)glGetString(GL_VERSION); |
| if (strstr(versionStr, "OpenGL ES 3.") && gl3stubInit()) { |
| g_renderer = createES3Renderer(); |
| } else if (strstr(versionStr, "OpenGL ES 2.")) { |
| g_renderer = createES2Renderer(); |
| } else { |
| ALOGE("Unsupported OpenGL ES version"); |
| } |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_com_android_gles3jni_GLES3JNILib_resize(JNIEnv* env, jobject obj, jint width, jint height) { |
| if (g_renderer) { |
| g_renderer->resize(width, height); |
| } |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_com_android_gles3jni_GLES3JNILib_step(JNIEnv* env, jobject obj) { |
| if (g_renderer) { |
| g_renderer->render(); |
| } |
| } |