blob: c4fbf7f3ecc29aab82d622f74681d11d4fe995cd [file] [log] [blame]
/*
* Copyright (C) 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 "GLUtils.h"
#include <stdlib.h>
#include <sys/time.h>
#include <android/asset_manager_jni.h>
#define LOG_TAG "PTS_OPENGL"
#define LOG_NDEBUG 0
#include <utils/Log.h>
static JNIEnv* sEnv = NULL;
static jobject sAssetManager = NULL;
void GLUtils::setEnvAndAssetManager(JNIEnv* env, jobject assetManager) {
sEnv = env;
sAssetManager = assetManager;
}
static AAsset* loadAsset(const char* path) {
AAssetManager* nativeManager = AAssetManager_fromJava(sEnv, sAssetManager);
if (nativeManager == NULL) {
return NULL;
}
return AAssetManager_open(nativeManager, path, AASSET_MODE_UNKNOWN);;
}
char* GLUtils::openTextFile(const char* path) {
AAsset* asset = loadAsset(path);
if (asset == NULL) {
ALOGE("Couldn't load %s", path);
return NULL;
}
off_t length = AAsset_getLength(asset);
char* buffer = new char[length + 1];
int num = AAsset_read(asset, buffer, length);
AAsset_close(asset);
if (num != length) {
ALOGE("Couldn't read %s", path);
delete[] buffer;
return NULL;
}
buffer[length] = '\0';
return buffer;
}
GLuint GLUtils::loadTexture(const char* path) {
GLuint textureId = 0;
jclass activityClass = sEnv->FindClass("com/android/pts/opengl/reference/GLGameActivity");
if (activityClass == NULL) {
ALOGE("Couldn't find activity class");
return -1;
}
jmethodID loadTexture = sEnv->GetStaticMethodID(activityClass, "loadTexture",
"(Landroid/content/res/AssetManager;Ljava/lang/String;)I");
if (loadTexture == NULL) {
ALOGE("Couldn't find loadTexture method");
return -1;
}
jstring pathStr = sEnv->NewStringUTF(path);
textureId = sEnv->CallStaticIntMethod(activityClass, loadTexture, sAssetManager, pathStr);
sEnv->DeleteLocalRef(pathStr);
return textureId;
}
static int readInt(char* b) {
return (((int) b[0]) << 24) | (((int) b[1]) << 16) | (((int) b[2]) << 8) | ((int) b[3]);
}
static float readFloat(char* b) {
union {
int input;
float output;
} data;
data.input = readInt(b);
return data.output;
}
Mesh* GLUtils::loadMesh(const char* path) {
char* buffer = openTextFile(path);
if (buffer == NULL) {
return NULL;
}
int index = 0;
int numVertices = readInt(buffer + index);
index += 4;
float* vertices = new float[numVertices * 3];
float* normals = new float[numVertices * 3];
float* texCoords = new float[numVertices * 2];
for (int i = 0; i < numVertices; i++) {
// Vertices
int vIndex = i * 3;
vertices[vIndex + 0] = readFloat(buffer + index);
index += 4;
vertices[vIndex + 1] = readFloat(buffer + index);
index += 4;
vertices[vIndex + 2] = readFloat(buffer + index);
index += 4;
// Normals
normals[vIndex + 0] = readFloat(buffer + index);
index += 4;
normals[vIndex + 1] = readFloat(buffer + index);
index += 4;
normals[vIndex + 2] = readFloat(buffer + index);
index += 4;
// Texture Coordinates
int tIndex = i * 2;
texCoords[tIndex + 0] = readFloat(buffer + index);
index += 4;
texCoords[tIndex + 1] = readFloat(buffer + index);
index += 4;
}
return new Mesh(vertices, normals, texCoords, numVertices);
}
// Loads the given source code as a shader of the given type.
static GLuint loadShader(GLenum shaderType, const char** source) {
GLuint shader = glCreateShader(shaderType);
if (shader) {
glShaderSource(shader, 1, source, NULL);
glCompileShader(shader);
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 0) {
char* infoLog = (char*) malloc(sizeof(char) * infoLen);
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
ALOGE("Error compiling shader:\n%s\n", infoLog);
free(infoLog);
}
glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
GLuint GLUtils::createProgram(const char** vertexSource, const char** fragmentSource) {
GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
if (!vertexShader) {
return 0;
}
GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
if (!fragmentShader) {
return 0;
}
GLuint program = glCreateProgram();
if (program) {
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
GLint linkStatus;
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (!linkStatus) {
GLint infoLen = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 0) {
char* infoLog = (char*) malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(program, infoLen, NULL, infoLog);
ALOGE("Error linking program:\n%s\n", infoLog);
free(infoLog);
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
double GLUtils::currentTimeMillis() {
struct timeval tv;
gettimeofday(&tv, (struct timezone *) NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
// Rounds a number up to the smallest power of 2 that is greater than the original number.
int GLUtils::roundUpToSmallestPowerOf2(int x) {
if (x < 0) {
return 0;
}
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
GLuint GLUtils::genTexture(int texWidth, int texHeight, int fill) {
GLuint textureId = 0;
int w = roundUpToSmallestPowerOf2(texWidth);
int h = roundUpToSmallestPowerOf2(texHeight);
uint32_t* m = new uint32_t[w * h];
if (m != NULL) {
uint32_t* d = m;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (fill == RANDOM_FILL) {
*d = 0xff000000 | ((y & 0xff) << 16) | ((x & 0xff) << 8) | ((x + y) & 0xff);
} else {
*d = 0xff000000 | fill;
}
d++;
}
}
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
delete[] m;
return textureId;
}
bool GLUtils::createFBO(GLuint& fboId, GLuint& rboId, GLuint& cboId, int width, int height) {
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glGenRenderbuffers(1, &rboId);
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);
glGenRenderbuffers(1, &cboId);
glBindRenderbuffer(GL_RENDERBUFFER, cboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cboId);
GLuint err = glGetError();
if (err != GL_NO_ERROR) {
ALOGE("GLError %d", err);
return false;
}
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
}