C++程序  |  240行  |  7.77 KB

/*
 * 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 "CTS_OPENGL"
#define LOG_NDEBUG 0
#include <android/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) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "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) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "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("android/opengl2/cts/reference/GLGameActivity");
    if (activityClass == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Couldn't find activity class");
        return -1;
    }
    jmethodID loadTexture = sEnv->GetStaticMethodID(activityClass, "loadTexture",
            "(Landroid/content/res/AssetManager;Ljava/lang/String;)I");
    if (loadTexture == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "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) {
    unsigned char* ub = (unsigned char*) b;
    return (((int) ub[0]) << 24) | (((int) ub[1]) << 16) | (((int) ub[2]) << 8) | ((int) ub[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);
                __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
                                    "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);
                __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
                                    "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 or equal to x.
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;
}