c77fb700创建于 2025年1月16日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * 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 "render_surface.h"

#import <UIKit/UIKit.h>

#include <cmath>
#include "log.h"

RenderSurface::RenderSurface(std::string& id)
  : id(id), eaglContextObj(nullptr) {
        EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        eaglContextObj = (__bridge_retained void*)context;
}

static char g_vertexShader[] =
    "attribute vec4 a_pos;\n"
    "attribute vec4 a_color;\n"
    "attribute vec4 a_normal;\n"
    "uniform vec3 u_lightColor;\n"
    "uniform vec3 u_lightDirection;\n"
    "uniform mat4 a_mx;\n"
    "uniform mat4 a_my;\n"
    "varying vec4 v_color;\n"
    "void main() {\n"
    "    float radian = radians(30.0);\n"
    "    float cos = cos(radian);\n"
    "    float sin = sin(radian);\n"
    "    gl_Position = a_mx * a_my * vec4(a_pos.x, a_pos.y, a_pos.z, 1.0);\n"
    "    vec3 normal = normalize((a_mx * a_my * a_normal).xyz);\n"
    "    float dot = max(dot(u_lightDirection, normal), 0.0);\n"
    "    vec3 reflectedLight = u_lightColor * a_color.rgb * dot;\n"
    "    v_color = vec4(reflectedLight, a_color.a);\n"
    "}\n\0";

static char g_fragmentShader[] =
    "precision mediump float;\n"
    "varying vec4 v_color;\n"
    "void main() {\n"
    "    gl_FragColor = v_color;\n"
    "}\n\0";

/* Create an array of vertex position data vertexData */
static float g_vertexData[] = {
    -0.75, -0.50, -0.43, 0.75, -0.50, -0.43, 0.00,  -0.50, 0.87,  0.75, -0.50, -0.43,
    0.00,  -0.50, 0.87,  0.00, 1.00,  0.00,  0.00,  -0.50, 0.87,  0.00, 1.00,  0.00,
    -0.75, -0.50, -0.43, 0.00, 1.00,  0.00,  -0.75, -0.50, -0.43, 0.75, -0.50, -0.43,
};

/* Create an array of vertex colors colorData */
static float g_colorData[] = {
    1, 0, 0, 1, 0, 0, 1, 0, 0, /* Red surface 1 */
    1, 0, 0, 1, 0, 0, 1, 0, 0, /* Red surface 2 */
    1, 0, 0, 1, 0, 0, 1, 0, 0, /* Red surface 3 */
    1, 0, 0, 1, 0, 0, 1, 0, 0  /* Red surface 4 */
};

/* An array of vertex normal vectors normalData */
static float g_normalData[] = {
    0.00,  -1.00, 0.00,  0.00,  -1.00, 0.00,  0.00,  -1.00, 0.00, -0.83, -0.28, -0.48,
    -0.83, -0.28, -0.48, -0.83, -0.28, -0.48, -0.83, 0.28,  0.48, -0.83, 0.28,  0.48,
    -0.83, 0.28,  0.48,  0.00,  -0.28, 0.96,  0.00,  -0.28, 0.96, 0.00,  -0.28, 0.96,
};

namespace {
    void enableVertexAttrib(GLuint index, float *data, int32_t len)
    {
        GLuint buffer;
        glGenBuffers(1, &buffer);
        glBindBuffer(GL_ARRAY_BUFFER, buffer);
        glBufferData(GL_ARRAY_BUFFER, len, data, GL_STATIC_DRAW);
        glVertexAttribPointer(index, TRIANGLES_POINT, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(index);
        return;
    }
}

GLuint RenderSurface::LoadShader(GLenum type, const char *shaderSrc)
{
    GLuint shader;
    GLint compiled;

    shader = glCreateShader(type);
    if (shader == 0) {
        LOGE("LoadShader shader error");
        return 0;
    }

    glShaderSource(shader, 1, &shaderSrc, nullptr);
    glCompileShader(shader);

    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

    if (!compiled) {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);

        if (infoLen > 1) {
            std::string infoLog(infoLen, '\0');
            glGetShaderInfoLog(shader, infoLen, nullptr, (GLchar *)&infoLog);
            LOGE("Error compiling shader:%{public}s\n", infoLog.c_str());
        }

        glDeleteShader(shader);
        return 0;
    }

    return shader;
}

GLuint RenderSurface::CreateProgram(const char *vertexShader, const char *fragShader)
{
    GLuint vertex;
    GLuint fragment;
    GLuint program;
    GLint linked;

    vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
    if (vertex == 0) {
        LOGE("LoadShader: vertexShader error");
        return 0;
    }

    fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
    if (fragment == 0) {
        LOGE("LoadShader: fragShader error");
        glDeleteShader(vertex);
        return 0;
    }

    program = glCreateProgram();
    if (program == 0) {
        LOGE("CreateProgram program error");
        glDeleteShader(vertex);
        glDeleteShader(fragment);
        return 0;
    }

    glAttachShader(program, vertex);
    glAttachShader(program, fragment);
    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &linked);

    if (!linked) {
        LOGE("CreateProgram linked error");
        GLint infoLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            std::string infoLog(infoLen, '\0');
            glGetProgramInfoLog(program, infoLen, nullptr, (GLchar *)&infoLog);
            LOGE("Error linking program:%{public}s\n", infoLog.c_str());
        }
        glDeleteShader(vertex);
        glDeleteShader(fragment);
        glDeleteProgram(program);
        return 0;
    }
    glDeleteShader(vertex);
    glDeleteShader(fragment);

    return program;
}

EAGLContext* RenderSurface::getEAGLContext() {
    return  (__bridge EAGLContext*)eaglContextObj;
}

int32_t RenderSurface::Init(void *window, int32_t width,  int32_t height)
{
    LOGI("Init window = %{public}p, w = %{public}d, h = %{public}d.", window, width, height);
    mSurfaceWidth = width;
    mSurfaceHeight = height;
    
    if (!eaglContextObj) {
        /* Create a new EAGLContext directly */
        EAGLContext *newContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        if (!newContext || ![EAGLContext setCurrentContext:newContext]) {
            NSLog(@"Failed to create EAGLContext");
            return -1;
        }
        eaglContextObj = (__bridge_retained void *)newContext;
    } else {
        /* Note: We do not need to recreate the EAGLContext here if we've already created it in our constructor */
        [EAGLContext setCurrentContext:getEAGLContext()];
    }
    
    CAEAGLLayer * caeagLayer = [[CAEAGLLayer alloc] init];
    CALayer * layer = (__bridge CALayer *)window;
    caeagLayer.frame = layer.frame;
    [layer addSublayer:caeagLayer];
    /* Let's assume that the passed window is of type UIView */
    mEglWindow = caeagLayer;
    
    
    mEglWindow.opaque = YES;
    mEglWindow.drawableProperties = @{
        kEAGLDrawablePropertyRetainedBacking : [NSNumber numberWithBool:NO],
        kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8
    };
    
    if (!eaglContextObj || ![EAGLContext setCurrentContext:getEAGLContext()]) {
        LOGE("Failed to set current EAGLContext");
        return -1;
    }
    InitRenderBuffer();

    mProgramHandle = CreateProgram(g_vertexShader, g_fragmentShader);
    if (!mProgramHandle) {
        LOGE("Could not create CreateProgram");
        return -1;
    }

    LOGI("Init success.");

    return 0;
}

void RenderSurface::InitRenderBuffer()
{
    if(mEglWindow.frame.size.width == 0 && mEglWindow.frame.size.height == 0) {
        return;
    }
    if( isInitBuffer_) {
        return;
    }
    /* Set the render buffer and frame buffer */
    glGenRenderbuffers(1, &mRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
    if (![getEAGLContext() renderbufferStorage:GL_RENDERBUFFER fromDrawable:mEglWindow]) {
        /* Could not proceed to renderbufferStorage, returned error */
        LOGE("iOS failed to call renderbufferStorage:fromDrawable:");
        return;
    }

    /* Generate and bind the frame buffer */
    glGenFramebuffers(1, &mFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
    /* Attach the render buffer to the frame buffer */
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);

    GLenum frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (frameBufferStatus != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"Framebuffer is not complete: %x", frameBufferStatus);
        switch (frameBufferStatus) {
            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
                NSLog(@"Framebuffer incomplete: Attachment is NOT complete.");
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
                NSLog(@"Framebuffer incomplete: No image is attached to FBO.");
                break;
            /* More cases can be added to check other Framebuffer status */
            default:
                NSLog(@"Framebuffer incomplete: Unknown error.");
        }
        /* The Framebuffer was not set successfully; an error is returned */
        return;
    } else {
        /* Framebuffer is complete, proceed with rendering */
        NSLog(@"Framebuffer is complete.");
    }

    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR) {
        NSLog(@"OpenGL Error after checking framebuffer status: 0x%04X", err);
    }
    isInitBuffer_ = true;
    Update(angleX, angleY);
    return;
}

void RenderSurface::Update(float angleXOffset, float angleYOffset)
{
    const float pi = 3.141592;

    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, mSurfaceWidth, mSurfaceHeight);
    glUseProgram(mProgramHandle);

    GLint aPos = glGetAttribLocation(mProgramHandle, "a_pos");
    GLint aColor = glGetAttribLocation(mProgramHandle, "a_color");
    GLint aNormal = glGetAttribLocation(mProgramHandle, "a_normal");
    GLint uLightColor = glGetUniformLocation(mProgramHandle, "u_lightColor");
    GLint uLightDirection = glGetUniformLocation(mProgramHandle, "u_lightDirection");
    GLint aMx = glGetUniformLocation(mProgramHandle, "a_mx");
    GLint aMy = glGetUniformLocation(mProgramHandle, "a_my");

    angleX = angleXOffset;
    angleY = angleYOffset;

    /* y axis rotation */
    float radianY = (angleY * pi) / 180.0;
    float cosY = cosf(radianY);
    float sinY = sinf(radianY);
    float myArr[] = {
        cosY, 0, -sinY, 0,
        0, 1, 0, 0,
        sinY, 0, cosY, 0,
        0, 0, 0, 1
    };

    glUniformMatrix4fv(aMy, 1, false, myArr);

    /* x axis rotation */
    float radianX = (angleX * pi) / 180.0;
    float cosX = cosf(radianX);
    float sinX = sinf(radianX);
    float mxArr[] = {
        1, 0, 0, 0, 0, cosX, -sinX, 0, 0, sinX, cosX, 0, 0, 0, 0, 1
    };

    glUniformMatrix4fv(aMx, 1, false, mxArr);

    /* Passing color and orientation data to the parallel light, RGB(1,1,1), unit vector (x,y,z) */
    glUniform3f(uLightColor, 1.0, 1.0, 1.0);

    /* The vector (x,y,z) is guaranteed to have length 1, which is the unit vector */
    float x = 2.0 / sqrt(15);
    float y = 2.0 / sqrt(15);
    float z = 3.0 / sqrt(15);

    glUniform3f(uLightDirection, x, -y, z);

    /* Create a buffer and pass in the vertex position data g_vertexData */
    enableVertexAttrib(aPos, g_vertexData, sizeof(g_vertexData));
    enableVertexAttrib(aNormal, g_normalData, sizeof(g_normalData));
    /* Create a colorBuffer and pass in the vertex position data g_colorData */
    enableVertexAttrib(aColor, g_colorData, sizeof(g_colorData));

    /* Make sure you enable the depth test before you start drawing to avoid cluttering the colors */
    glEnable(GL_DEPTH_TEST);

    /* Perform the draw and update */
    glDrawArrays(GL_TRIANGLES, 0, TETRAHEDRON_POINT);

    if ([getEAGLContext() presentRenderbuffer:GL_RENDERBUFFER]) {
        LOGI("Swap buffers success.");
    } else {
        LOGE("Swap buffers failed.");
    }
}

void RenderSurface::UpdateSzie(float width, float height) {
    CGFloat scale = [UIScreen mainScreen].scale;
    mSurfaceWidth = width/scale;
    mSurfaceHeight = height/scale;
    mEglWindow.frame = CGRectMake(0, 0, mSurfaceWidth, mSurfaceHeight);
    InitRenderBuffer();
}

float RenderSurface::GetAngleX()
{
    return angleX;
}

float RenderSurface::GetAngleY()
{
    return angleY;
}

int32_t RenderSurface::Quit(void)
{
    BOOL success = YES;

    if (getEAGLContext()) {
        if (getEAGLContext() != [EAGLContext currentContext]) {
            [EAGLContext setCurrentContext:getEAGLContext()];
        }

        if ([EAGLContext currentContext] == getEAGLContext()) {
            if (mRenderbuffer) {
                glDeleteRenderbuffers(1, &mRenderbuffer);
                mRenderbuffer = 0;
            }

            if (mFramebuffer) {
                glDeleteFramebuffers(1, &mFramebuffer);
                mFramebuffer = 0;
            }
            success =  [EAGLContext setCurrentContext:nil];
            eaglContextObj = nullptr;
        }
        isInitBuffer_ = false;
    }

    if (!success) {
        LOGW("Quit failure.");
    } else {
        LOGE("Quit success.");
    }

    return success ? 0 : -1;
}