Source

love / src / modules / graphics / opengl / GLES.cpp

Full commit
/**
 * Copyright (c) 2006-2012 LOVE Development Team
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 **/

#include "GLES.h"

#ifdef HAVE_GLES

// LOVE
#include "common/Matrix.h"

// OpenGL
#include <GLES2/gl2.h>

// STD
#include <cstdio>
#include <cstring> // memcpy
#include <vector>

using love::Matrix;

namespace
{

GLuint drawQuadProgram;

float color[4] = { 1, 1, 1, 1 };
float colorWhite[4] = { 1, 1, 1, 1 };

// Modes: 0=modelview, 1=projection, 2=color, 3=texture
std::vector<Matrix> stack[4];

int init()
{
	stack[0].resize(1);
	stack[1].resize(1);
	stack[2].resize(1);
	stack[3].resize(1);
	return 0;
}

int mode = init(); // easy way to initialize

int texEnvMode = GL_MODULATE;

GLuint loadShader(GLenum shaderType, const char* shaderSrc)
{
	GLuint shader = glCreateShader(shaderType);
	if (!shader)
	{
		fprintf(stderr, "no shader love :-(\n");
		return 0;
	}

	glShaderSource(shader, 1, &shaderSrc, NULL);

	glCompileShader(shader);

	GLint compiled;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

	if (!compiled)
	{
		char infoLog[1024];
		glGetShaderInfoLog(shader, 1024, NULL, infoLog);
		fprintf(stderr, "shader failed to compile: %s\n", infoLog);
		glDeleteShader(shader);
		return 0;
	}

	return shader;
}

GLuint loadProgram(const char* vertexShaderSrc, const char* fragmentShaderSrc)
{
	GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderSrc);
	if (!vertexShader)
	{
		return 0;
	}

	GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
	if (!fragmentShader)
	{
		glDeleteShader(vertexShader);
		return 0;
	}
	
	GLuint program = glCreateProgram();
	if (!program)
	{
		fprintf(stderr, "no program love :-(\n");
		glDeleteShader(vertexShader);
		glDeleteShader(fragmentShader);
		return 0;
	}

	glAttachShader(program, vertexShader);
	glAttachShader(program, fragmentShader);

	glLinkProgram(program);

	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	GLint linked;
	glGetProgramiv(program, GL_LINK_STATUS, &linked);

	if (!linked)
	{
		char infoLog[1024];
		glGetProgramInfoLog(program, 1024, NULL, infoLog);
		fprintf(stderr, "program failed to link: %s\n", infoLog);
		glDeleteProgram(program);
		return 0;
	}

	return program;
}

} // namespace

extern "C"
{

void glesGetTexEnvModeiv(int* params)
{
	*params = texEnvMode;
}

void glesTexEnvModei(int mode)
{
	texEnvMode = mode;
}

void glesGetColorfv(float* params)
{
	memcpy(params, color, sizeof(float)*4);
}

void glColor4fv(const float* v)
{
	memcpy(color, v, sizeof(float)*4);
}

void glColor4f(float red, float green, float blue, float alpha)
{
	color[0] = red;
	color[1] = green;
	color[2] = blue;
	color[3] = alpha;
}

void glColor4ubv(const unsigned char* v)
{
	color[0] = v[0]/255.0f;
	color[1] = v[1]/255.0f;
	color[2] = v[2]/255.0f;
	color[3] = v[3]/255.0f;
}

void glesGetMatrixfv(float* params)
{
	memcpy(params, stack[mode].back().getElements(), sizeof(float)*16);
}

void glMatrixMode(int mode)
{
	::mode = mode - GL_MODELVIEW;
}

void glLoadIdentity(void)
{
	stack[mode].back().setIdentity();
}

void glPushMatrix(void)
{
	stack[mode].push_back(stack[mode].back());
}

void glPopMatrix(void)
{
	stack[mode].pop_back();
}

void glMultMatrixf(const float* m)
{
	Matrix t;
	t.setElements(m);
	stack[mode].back() *= t;
}

void glTranslatef(float x, float y, float z)
{
	// NOTE z ignored
	stack[mode].back().translate(x, y);
}

void glScalef(float x, float y, float z)
{
	// NOTE z ignored
	stack[mode].back().scale(x, y);
}

void glRotatef(float angle, float x, float y, float z)
{
	// NOTE xyz ignored
	stack[mode].back().rotate(LOVE_TORAD(angle));
}

void glOrthof(float left, float right, float bottom, float top, float near, float far)
{
	float m[16];
	memset(m, 0, sizeof(float)*16);

	m[0]  = 2.0f / (right-left);
	m[5]  = 2.0f / (top-bottom);
	m[10] = 2.0f / (far-near);

	m[12] = - (right+left) / (right-left);
	m[13] = - (top+bottom) / (top-bottom);
	m[14] = - (far+near)   / (far-near);
	m[15] = 1;

	Matrix t;
	t.setElements(m);
	stack[mode].back() *= t;
}

void glPushAttrib(unsigned int mask)
{
	// TODO
}

void glPopAttrib(void)
{
	// TODO
}

void glesDrawQuad(int vstride, const float* vdata, int tstride, const float* tdata)
{
	static GLubyte indices[] = { 0, 1, 3, 2 };
	static GLuint program = 0;
	static GLint locP, locM, locC, locS, locV, locT;

	// TODO add support for color modulation

	if (!program)
	{
		program = loadProgram(
		// VERTEX SHADER
		"    uniform mat4 u_matProjection;                                     \n"
		"    uniform mat4 u_matModelView;                                      \n"
		"    attribute vec4 a_position;                                        \n"
		"    attribute vec2 a_texCoord;                                        \n"
		"    varying vec2 v_texCoord;                                          \n"
		"    void main()                                                       \n"
		"    {                                                                 \n"
		"        gl_Position = u_matProjection * u_matModelView * a_position;  \n"
		"        v_texCoord = a_texCoord;                                      \n"
		"    }                                                                 \n",
		// FRAGMENT SHADER
		"    uniform vec4 u_color;                                             \n"
		"    uniform sampler2D s_texture;                                      \n"
		"    varying vec2 v_texCoord;                                          \n"
		"    void main()                                                       \n"
		"    {                                                                 \n"
		"        gl_FragColor = u_color * texture2D(s_texture, v_texCoord);    \n"
		"    }                                                                 \n");

		locP = glGetUniformLocation(program, "u_matProjection");
		locM = glGetUniformLocation(program, "u_matModelView");
		locC = glGetUniformLocation(program, "u_color");
		locS = glGetUniformLocation(program, "s_texture");
		locV = glGetAttribLocation(program, "a_position");
		locT = glGetAttribLocation(program, "a_texCoord");
	}

	glUseProgram(program);

	glUniformMatrix4fv(locP, 1, GL_FALSE, stack[1].back().getElements());
	glUniformMatrix4fv(locM, 1, GL_FALSE, stack[0].back().getElements());
	glUniform4fv(locC, 1, texEnvMode == GL_MODULATE ? color : colorWhite);

	glUniform1i(locS, 0);

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);

	glVertexAttribPointer(locV, 2, GL_FLOAT, GL_FALSE, vstride, vdata);
	glVertexAttribPointer(locT, 2, GL_FLOAT, GL_FALSE, tstride, tdata);

	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
}

} // extern "C"

#endif // HAVE_GLES