Initial Commit
This commit is contained in:
47
CMakeLists.txt
Normal file
47
CMakeLists.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
project(text_rendering_2)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Enable Package Managers
|
||||
include(cmake/CPM.cmake)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME GLAD
|
||||
URL https://git.redacted.cc/Redacted/glad/archive/v2.1.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME glm
|
||||
GITHUB_REPOSITORY g-truc/glm
|
||||
OPTIONS "GLM_STATIC_LIBRARY_ENABLE"
|
||||
#SOURCE_SUBDIR glm
|
||||
GIT_TAG 0.9.9.8
|
||||
)
|
||||
|
||||
|
||||
find_package(glm REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(GLUT REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(glfw3 REQUIRED)
|
||||
find_package(GLEW REQUIRED)
|
||||
|
||||
add_executable(text_rendering_2 main.cpp
|
||||
Shader.h
|
||||
common/shader_utils.cpp
|
||||
common/shader_utils.h)
|
||||
|
||||
|
||||
include_directories(${OPENGL_INCLUDE_DIRS})
|
||||
include_directories(${GLUT_INCLUDE_DIRS})
|
||||
include_directories(${glad_SOURCE_DIR}/include)
|
||||
include_directories(${GLFW_INCLUDE_DIRS})
|
||||
include_directories(${GLEW_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(text_rendering_2 PUBLIC ${OPENGL_LIBRARIES})
|
||||
target_link_libraries(text_rendering_2 PUBLIC ${GLUT_LIBRARIES})
|
||||
target_link_libraries(text_rendering_2 PRIVATE ${FREETYPE_LIBRARIES})
|
||||
target_include_directories(text_rendering_2 PRIVATE ${FREETYPE_INCLUDE_DIRS})
|
||||
target_link_libraries(text_rendering_2 PRIVATE glad glfw GLEW)
|
||||
target_link_libraries(text_rendering_2 PRIVATE glm::glm)
|
BIN
FreeSans.ttf
Normal file
BIN
FreeSans.ttf
Normal file
Binary file not shown.
194
Shader.h
Normal file
194
Shader.h
Normal file
@@ -0,0 +1,194 @@
|
||||
|
||||
|
||||
#ifndef SHADER_H
|
||||
#define SHADER_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
unsigned int ID;
|
||||
// constructor generates the shader on the fly
|
||||
// ------------------------------------------------------------------------
|
||||
Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
|
||||
{
|
||||
// 1. retrieve the vertex/fragment source code from filePath
|
||||
std::string vertexCode;
|
||||
std::string fragmentCode;
|
||||
std::string geometryCode;
|
||||
std::ifstream vShaderFile;
|
||||
std::ifstream fShaderFile;
|
||||
std::ifstream gShaderFile;
|
||||
// ensure ifstream objects can throw exceptions:
|
||||
vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
||||
fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
||||
gShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
||||
try
|
||||
{
|
||||
// open files
|
||||
vShaderFile.open(vertexPath);
|
||||
fShaderFile.open(fragmentPath);
|
||||
std::stringstream vShaderStream, fShaderStream;
|
||||
// read file's buffer contents into streams
|
||||
vShaderStream << vShaderFile.rdbuf();
|
||||
fShaderStream << fShaderFile.rdbuf();
|
||||
// close file handlers
|
||||
vShaderFile.close();
|
||||
fShaderFile.close();
|
||||
// convert stream into string
|
||||
vertexCode = vShaderStream.str();
|
||||
fragmentCode = fShaderStream.str();
|
||||
// if geometry shader path is present, also load a geometry shader
|
||||
if(geometryPath != nullptr)
|
||||
{
|
||||
gShaderFile.open(geometryPath);
|
||||
std::stringstream gShaderStream;
|
||||
gShaderStream << gShaderFile.rdbuf();
|
||||
gShaderFile.close();
|
||||
geometryCode = gShaderStream.str();
|
||||
}
|
||||
}
|
||||
catch (std::ifstream::failure& e)
|
||||
{
|
||||
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;
|
||||
}
|
||||
const char* vShaderCode = vertexCode.c_str();
|
||||
const char * fShaderCode = fragmentCode.c_str();
|
||||
// 2. compile shaders
|
||||
unsigned int vertex, fragment;
|
||||
// vertex shader
|
||||
vertex = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertex, 1, &vShaderCode, NULL);
|
||||
glCompileShader(vertex);
|
||||
checkCompileErrors(vertex, "VERTEX");
|
||||
// fragment Shader
|
||||
fragment = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragment, 1, &fShaderCode, NULL);
|
||||
glCompileShader(fragment);
|
||||
checkCompileErrors(fragment, "FRAGMENT");
|
||||
// if geometry shader is given, compile geometry shader
|
||||
unsigned int geometry;
|
||||
if(geometryPath != nullptr)
|
||||
{
|
||||
//const char * gShaderCode = geometryCode.c_str();
|
||||
//geometry = glCreateShader(GL_GEOMETRY_SHADER);
|
||||
//glShaderSource(geometry, 1, &gShaderCode, NULL);
|
||||
//glCompileShader(geometry);
|
||||
//checkCompileErrors(geometry, "GEOMETRY");
|
||||
}
|
||||
// shader Program
|
||||
ID = glCreateProgram();
|
||||
glAttachShader(ID, vertex);
|
||||
glAttachShader(ID, fragment);
|
||||
if(geometryPath != nullptr)
|
||||
glAttachShader(ID, geometry);
|
||||
glLinkProgram(ID);
|
||||
checkCompileErrors(ID, "PROGRAM");
|
||||
// delete the shaders as they're linked into our program now and no longer necessary
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
if(geometryPath != nullptr)
|
||||
glDeleteShader(geometry);
|
||||
|
||||
}
|
||||
// activate the shader
|
||||
// ------------------------------------------------------------------------
|
||||
void use()
|
||||
{
|
||||
glUseProgram(ID);
|
||||
}
|
||||
// utility uniform functions
|
||||
// ------------------------------------------------------------------------
|
||||
void setBool(const std::string &name, bool value) const
|
||||
{
|
||||
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setInt(const std::string &name, int value) const
|
||||
{
|
||||
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setFloat(const std::string &name, float value) const
|
||||
{
|
||||
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setVec2(const std::string &name, const glm::vec2 &value) const
|
||||
{
|
||||
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
|
||||
}
|
||||
void setVec2(const std::string &name, float x, float y) const
|
||||
{
|
||||
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setVec3(const std::string &name, const glm::vec3 &value) const
|
||||
{
|
||||
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
|
||||
}
|
||||
void setVec3(const std::string &name, float x, float y, float z) const
|
||||
{
|
||||
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setVec4(const std::string &name, const glm::vec4 &value) const
|
||||
{
|
||||
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
|
||||
}
|
||||
void setVec4(const std::string &name, float x, float y, float z, float w)
|
||||
{
|
||||
glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setMat2(const std::string &name, const glm::mat2 &mat) const
|
||||
{
|
||||
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setMat3(const std::string &name, const glm::mat3 &mat) const
|
||||
{
|
||||
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setMat4(const std::string &name, const glm::mat4 &mat) const
|
||||
{
|
||||
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
|
||||
}
|
||||
|
||||
private:
|
||||
// utility function for checking shader compilation/linking errors.
|
||||
// ------------------------------------------------------------------------
|
||||
void checkCompileErrors(GLuint shader, std::string type)
|
||||
{
|
||||
GLint success;
|
||||
GLchar infoLog[1024];
|
||||
if(type != "PROGRAM")
|
||||
{
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if(!success)
|
||||
{
|
||||
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
|
||||
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glGetProgramiv(shader, GL_LINK_STATUS, &success);
|
||||
if(!success)
|
||||
{
|
||||
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
|
||||
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
24
cmake/CPM.cmake
Normal file
24
cmake/CPM.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||
|
||||
set(CPM_DOWNLOAD_VERSION 0.38.7)
|
||||
set(CPM_HASH_SUM "83e5eb71b2bbb8b1f2ad38f1950287a057624e385c238f6087f94cdfc44af9c5")
|
||||
|
||||
if(CPM_SOURCE_CACHE)
|
||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
||||
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
else()
|
||||
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
endif()
|
||||
|
||||
# Expand relative path. This is important if the provided path contains a tilde (~)
|
||||
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
||||
|
||||
file(DOWNLOAD
|
||||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
||||
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
|
||||
)
|
||||
|
||||
include(${CPM_DOWNLOAD_LOCATION})
|
213
common/shader_utils.cpp
Normal file
213
common/shader_utils.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* From the OpenGL Programming wikibook: http://en.wikibooks.org/wiki/OpenGL_Programming
|
||||
* This file is in the public domain.
|
||||
* Contributors: Sylvain Beucler
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
/**
|
||||
* Store all the file's contents in memory, useful to pass shaders
|
||||
* source code to OpenGL
|
||||
*/
|
||||
char* file_read(const char* filename)
|
||||
{
|
||||
FILE* in = fopen(filename, "rb");
|
||||
if (in == NULL) return NULL;
|
||||
|
||||
int res_size = BUFSIZ;
|
||||
char* res = (char*)malloc(res_size);
|
||||
int nb_read_total = 0;
|
||||
|
||||
while (!feof(in) && !ferror(in)) {
|
||||
if (nb_read_total + BUFSIZ > res_size) {
|
||||
if (res_size > 10*1024*1024) break;
|
||||
res_size = res_size * 2;
|
||||
res = (char*)realloc(res, res_size);
|
||||
}
|
||||
char* p_res = res + nb_read_total;
|
||||
nb_read_total += fread(p_res, 1, BUFSIZ, in);
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
res = (char*)realloc(res, nb_read_total + 1);
|
||||
res[nb_read_total] = '\0';
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display compilation errors from the OpenGL shader compiler
|
||||
*/
|
||||
void print_log(GLuint object)
|
||||
{
|
||||
GLint log_length = 0;
|
||||
if (glIsShader(object))
|
||||
glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
|
||||
else if (glIsProgram(object))
|
||||
glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
|
||||
else {
|
||||
fprintf(stderr, "printlog: Not a shader or a program\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char* log = (char*)malloc(log_length);
|
||||
|
||||
if (glIsShader(object))
|
||||
glGetShaderInfoLog(object, log_length, NULL, log);
|
||||
else if (glIsProgram(object))
|
||||
glGetProgramInfoLog(object, log_length, NULL, log);
|
||||
|
||||
fprintf(stderr, "%s", log);
|
||||
free(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the shader from file 'filename', with error handling
|
||||
*/
|
||||
GLuint create_shader(const char* filename, GLenum type)
|
||||
{
|
||||
const GLchar* source = file_read(filename);
|
||||
if (source == NULL) {
|
||||
fprintf(stderr, "Error opening %s: ", filename); perror("");
|
||||
return 0;
|
||||
}
|
||||
GLuint res = glCreateShader(type);
|
||||
const GLchar* sources[] = {
|
||||
// Define GLSL version
|
||||
#ifdef GL_ES_VERSION_2_0
|
||||
"#version 100\n" // OpenGL ES 2.0
|
||||
#else
|
||||
"#version 120\n" // OpenGL 2.1
|
||||
#endif
|
||||
,
|
||||
// GLES2 precision specifiers
|
||||
#ifdef GL_ES_VERSION_2_0
|
||||
// Define default float precision for fragment shaders:
|
||||
(type == GL_FRAGMENT_SHADER) ?
|
||||
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
|
||||
"precision highp float; \n"
|
||||
"#else \n"
|
||||
"precision mediump float; \n"
|
||||
"#endif \n"
|
||||
: ""
|
||||
// Note: OpenGL ES automatically defines this:
|
||||
// #define GL_ES
|
||||
#else
|
||||
// Ignore GLES 2 precision specifiers:
|
||||
"#define lowp \n"
|
||||
"#define mediump\n"
|
||||
"#define highp \n"
|
||||
#endif
|
||||
,
|
||||
source };
|
||||
glShaderSource(res, 3, sources, NULL);
|
||||
free((void*)source);
|
||||
|
||||
glCompileShader(res);
|
||||
GLint compile_ok = GL_FALSE;
|
||||
glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
|
||||
if (compile_ok == GL_FALSE) {
|
||||
fprintf(stderr, "%s:", filename);
|
||||
print_log(res);
|
||||
glDeleteShader(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GLuint create_program(const char *vertexfile, const char *fragmentfile) {
|
||||
GLuint program = glCreateProgram();
|
||||
GLuint shader;
|
||||
|
||||
if(vertexfile) {
|
||||
shader = create_shader(vertexfile, GL_VERTEX_SHADER);
|
||||
if(!shader)
|
||||
return 0;
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
if(fragmentfile) {
|
||||
shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
|
||||
if(!shader)
|
||||
return 0;
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
GLint link_ok = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
|
||||
if (!link_ok) {
|
||||
fprintf(stderr, "glLinkProgram:");
|
||||
print_log(program);
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
#ifdef GL_GEOMETRY_SHADER
|
||||
GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) {
|
||||
GLuint program = glCreateProgram();
|
||||
GLuint shader;
|
||||
|
||||
if(vertexfile) {
|
||||
shader = create_shader(vertexfile, GL_VERTEX_SHADER);
|
||||
if(!shader)
|
||||
return 0;
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
if(geometryfile) {
|
||||
shader = create_shader(geometryfile, GL_GEOMETRY_SHADER);
|
||||
if(!shader)
|
||||
return 0;
|
||||
glAttachShader(program, shader);
|
||||
|
||||
glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, input);
|
||||
glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, output);
|
||||
glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, vertices);
|
||||
}
|
||||
|
||||
if(fragmentfile) {
|
||||
shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
|
||||
if(!shader)
|
||||
return 0;
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
GLint link_ok = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
|
||||
if (!link_ok) {
|
||||
fprintf(stderr, "glLinkProgram:");
|
||||
print_log(program);
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
#else
|
||||
GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) {
|
||||
fprintf(stderr, "Missing support for geometry shaders.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
GLint get_attrib(GLuint program, const char *name) {
|
||||
GLint attribute = glGetAttribLocation(program, name);
|
||||
if(attribute == -1)
|
||||
fprintf(stderr, "Could not bind attribute %s\n", name);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
GLint get_uniform(GLuint program, const char *name) {
|
||||
GLint uniform = glGetUniformLocation(program, name);
|
||||
if(uniform == -1)
|
||||
fprintf(stderr, "Could not bind uniform %s\n", name);
|
||||
return uniform;
|
||||
}
|
16
common/shader_utils.h
Normal file
16
common/shader_utils.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* From the OpenGL Programming wikibook: http://en.wikibooks.org/wiki/OpenGL_Programming
|
||||
* This file is in the public domain.
|
||||
* Contributors: Sylvain Beucler
|
||||
*/
|
||||
#ifndef _SHADER_UTILS_H
|
||||
#define _SHADER_UTILS_H
|
||||
#include <GL/glew.h>
|
||||
char* file_read(const char* filename);
|
||||
void print_log(GLuint object);
|
||||
GLuint create_shader(const char* filename, GLenum type);
|
||||
GLuint create_program(const char* vertexfile, const char *fragmentfile);
|
||||
GLuint create_gs_program(const char* vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices);
|
||||
GLint get_attrib(GLuint program, const char *name);
|
||||
GLint get_uniform(GLuint program, const char *name);
|
||||
#endif
|
220
main.cpp
Normal file
220
main.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GL/freeglut.h>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "common/shader_utils.h"
|
||||
|
||||
GLuint program;
|
||||
GLint attribute_coord;
|
||||
GLint uniform_tex;
|
||||
GLint uniform_color;
|
||||
|
||||
struct point {
|
||||
GLfloat x;
|
||||
GLfloat y;
|
||||
GLfloat s;
|
||||
GLfloat t;
|
||||
};
|
||||
|
||||
GLuint vbo;
|
||||
|
||||
FT_Library ft;
|
||||
FT_Face face;
|
||||
|
||||
const char *fontfilename;
|
||||
|
||||
int init_resources() {
|
||||
/* Initialize the FreeType2 library */
|
||||
if (FT_Init_FreeType(&ft)) {
|
||||
fprintf(stderr, "Could not init freetype library\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load a font */
|
||||
if (FT_New_Face(ft, fontfilename, 0, &face)) {
|
||||
fprintf(stderr, "Could not open font %s\n", fontfilename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
program = create_program("text.v.glsl", "text.f.glsl");
|
||||
if(program == 0)
|
||||
return 0;
|
||||
|
||||
attribute_coord = get_attrib(program, "coord");
|
||||
uniform_tex = get_uniform(program, "tex");
|
||||
uniform_color = get_uniform(program, "color");
|
||||
|
||||
if(attribute_coord == -1 || uniform_tex == -1 || uniform_color == -1)
|
||||
return 0;
|
||||
|
||||
// Create the vertex buffer object
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render text using the currently loaded font and currently set font size.
|
||||
* Rendering starts at coordinates (x, y), z is always 0.
|
||||
* The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy).
|
||||
*/
|
||||
void render_text(const char *text, float x, float y, float sx, float sy) {
|
||||
const char *p;
|
||||
FT_GlyphSlot g = face->glyph;
|
||||
|
||||
/* Create a texture that will be used to hold one "glyph" */
|
||||
GLuint tex;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glUniform1i(uniform_tex, 0);
|
||||
|
||||
/* We require 1 byte alignment when uploading texture data */
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
/* Clamping to edges is important to prevent artifacts when scaling */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
/* Linear filtering usually looks best for text */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
/* Set up the VBO for our vertex data */
|
||||
glEnableVertexAttribArray(attribute_coord);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
/* Loop through all characters */
|
||||
for (p = text; *p; p++) {
|
||||
/* Try to load and render the character */
|
||||
if (FT_Load_Char(face, *p, FT_LOAD_RENDER))
|
||||
continue;
|
||||
|
||||
/* Upload the "bitmap", which contains an 8-bit grayscale image, as an alpha texture */
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
|
||||
|
||||
/* Calculate the vertex and texture coordinates */
|
||||
float x2 = x + g->bitmap_left * sx;
|
||||
float y2 = -y - g->bitmap_top * sy;
|
||||
float w = g->bitmap.width * sx;
|
||||
float h = g->bitmap.rows * sy;
|
||||
|
||||
point box[4] = {
|
||||
{x2, -y2, 0, 0},
|
||||
{x2 + w, -y2, 1, 0},
|
||||
{x2, -y2 - h, 0, 1},
|
||||
{x2 + w, -y2 - h, 1, 1},
|
||||
};
|
||||
|
||||
/* Draw the character on the screen */
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof box, box, GL_DYNAMIC_DRAW);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
/* Advance the cursor to the start of the next character */
|
||||
x += (g->advance.x >> 6) * sx;
|
||||
y += (g->advance.y >> 6) * sy;
|
||||
}
|
||||
|
||||
glDisableVertexAttribArray(attribute_coord);
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
|
||||
void display() {
|
||||
float sx = 2.0 / glutGet(GLUT_WINDOW_WIDTH);
|
||||
float sy = 2.0 / glutGet(GLUT_WINDOW_HEIGHT);
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
/* White background */
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
/* Enable blending, necessary for our alpha texture */
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLfloat black[4] = { 0, 0, 0, 1 };
|
||||
GLfloat red[4] = { 1, 0, 0, 1 };
|
||||
GLfloat transparent_green[4] = { 0, 1, 0, 0.5 };
|
||||
|
||||
/* Set font size to 48 pixels, color to black */
|
||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
glUniform4fv(uniform_color, 1, black);
|
||||
|
||||
/* Effects of alignment */
|
||||
render_text("The Quick Brown Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 50 * sy, sx, sy);
|
||||
render_text("The Misaligned Fox Jumps Over The Lazy Dog", -1 + 8.5 * sx, 1 - 100.5 * sy, sx, sy);
|
||||
|
||||
/* Scaling the texture versus changing the font size */
|
||||
render_text("The Small Texture Scaled Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 175 * sy, sx * 0.5, sy * 0.5);
|
||||
FT_Set_Pixel_Sizes(face, 0, 24);
|
||||
render_text("The Small Font Sized Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 200 * sy, sx, sy);
|
||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
render_text("The Tiny Texture Scaled Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 235 * sy, sx * 0.25, sy * 0.25);
|
||||
FT_Set_Pixel_Sizes(face, 0, 12);
|
||||
render_text("The Tiny Font Sized Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 250 * sy, sx, sy);
|
||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
|
||||
/* Colors and transparency */
|
||||
render_text("The Solid Black Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 430 * sy, sx, sy);
|
||||
|
||||
glUniform4fv(uniform_color, 1, red);
|
||||
render_text("The Solid Red Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 330 * sy, sx, sy);
|
||||
render_text("The Solid Red Fox Jumps Over The Lazy Dog", -1 + 28 * sx, 1 - 450 * sy, sx, sy);
|
||||
|
||||
glUniform4fv(uniform_color, 1, transparent_green);
|
||||
render_text("The Transparent Green Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 380 * sy, sx, sy);
|
||||
render_text("The Transparent Green Fox Jumps Over The Lazy Dog", -1 + 18 * sx, 1 - 440 * sy, sx, sy);
|
||||
|
||||
glutSwapBuffers();
|
||||
}
|
||||
|
||||
void free_resources() {
|
||||
glDeleteProgram(program);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
glutInit(&argc, argv);
|
||||
glutInitContextVersion(2,0);
|
||||
glutInitDisplayMode(GLUT_RGB);
|
||||
glutInitWindowSize(640, 480);
|
||||
glutCreateWindow("Basic Text");
|
||||
|
||||
if (argc > 1)
|
||||
fontfilename = argv[1];
|
||||
else
|
||||
fontfilename = "FreeSans.ttf";
|
||||
|
||||
GLenum glew_status = glewInit();
|
||||
|
||||
if (GLEW_OK != glew_status) {
|
||||
fprintf(stderr, "Error: %s\n", glewGetErrorString(glew_status));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!GLEW_VERSION_2_0) {
|
||||
fprintf(stderr, "No support for OpenGL 2.0 found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (init_resources()) {
|
||||
glutDisplayFunc(display);
|
||||
glutMainLoop();
|
||||
}
|
||||
|
||||
free_resources();
|
||||
return 0;
|
||||
}
|
12
shader.fs
Normal file
12
shader.fs
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 330 core
|
||||
in vec2 TexCoords;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D text;
|
||||
uniform vec3 textColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
|
||||
color = vec4(textColor, 1.0) * sampled;
|
||||
}
|
11
shader.vs
Normal file
11
shader.vs
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
|
||||
TexCoords = vertex.zw;
|
||||
}
|
7
text.f.glsl
Normal file
7
text.f.glsl
Normal file
@@ -0,0 +1,7 @@
|
||||
varying vec2 texpos;
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(1, 1, 1, texture2D(tex, texpos).a) * color;
|
||||
}
|
8
text.v.glsl
Normal file
8
text.v.glsl
Normal file
@@ -0,0 +1,8 @@
|
||||
attribute vec4 coord;
|
||||
varying vec2 texpos;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = vec4(coord.xy, 0, 1);
|
||||
texpos = coord.zw;
|
||||
}
|
||||
|
Reference in New Issue
Block a user