├── 3rdParty.rar ├── README.md ├── Render_with_OpenGL ├── include │ ├── Camera.h │ ├── Light.h │ ├── Mesh.h │ ├── Shader.h │ └── Texture2D.h ├── shader │ ├── text3D.frag │ └── text3D.vert └── src │ └── RenderText3D.cpp ├── Text3D ├── include │ ├── FreeTypeFont.h │ ├── Glyph3D.h │ ├── Tessellator.h │ └── TriangleIndexFunctor.h └── src │ ├── Glyph3D.cpp │ └── Tessellator.cpp └── fonts ├── STXINWEI.TTF ├── arial.ttf └── simhei.ttf /3rdParty.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/3rdParty.rar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Text3D_with_OpenGL 2 | Render 3D Texts with OpenGL! 3 | 4 | 这是一个用OpenGL渲染3D文字的程序,想要全面了解其原理,需要具备以下知识: 5 | 6 | 1. C/C++ 7 | 2. OpenGL & GLSL(OpenGL基本的绘制方式以及顶点着色器和片元着色器) 8 | 3. 计算机图形学(二次贝塞尔曲线) 9 | 4. FreeType2(涉及FT_Outline_Decompose函数,需要了解字形轮廓的分解) 10 | 5. 计算几何相关算法(多边形的三角剖分) 11 | 12 | 当前的实现版本使用GLU库进行三角剖分,不过GLU库非常古老,不太好用了~不久后将会实现我们自己的三角剖分程序! 13 | 14 | 先来看一下基本的流程: 15 | 1. 使用FreeType2获取字形的轮廓信息,我们将获取到的信息封装为FreeTypeFont这个类型; 16 | 2. 使用GLU库对获取到的字形轮廓进行多边形的三角剖分,生成三角网格面片,注意,这时的字形还不是3D文字,而是只有一个面; 17 | 3. 使用在上一步生成的字形三角网格面生成3D文字的背面和侧面; 18 | 4. 使用OpenGL对3D文字模型进行渲染; 19 | 20 | 注: 21 | 22 | 所有第三方依赖库都放在了3rdParty.rar里面,而实现文件都在Text3D文件夹下面! 23 | 24 | FreeType的文档请看这里:https://www.freetype.org/freetype2/docs/documentation.html 25 | 26 | 在处理图形学中的数学类型时,我们使用了glm库,参考这里:https://github.com/g-truc/glm/tags 27 | 28 | GLU库的文档可以看这里https://khronos.org/registry/OpenGL/specs/gl/glu1.3.pdf 29 | 30 | Windows系统的字体文件可以在C:\Windows\Fonts路径下找到! 31 | 32 | Text3D工程中需要包含的第三方依赖库如下图: 33 | 34 | 35 | 36 | Render_with_OpenGL工程中需要包含的第三方依赖库如下图: 37 | 38 | 39 | 40 | 其中opengl32.lib和glu32.lib是已经在Windows系统中存在的了,其他系统尚不清楚,抱歉~ 41 | 42 | 下图为实现效果: 43 | 44 | 45 | 46 | 项目仍在持续更行中,敬请期待! 47 | 48 | 文档尚未完成,README NOT COMPLETED YET! 49 | -------------------------------------------------------------------------------- /Render_with_OpenGL/include/Camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | enum CameraMovement{ FORWARD, BACKWARD, LEFT, RIGHT }; 12 | 13 | //the euler angle and camera option's initialize value 14 | const float YAW = -90.0f; 15 | const float PITCH = 0.0f; 16 | const float SPEED = 2.5f; 17 | const float SENSITIVITY = 0.1f; 18 | const float ZOOM = 45.0f; 19 | 20 | //this is an abstract base class 21 | class BaseCamera 22 | { 23 | public: 24 | glm::vec3 position; //camera's position 25 | glm::vec3 front; //camera's front vector 26 | glm::vec3 up; //camera's up vector 27 | glm::vec3 right; //camera's right vector 28 | glm::vec3 worldUp; //world's up vector, it's [0, 1, 0] commonly 29 | 30 | GLfloat pitch; 31 | GLfloat yaw; 32 | 33 | GLfloat moveSpeed; 34 | GLfloat mouseSensitivity; 35 | GLfloat zoom; 36 | 37 | BaseCamera(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f), 38 | const glm::vec3 &u = glm::vec3(0.0f, 1.0f, 0.0f), 39 | GLfloat p = PITCH, GLfloat y = YAW); 40 | //get view matrix, it is the essential of an opengl camera 41 | glm::mat4 getViewMatrix(); 42 | //camera movement 43 | virtual void processKeyboard(CameraMovement direction, GLfloat deltaTime) = 0; 44 | //camera rotation 45 | virtual void processMouseMovement(GLfloat x, GLfloat y); 46 | //camera' zoom 47 | virtual void processMouseScroll(GLfloat z); 48 | 49 | private: 50 | //according to the eular angles to construct the camera's coordinate 51 | void updateCamera(); 52 | }; 53 | 54 | BaseCamera::BaseCamera(const glm::vec3 &pos, const glm::vec3 &u, GLfloat p, GLfloat y) : 55 | position(pos), worldUp(u), pitch(p), yaw(y), moveSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM) 56 | { 57 | updateCamera(); 58 | } 59 | 60 | void BaseCamera::updateCamera() 61 | { 62 | front.x = std::cos(glm::radians(pitch)) * std::cos(glm::radians(yaw)); 63 | front.y = std::sin(glm::radians(pitch)); 64 | front.z = std::cos(glm::radians(pitch)) * std::sin(glm::radians(yaw)); 65 | front = glm::normalize(front); 66 | 67 | right = glm::normalize(glm::cross(front, worldUp)); 68 | up = glm::normalize(glm::cross(right, front)); 69 | } 70 | 71 | glm::mat4 BaseCamera::getViewMatrix() 72 | { 73 | return glm::lookAt(position, position + front, up); 74 | } 75 | 76 | void BaseCamera::processKeyboard(CameraMovement direction, GLfloat deltaTime) 77 | { 78 | GLfloat v = moveSpeed * deltaTime; 79 | switch (direction) 80 | { 81 | case FORWARD: 82 | position += front * v; 83 | break; 84 | case BACKWARD: 85 | position -= front * v; 86 | break; 87 | case LEFT: 88 | position -= right * v; 89 | break; 90 | case RIGHT: 91 | position += right * v; 92 | break; 93 | default: 94 | break; 95 | } 96 | } 97 | 98 | void BaseCamera::processMouseMovement(GLfloat deltaX, GLfloat deltaY) 99 | { 100 | //the x-oriented position's difference in one frame 101 | deltaX *= mouseSensitivity; 102 | //the y-oriented position's difference in one frame 103 | deltaY *= mouseSensitivity; 104 | 105 | yaw += deltaX; 106 | pitch += deltaY; 107 | 108 | if (pitch > 89.0f) 109 | pitch = 89.0f; 110 | else if (pitch < -89.0f) 111 | pitch = -89.0f; 112 | //because we changed eular angle, we update the camera's coordinate 113 | updateCamera(); 114 | } 115 | 116 | void BaseCamera::processMouseScroll(GLfloat z) 117 | { 118 | if (zoom >= 1.0f && zoom <= 45.0f) 119 | zoom -= z; 120 | if (zoom < 1.0f) 121 | zoom = 1.0f; 122 | if (zoom > 45.0f) 123 | zoom = 45.0f; 124 | } 125 | 126 | //this is a free camera that can move everywhere in the space 127 | class FreeCamera : public BaseCamera 128 | { 129 | public: 130 | FreeCamera(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f), 131 | const glm::vec3 &u = glm::vec3(0.0f, 1.0f, 0.0f), 132 | GLfloat p = PITCH, GLfloat y = YAW); 133 | void processKeyboard(CameraMovement direction, GLfloat deltaTime) override; 134 | }; 135 | 136 | FreeCamera::FreeCamera(const glm::vec3 &pos, const glm::vec3 &u, GLfloat p, GLfloat y) : BaseCamera(pos, u, p, y){} 137 | 138 | void FreeCamera::processKeyboard(CameraMovement direction, GLfloat deltaTime) 139 | { 140 | BaseCamera::processKeyboard(direction, deltaTime); 141 | } 142 | 143 | //this is a fps camera that can move only on the ground(the plane which y = 0) 144 | class FPSCamera : public BaseCamera 145 | { 146 | public: 147 | FPSCamera::FPSCamera(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f), 148 | const glm::vec3 &u = glm::vec3(0.0f, 1.0f, 0.0f), 149 | GLfloat p = PITCH, GLfloat y = YAW); 150 | void processKeyboard(CameraMovement direction, GLfloat deltaTime) override; 151 | }; 152 | 153 | FPSCamera::FPSCamera(const glm::vec3 &pos, const glm::vec3 &u, GLfloat p, GLfloat y) : BaseCamera(pos, u, p, y){} 154 | 155 | void FPSCamera::processKeyboard(CameraMovement direction, GLfloat deltaTime) 156 | { 157 | BaseCamera::processKeyboard(direction, deltaTime); 158 | //we cannot leave the ground 159 | position.y = 0; 160 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/include/Light.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | //attenuation struct 8 | struct Attenuation 9 | { 10 | GLfloat constant; 11 | GLfloat linear; 12 | GLfloat quadratic; 13 | }; 14 | //cutoff struct 15 | struct Cutoff 16 | { 17 | //cutoff angle 18 | GLfloat innerCutoff; 19 | GLfloat outerCutoff; 20 | }; 21 | 22 | class DirLight 23 | { 24 | public: 25 | glm::vec3 direction; 26 | glm::vec3 ambient; 27 | glm::vec3 diffuse; 28 | glm::vec3 specular; 29 | 30 | void setUniform(GLuint id, GLuint offset); 31 | }; 32 | 33 | class PointLight 34 | { 35 | public: 36 | glm::vec3 position; 37 | 38 | glm::vec3 ambient; 39 | glm::vec3 diffuse; 40 | glm::vec3 specular; 41 | 42 | //the attenuation factor 43 | Attenuation atten; 44 | 45 | void setUniform(GLuint id, GLuint offset); 46 | }; 47 | 48 | class SpotLight 49 | { 50 | public: 51 | glm::vec3 position; 52 | glm::vec3 direction; 53 | 54 | glm::vec3 ambient; 55 | glm::vec3 diffuse; 56 | glm::vec3 specular; 57 | //the attenuation factor 58 | Attenuation atten; 59 | //cutoff angle 60 | Cutoff cutoff; 61 | 62 | void setUniform(GLuint id, GLuint offset); 63 | }; 64 | 65 | void DirLight::setUniform(GLuint id, GLuint offset) 66 | { 67 | glBindBuffer(GL_UNIFORM_BUFFER, id); 68 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 0, 16, glm::value_ptr(direction)); 69 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 16, 16, glm::value_ptr(ambient)); 70 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 32, 16, glm::value_ptr(diffuse)); 71 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 48, 16, glm::value_ptr(specular)); 72 | glBindBuffer(GL_UNIFORM_BUFFER, 0); 73 | } 74 | 75 | void PointLight::setUniform(GLuint id, GLuint offset) 76 | { 77 | glBindBuffer(GL_UNIFORM_BUFFER, id); 78 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 0, 16, glm::value_ptr(position)); 79 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 16, 16, glm::value_ptr(ambient)); 80 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 32, 16, glm::value_ptr(diffuse)); 81 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 48, 16, glm::value_ptr(specular)); 82 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 64, 4, &atten.constant); 83 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 68, 4, &atten.linear); 84 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 72, 4, &atten.quadratic); 85 | glBindBuffer(GL_UNIFORM_BUFFER, 0); 86 | } 87 | 88 | void SpotLight::setUniform(GLuint id, GLuint offset) 89 | { 90 | glBindBuffer(GL_UNIFORM_BUFFER, id); 91 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 0, 16, glm::value_ptr(position)); 92 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 16, 16, glm::value_ptr(direction)); 93 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 32, 16, glm::value_ptr(ambient)); 94 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 48, 16, glm::value_ptr(diffuse)); 95 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 64, 16, glm::value_ptr(specular)); 96 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 80, 4, &atten.constant); 97 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 84, 4, &atten.linear); 98 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 88, 4, &atten.quadratic); 99 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 96, 4, &cutoff.innerCutoff); 100 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 100, 4, &cutoff.outerCutoff); 101 | glBindBuffer(GL_UNIFORM_BUFFER, 0); 102 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/include/Mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | struct Vertex 14 | { 15 | glm::vec3 position; 16 | glm::vec3 normal; 17 | glm::vec2 texCoord; 18 | 19 | /*Vertex(const glm::vec3 &pos, const glm::vec3 &n, const glm::vec2 &tex = glm::vec2()) : 20 | position(pos), normal(n), texCoord(tex) {}*/ 21 | }; 22 | 23 | class Mesh 24 | { 25 | public: 26 | std::vector vertices; 27 | std::vector indices; 28 | std::vector textures; 29 | 30 | GLuint VAO, VBO, EBO; 31 | 32 | explicit Mesh(const std::vector &v, const std::vector &i = {}, const std::vector &t = {}); 33 | void draw(Shader shader) const; 34 | 35 | private: 36 | void setupVAO(); 37 | }; 38 | 39 | Mesh::Mesh(const std::vector &v, const std::vector &i, const std::vector &t) : 40 | vertices(v), indices(i), textures(t) 41 | { 42 | setupVAO(); 43 | } 44 | 45 | void Mesh::draw(Shader shader) const 46 | { 47 | unsigned diffNum = 1, specNum = 1; 48 | for (size_t i = 0; i < textures.size(); ++i) 49 | { 50 | glActiveTexture(GL_TEXTURE0 + i); 51 | int n = 0; 52 | std::string t = textures[i].type; 53 | //we use n to judge the texture's name in shader 54 | //in shader we must named textures as "texture_diffuseN" or "texture_specularN" or others(N is start from 1) 55 | if (t == "texture_diffuse") 56 | n = diffNum++; 57 | else if (t == "texture_specular") 58 | n = specNum++; 59 | std::string N = std::to_string(n); 60 | //don't forget "material.", in shader the sampler of texture is in Material structure 61 | shader.setUniformInt("material." + t + N, i); 62 | glBindTexture(GL_TEXTURE_2D, textures[i].id); 63 | } 64 | 65 | glBindVertexArray(VAO); 66 | if (!indices.empty()) 67 | glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); 68 | else 69 | glDrawArrays(GL_TRIANGLES, 0, vertices.size()); 70 | glBindVertexArray(0); 71 | 72 | glActiveTexture(GL_TEXTURE0); 73 | } 74 | 75 | void Mesh::setupVAO() 76 | { 77 | if (vertices.empty()) 78 | { 79 | std::cerr << "Vertices load failed!" << std::endl; 80 | return; 81 | } 82 | 83 | glGenVertexArrays(1, &VAO); 84 | glGenBuffers(1, &VBO); 85 | glGenBuffers(1, &EBO); 86 | 87 | glBindVertexArray(VAO); 88 | glBindBuffer(GL_ARRAY_BUFFER, VBO); 89 | glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); 90 | if (!indices.empty()) 91 | { 92 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 93 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW); 94 | } 95 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); 96 | glEnableVertexAttribArray(0); 97 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); 98 | glEnableVertexAttribArray(1); 99 | glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoord)); 100 | glEnableVertexAttribArray(2); 101 | glBindVertexArray(0); 102 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/include/Shader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class Shader 14 | { 15 | public: 16 | GLuint program; 17 | 18 | Shader(const std::string &vPath, const std::string &fPath); 19 | void use() const; 20 | void setUniformBool(const std::string &name, GLboolean value) const; 21 | void setUniformInt(const std::string &name, GLint value) const; 22 | void setUniformFloat(const std::string &name, GLfloat value) const; 23 | void setUniformVec3(const std::string &name, const glm::vec3 &v); 24 | void setUniformMat4(const std::string &name, const glm::mat4 &m); 25 | 26 | private: 27 | void checkErro(GLuint shader, const std::string &type) const; 28 | }; 29 | 30 | Shader::Shader(const std::string &vPath, const std::string &fPath) 31 | { 32 | //read the code from shader file 33 | std::ifstream vertFile, fragFile; 34 | std::string vertCode, fragCode; 35 | vertFile.exceptions(std::ifstream::badbit | std::ifstream::failbit); 36 | fragFile.exceptions(std::ifstream::badbit | std::ifstream::failbit); 37 | try 38 | { 39 | vertFile.open(vPath); 40 | fragFile.open(fPath); 41 | std::stringstream vStream, fStream; 42 | vStream << vertFile.rdbuf(); 43 | fStream << fragFile.rdbuf(); 44 | vertFile.close(); 45 | fragFile.close(); 46 | vertCode = vStream.str(); 47 | fragCode = fStream.str(); 48 | } 49 | catch (std::ifstream::failure e) 50 | { 51 | std::cerr << "ERRO::SHADER::FILE_NOT_SUCCESSFULLY_READ"; 52 | std::abort(); 53 | } 54 | const char *vc = vertCode.c_str(); 55 | const char *fc = fragCode.c_str(); 56 | GLint success = 0; 57 | char infoLog[512] = {}; 58 | //compile vertex shader 59 | GLuint vshader = glCreateShader(GL_VERTEX_SHADER); 60 | glShaderSource(vshader, 1, &vc, nullptr); 61 | glCompileShader(vshader); 62 | checkErro(vshader, "VERTEX"); 63 | 64 | //compile fragment shader 65 | GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); 66 | glShaderSource(fshader, 1, &fc, nullptr); 67 | glCompileShader(fshader); 68 | checkErro(fshader, "FRAGMENT"); 69 | 70 | //create shader program and link the shaders 71 | program = glCreateProgram(); 72 | glAttachShader(program, vshader); 73 | glAttachShader(program, fshader); 74 | glLinkProgram(program); 75 | checkErro(program, "PROGRAM"); 76 | 77 | glDeleteShader(vshader); 78 | glDeleteShader(fshader); 79 | } 80 | 81 | void Shader::use() const 82 | { 83 | glUseProgram(program); 84 | } 85 | 86 | void Shader::setUniformBool(const std::string &name, GLboolean value) const 87 | { 88 | glUniform1i(glGetUniformLocation(program, name.c_str()), value); 89 | } 90 | void Shader::setUniformInt(const std::string &name, GLint value) const 91 | { 92 | glUniform1i(glGetUniformLocation(program, name.c_str()), value); 93 | } 94 | 95 | void Shader::setUniformFloat(const std::string &name, GLfloat value) const 96 | { 97 | glUniform1f(glGetUniformLocation(program, name.c_str()), value); 98 | } 99 | 100 | void Shader::setUniformVec3(const std::string &name, const glm::vec3 &v) 101 | { 102 | glUniform3fv(glGetUniformLocation(program, name.c_str()), 1, glm::value_ptr(v)); 103 | } 104 | 105 | void Shader::setUniformMat4(const std::string &name, const glm::mat4 &m) 106 | { 107 | glUniformMatrix4fv(glGetUniformLocation(program, name.c_str()), 1, GL_FALSE, glm::value_ptr(m)); 108 | } 109 | 110 | void Shader::checkErro(GLuint shader, const std::string &type) const 111 | { 112 | GLint success = 0; 113 | char infoLog[512] = {}; 114 | 115 | if (type != "PROGRAM") 116 | { 117 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 118 | if (!success) 119 | { 120 | glGetShaderInfoLog(shader, 512, nullptr, infoLog); 121 | std::cerr << "ERRO::SHADER::" << type << "::COMPILATION_FAILD\n" << infoLog; 122 | std::abort(); 123 | } 124 | } 125 | else 126 | { 127 | glGetProgramiv(shader, GL_LINK_STATUS, &success); 128 | if (!success) 129 | { 130 | glGetProgramInfoLog(shader, 512, nullptr, infoLog); 131 | std::cerr << "ERRO::PROGRAM::LINKED_FAILD" << infoLog; 132 | std::abort(); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/include/Texture2D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | struct Texture2D 11 | { 12 | GLuint id; 13 | std::string type; //texture's type(diffuse texture or specular texture or others) 14 | std::string path; //texture source image's file path(this member is used to identify the texture's source image) 15 | 16 | void loadFromFile(const std::string p); 17 | }; 18 | 19 | void Texture2D::loadFromFile(const std::string p) 20 | { 21 | glGenTextures(1, &id); 22 | 23 | int width, height, nrComponents; 24 | unsigned char *data = stbi_load(p.c_str(), &width, &height, &nrComponents, 0); 25 | if (data) 26 | { 27 | GLenum format; 28 | switch (nrComponents) 29 | { 30 | case 1: 31 | format = GL_RED; 32 | break; 33 | case 3: 34 | format = GL_RGB; 35 | break; 36 | case 4: 37 | format = GL_RGBA; 38 | break; 39 | default: 40 | break; 41 | } 42 | glBindTexture(GL_TEXTURE_2D, id); 43 | glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); 44 | glGenerateMipmap(GL_TEXTURE_2D); 45 | 46 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 47 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 48 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 49 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 50 | } 51 | else 52 | { 53 | std::cerr << "Failed to load image from file: " << p << std::endl; 54 | } 55 | stbi_image_free(data); 56 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/shader/text3D.frag: -------------------------------------------------------------------------------- 1 | #version 420 core 2 | 3 | in vec3 Normal; 4 | in vec3 FragPos; 5 | in vec2 TexCoord; 6 | 7 | out vec4 fragColor; 8 | 9 | struct Material 10 | { 11 | vec3 ambient; 12 | vec3 diffuse; 13 | vec3 specular; 14 | float shininess; 15 | }; 16 | 17 | struct Attenuation 18 | { 19 | float constant; 20 | float linear; 21 | float quadratic; 22 | }; 23 | 24 | struct Cutoff 25 | { 26 | float cutoff; 27 | float outCutoff; 28 | }; 29 | 30 | struct DirectionalLight 31 | { 32 | vec3 direction; 33 | 34 | vec3 ambient; 35 | vec3 diffuse; 36 | vec3 specular; 37 | }; 38 | 39 | struct PointLight 40 | { 41 | vec3 position; 42 | 43 | vec3 ambient; 44 | vec3 diffuse; 45 | vec3 specular; 46 | 47 | //the attenuation factor 48 | Attenuation atten; 49 | }; 50 | 51 | struct SpotLight 52 | { 53 | vec3 position; 54 | vec3 direction; 55 | 56 | vec3 ambient; 57 | vec3 diffuse; 58 | vec3 specular; 59 | 60 | //the attenuation factor 61 | Attenuation atten; 62 | 63 | //cutoff angle 64 | Cutoff co; 65 | }; 66 | 67 | const int PointLightNum = 1; 68 | const int SpotLightNum = 1; 69 | 70 | uniform Material material; 71 | 72 | layout(std140, binding = 1) uniform Lights 73 | { 74 | DirectionalLight dirLight; 75 | PointLight pointLights[PointLightNum]; 76 | SpotLight torch[SpotLightNum]; 77 | }; 78 | 79 | uniform vec3 viewPos; 80 | 81 | float diff(vec3 lightDir, vec3 normal); 82 | float spec(vec3 lightDir, vec3 normal, vec3 viewDir, float shininess); 83 | 84 | vec3 calcDirLight(DirectionalLight light, vec3 normal, vec3 viewPos); 85 | vec3 calcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewPos); 86 | vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewPos); 87 | 88 | void main() 89 | { 90 | vec3 result; 91 | result += calcDirLight(dirLight, Normal, viewPos); 92 | for(int i = 0; i < PointLightNum; ++i) 93 | result += calcPointLight(pointLights[i], Normal, FragPos, viewPos); 94 | for(int i = 0; i < SpotLightNum; ++i) 95 | result += calcSpotLight(torch[i], Normal, FragPos, viewPos); 96 | 97 | fragColor = vec4(result, 1.0f); 98 | } 99 | 100 | float diff(vec3 lightDir, vec3 normal) 101 | { 102 | return max(dot(lightDir, normal), 0.0f); 103 | } 104 | 105 | float spec(vec3 lightDir, vec3 normal, vec3 viewDir, float shininess) 106 | { 107 | vec3 rd = reflect(-lightDir, normal); 108 | return pow(max(dot(rd, viewDir), 0.0f), shininess); 109 | } 110 | 111 | vec3 calcDirLight(DirectionalLight light, vec3 normal, vec3 viewPos) 112 | { 113 | vec3 ld = normalize(-light.direction); 114 | vec3 n = normalize(normal); 115 | vec3 vd = normalize(viewPos - FragPos); 116 | vec3 ambient = light.ambient * material.ambient; 117 | vec3 diffuse = light.diffuse * diff(ld, n) * material.diffuse; 118 | vec3 specular = light.specular * spec(ld, n, vd, material.shininess) * material.specular; 119 | 120 | return ambient + diffuse + specular; 121 | } 122 | 123 | vec3 calcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewPos) 124 | { 125 | vec3 ld = normalize(light.position - fragPos); 126 | vec3 n = normalize(normal); 127 | vec3 vd = normalize(viewPos - FragPos); 128 | 129 | //calculate attenuation 130 | float d = length(light.position - fragPos); 131 | float atten = 1.0f / (light.atten.constant + light.atten.linear * d + light.atten.quadratic * d * d); 132 | 133 | vec3 ambient = light.ambient * material.ambient; 134 | vec3 diffuse = light.diffuse * diff(ld, n) * material.diffuse; 135 | vec3 specular = light.specular * spec(ld, n, vd, material.shininess) * material.specular; 136 | 137 | return (ambient + diffuse + specular) * atten; 138 | } 139 | 140 | vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewPos) 141 | { 142 | vec3 ld = normalize(light.position - fragPos); 143 | vec3 n = normalize(normal); 144 | vec3 vd = normalize(viewPos - FragPos); 145 | 146 | //calculate cutoff 147 | float theta = dot(-ld, light.direction); 148 | float epsilon = light.co.cutoff - light.co.outCutoff; 149 | float iten = clamp((theta - light.co.outCutoff) / epsilon, 0.0f, 1.0f); 150 | 151 | //calculate attenuation 152 | float d = length(light.position - fragPos); 153 | float atten = 1.0f / (light.atten.constant + light.atten.linear * d + light.atten.quadratic * d * d); 154 | 155 | vec3 ambient = light.ambient * material.ambient; 156 | vec3 diffuse = light.diffuse * diff(ld, n) * material.diffuse; 157 | vec3 specular = light.specular * spec(ld, n, vd, material.shininess) * material.specular; 158 | 159 | return (ambient + (diffuse + specular) * iten) * atten; 160 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/shader/text3D.vert: -------------------------------------------------------------------------------- 1 | #version 420 core 2 | 3 | layout(location = 0) in vec3 vPosition; 4 | layout(location = 1) in vec3 vNormal; 5 | layout(location = 2) in vec2 vTexCoord; 6 | 7 | out vec3 Normal; 8 | out vec3 FragPos; 9 | out vec2 TexCoord; 10 | 11 | uniform mat4 model; 12 | layout(std140, binding = 0) uniform Matrix 13 | { 14 | mat4 view; 15 | mat4 projection; 16 | }; 17 | 18 | void main() 19 | { 20 | gl_Position = projection * view * model * vec4(vPosition, 1.0f); 21 | FragPos = vec3(model * vec4(vPosition, 1.0f)); 22 | Normal = mat3(model) * vNormal; 23 | TexCoord = vTexCoord; 24 | } -------------------------------------------------------------------------------- /Render_with_OpenGL/src/RenderText3D.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/Render_with_OpenGL/src/RenderText3D.cpp -------------------------------------------------------------------------------- /Text3D/include/FreeTypeFont.h: -------------------------------------------------------------------------------- 1 | //use freetype2 to decompose the glyph's outline 2 | //a distinction should be made between the concept of outline and contour 3 | //outline is the glyph's whole outline and contours are the seperated lines in the outline 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | //glm 11 | #include 12 | 13 | //FreeType 14 | #include 15 | #include FT_FREETYPE_H 16 | #include FT_OUTLINE_H 17 | 18 | #include "Glyph3D.h" 19 | 20 | namespace FreeType 21 | { 22 | //this class used to store the vertices and contour list of the original glyph 23 | //note that the outline is consist of many coutours so the contour list is an array of an unsigned array 24 | struct Char3DInfo 25 | { 26 | Vec3Array vertices; //store the postions of glyph vertex 27 | ElementArray current_indices; //store the indices of coutour 28 | std::vector contour_list; //contour's indices list 29 | glm::vec3 previous; //the previous vertex of current vertex(use to sample Bezier) 30 | unsigned int num_steps; //number of step to sample the Bezier 31 | float coord_scale; //freetype use the 1/64 pixel format means we shoule set this value to 1/64 32 | 33 | Char3DInfo() : num_steps(10), coord_scale(1.0 / 64.0) {} 34 | 35 | void completeCurrentContour() 36 | { 37 | if (!vertices.empty() && !current_indices.empty()) 38 | contour_list.push_back(current_indices); 39 | current_indices.clear(); 40 | } 41 | //return the data to Glyph3D to do triangulation 42 | Glyph3D get() 43 | { 44 | completeCurrentContour(); 45 | return Glyph3D(vertices, contour_list); 46 | } 47 | 48 | void addVertex(glm::vec3 pos) 49 | { 50 | previous = pos; 51 | pos *= coord_scale; 52 | //no need to add the same vertex 53 | if (!vertices.empty() && vertices.back() == pos) 54 | return; 55 | //for the closed contour 56 | if (!current_indices.empty() && vertices[current_indices[0]] == pos) 57 | current_indices.push_back(current_indices[0]); 58 | else 59 | { 60 | current_indices.push_back(vertices.size()); 61 | vertices.push_back(pos); 62 | } 63 | } 64 | //move to a new contour 65 | void moveTo(const glm::vec2 &pos) 66 | { 67 | completeCurrentContour(); 68 | addVertex(glm::vec3(pos.x, pos.y, 0)); 69 | } 70 | //draw line segment 71 | void lineTo(const glm::vec2 &pos) 72 | { 73 | addVertex(glm::vec3(pos.x, pos.y, 0)); 74 | } 75 | //draw conic Bezier 76 | void conicTo(const glm::vec2 &control, const glm::vec2 &pos) 77 | { 78 | glm::vec3 p0 = previous; 79 | glm::vec3 p1 = glm::vec3(control.x, control.y, 0); 80 | glm::vec3 p2 = glm::vec3(pos.x, pos.y, 0); 81 | 82 | GLfloat dt = 1.0 / num_steps; 83 | GLfloat u = 0; 84 | for (unsigned int i = 0; i <= num_steps; ++i) 85 | { 86 | GLfloat w = 1; 87 | GLfloat bs = 1.0 / ((1 - u)*(1 - u) + 2 * (1 - u)*u*w + u*u); 88 | glm::vec3 p = (p0*((1 - u)*(1 - u)) + p1*(2 * (1 - u)*u*w) + p2*(u*u)) * bs; 89 | addVertex(p); 90 | 91 | u += dt; 92 | } 93 | } 94 | //draw cubic Bezier 95 | void cubicTo(const glm::vec2 &control1, const glm::vec2 &control2, const glm::vec2 &pos) 96 | { 97 | glm::vec3 p0 = previous; 98 | glm::vec3 p1 = glm::vec3(control1.x, control1.y, 0); 99 | glm::vec3 p2 = glm::vec3(control2.x, control2.y, 0); 100 | glm::vec3 p3 = glm::vec3(pos.x, pos.y, 0); 101 | 102 | GLfloat cx = 3 * (p1.x - p0.x); 103 | GLfloat bx = 3 * (p2.x - p1.x) - cx; 104 | GLfloat ax = p3.x - p0.x - cx - bx; 105 | GLfloat cy = 3 * (p1.y - p0.y); 106 | GLfloat by = 3 * (p2.y - p1.y) - cy; 107 | GLfloat ay = p3.y - p0.y - cy - by; 108 | 109 | GLfloat dt = 1.0 / num_steps; 110 | GLfloat u = 0; 111 | for (unsigned int i = 0; i <= num_steps; ++i) 112 | { 113 | glm::vec3 p = glm::vec3(ax*u*u*u + bx*u*u + cx*u + p0.x, ay*u*u*u + by*u*u + cy*u + p0.y, 0); 114 | addVertex(p); 115 | 116 | u += dt; 117 | } 118 | } 119 | }; //end of Char3DInfo 120 | 121 | //callback function use to decompose outline 122 | //return 0 means successful 123 | int moveTo(const FT_Vector* to, void* user) 124 | { 125 | Char3DInfo* char3d = (Char3DInfo*)user; 126 | char3d->moveTo(glm::vec2(to->x, to->y)); 127 | return 0; 128 | } 129 | int lineTo(const FT_Vector* to, void* user) 130 | { 131 | Char3DInfo* char3d = (Char3DInfo*)user; 132 | char3d->lineTo(glm::vec2(to->x, to->y)); 133 | return 0; 134 | } 135 | int conicTo(const FT_Vector* control, const FT_Vector* to, void* user) 136 | { 137 | Char3DInfo* char3d = (Char3DInfo*)user; 138 | char3d->conicTo(glm::vec2(control->x, control->y), glm::vec2(to->x, to->y)); 139 | return 0; 140 | } 141 | int cubicTo(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user) 142 | { 143 | Char3DInfo* char3d = (Char3DInfo*)user; 144 | char3d->cubicTo( 145 | glm::vec2(control1->x, control1->y), 146 | glm::vec2(control2->x, control2->y), 147 | glm::vec2(to->x, to->y)); 148 | return 0; 149 | } 150 | } //close namespace FreeType 151 | 152 | 153 | class FreeTypeFont 154 | { 155 | public: 156 | FreeTypeFont(wchar_t code, GLchar *font_file); 157 | ~FreeTypeFont() 158 | { 159 | FT_Done_Face(face); 160 | FT_Done_FreeType(ft); 161 | } 162 | Glyph3D getGlyph3D(); 163 | GLuint advanceX() const { return AdvanceX; } 164 | 165 | private: 166 | wchar_t charcode; 167 | FT_Library ft; 168 | FT_Face face; 169 | GLuint AdvanceX; 170 | FreeType::Char3DInfo char3d; 171 | }; 172 | 173 | FreeTypeFont::FreeTypeFont(wchar_t code, char *font_file) 174 | { 175 | if (FT_Init_FreeType(&ft)) 176 | std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl; 177 | if (FT_New_Face(ft, font_file, 0, &face)) 178 | std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; 179 | FT_Set_Pixel_Sizes(face, 0, 24); 180 | 181 | charcode = code; 182 | //load character face 183 | FT_Error error = FT_Load_Char(face, charcode, FT_LOAD_DEFAULT); 184 | if (error) 185 | std::cout << "FT_Load_Char(...) error 0x" << std::hex << error << std::dec << std::endl; 186 | if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) 187 | std::cout << "FreeTypeFont3D::getGlyph : not a vector font" << std::endl; 188 | 189 | AdvanceX = (face->glyph->advance.x) >> 6; 190 | } 191 | 192 | Glyph3D FreeTypeFont::getGlyph3D() 193 | { 194 | FT_Outline outline = face->glyph->outline; 195 | FT_Outline_Funcs funcs; 196 | funcs.conic_to = (FT_Outline_ConicToFunc)&FreeType::conicTo; 197 | funcs.line_to = (FT_Outline_LineToFunc )&FreeType::lineTo; 198 | funcs.cubic_to = (FT_Outline_CubicToFunc)&FreeType::cubicTo; 199 | funcs.move_to = (FT_Outline_MoveToFunc )&FreeType::moveTo; 200 | funcs.shift = 0; 201 | funcs.delta = 0; 202 | 203 | //register the callback function 204 | FT_Error _error = FT_Outline_Decompose(&outline, &funcs, &char3d); 205 | if (_error) 206 | std::cout << "FreeTypeFont3D::getGlyph : - outline decompose failed ..." << std::endl; 207 | 208 | return char3d.get(); 209 | } -------------------------------------------------------------------------------- /Text3D/include/Glyph3D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TriangleIndexFunctor.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | using ElementArray = std::vector; 12 | using Vec3Array = std::vector; 13 | 14 | class Glyph3D 15 | { 16 | public: 17 | Glyph3D() = default; 18 | Glyph3D(const Vec3Array& va, const std::vector& el) : 19 | _vertices(va), _elements(el) {} 20 | 21 | //the positions and contours's indices used to do triangulation 22 | Vec3Array* getVertexPos() { return &_vertices; } 23 | std::vector* getElementsArray() { return &_elements; } 24 | 25 | unsigned int getElementsNum() { return _elements.size(); } 26 | //remove from index b, remove numToRemove's contours 27 | bool removeElements(unsigned int b, unsigned int numToRemove); 28 | 29 | void addNormalArray(const Vec3Array &normals) 30 | { 31 | _normals.push_back(normals); 32 | } 33 | 34 | //add indices 35 | void addPrimitiveSet(ElementArray* element) { _elements.push_back(*element); } 36 | 37 | //add primitive mode 38 | void addMode(GLenum mode) { _modeList.push_back(mode); } 39 | void clearModeList() { _modeList.clear(); } 40 | 41 | //generate the GL_TRIANGLES indices 42 | void accept(PrimitiveIndexFunctor& functor) const; 43 | 44 | //the final data that transmited to opengl 45 | //three face's date(front face, wall face and back face) merge to one mesh 46 | Vec3Array getNormalArray() const; 47 | ElementArray getIndices() const; 48 | 49 | //output final data to txt file 50 | void output(); 51 | 52 | public: 53 | Vec3Array _vertices; //positions 54 | std::vector _normals; 55 | std::vector _elements; 56 | 57 | //because the gluTess not only generate GL_TRIANGLES mode but also GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP; 58 | //but we will unify them to GL_TRIANGLES, so we use this vector to store the original data that from gluTess 59 | //and process them by our PrimitiveIndexFuntor 60 | std::vector _modeList; 61 | }; 62 | 63 | 64 | struct CollectTriangleIndicesFunctor 65 | { 66 | CollectTriangleIndicesFunctor() = default; 67 | 68 | using Indices = std::vector; 69 | Indices _indices; 70 | 71 | void operator() (unsigned int p1, unsigned int p2, unsigned int p3) 72 | { 73 | if (p1 == p2 || p2 == p3 || p1 == p3) 74 | { 75 | return; 76 | } 77 | 78 | _indices.push_back(p1); 79 | _indices.push_back(p3); 80 | _indices.push_back(p2); 81 | } 82 | }; -------------------------------------------------------------------------------- /Text3D/include/Tessellator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Glyph3D.h" 11 | 12 | #define CALLBACK __stdcall 13 | typedef void (CALLBACK * GLU_TESS_CALLBACK)(); 14 | 15 | class Tessellator 16 | { 17 | public: 18 | Tessellator(); 19 | ~Tessellator(); 20 | 21 | void retessellatePolygons(Glyph3D &geom); 22 | 23 | //store the data which from glu triangulation 24 | struct Prim 25 | { 26 | Prim(GLenum mode) :_mode(mode) {} 27 | 28 | using VecList = std::vector; 29 | 30 | GLenum _mode; 31 | VecList _vertices; 32 | }; 33 | 34 | void beginTessellation(); 35 | 36 | void beginContour(); 37 | 38 | void addVertex(glm::vec3* vertex); 39 | 40 | void endContour(); 41 | 42 | void endTessellation(); 43 | 44 | using PrimList = std::vector; 45 | 46 | PrimList& getPrimList() { return _primList; } 47 | 48 | void reset(); 49 | 50 | private: 51 | void collectTessellation(Glyph3D &geom, unsigned int originalIndex); 52 | 53 | using VertexPtrToIndexMap = std::map; 54 | void addContour(ElementArray* primitive, Vec3Array* vertices); 55 | void handleNewVertices(Glyph3D& geom, VertexPtrToIndexMap &vertexPtrToIndexMap); 56 | 57 | void begin(GLenum mode); 58 | void vertex(glm::vec3* vertex); 59 | void combine(glm::vec3* vertex, void* vertex_data[4], GLfloat weight[4]); 60 | void end(); 61 | void error(GLenum errorCode); 62 | 63 | static void CALLBACK beginCallback(GLenum which, void* userData); 64 | static void CALLBACK vertexCallback(GLvoid *data, void* userData); 65 | static void CALLBACK combineCallback(GLdouble coords[3], void* vertex_data[4], 66 | GLfloat weight[4], void** outData, 67 | void* useData); 68 | static void CALLBACK endCallback(void* userData); 69 | static void CALLBACK errorCallback(GLenum errorCode, void* userData); 70 | 71 | struct Vec3d 72 | { 73 | double _v[3]; 74 | }; 75 | 76 | struct NewVertex 77 | { 78 | 79 | NewVertex() : 80 | _vpos(0), 81 | _f1(0), 82 | _v1(0), 83 | _f2(0), 84 | _v2(0), 85 | _f3(0), 86 | _v3(0), 87 | _f4(0), 88 | _v4(0) {} 89 | 90 | NewVertex(const NewVertex& nv) : 91 | _vpos(nv._vpos), 92 | _f1(nv._f1), 93 | _v1(nv._v1), 94 | _f2(nv._f2), 95 | _v2(nv._v2), 96 | _f3(nv._f3), 97 | _v3(nv._v3), 98 | _f4(nv._f4), 99 | _v4(nv._v4) {} 100 | 101 | NewVertex(glm::vec3* vx, 102 | float f1, glm::vec3* v1, 103 | float f2, glm::vec3* v2, 104 | float f3, glm::vec3* v3, 105 | float f4, glm::vec3* v4) : 106 | _vpos(vx), 107 | _f1(f1), 108 | _v1(v1), 109 | _f2(f2), 110 | _v2(v2), 111 | _f3(f3), 112 | _v3(v3), 113 | _f4(f4), 114 | _v4(v4) {} 115 | 116 | glm::vec3 *_vpos; 117 | 118 | float _f1; 119 | glm::vec3* _v1; 120 | 121 | float _f2; 122 | glm::vec3* _v2; 123 | 124 | float _f3; 125 | glm::vec3* _v3; 126 | 127 | float _f4; 128 | glm::vec3* _v4; 129 | 130 | }; 131 | 132 | using NewVertexList = std::vector; 133 | using Vec3dList = std::vector; 134 | 135 | GLUtesselator* _tobj; 136 | 137 | PrimList _primList; 138 | Vec3dList _coordData; 139 | NewVertexList _newVertexList; 140 | GLenum _errorCode; 141 | 142 | unsigned int _numberVerts; 143 | 144 | std::vector _Contours; 145 | 146 | unsigned int _index; 147 | 148 | unsigned int _extraPrimitives; 149 | }; 150 | 151 | void computeGlyphGeometry(Glyph3D &glyph, float width); -------------------------------------------------------------------------------- /Text3D/include/TriangleIndexFunctor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class PrimitiveIndexFunctor 8 | { 9 | public: 10 | 11 | virtual ~PrimitiveIndexFunctor() {} 12 | 13 | virtual void setVertexArray(unsigned int count, const glm::vec2* vertices) = 0; 14 | virtual void setVertexArray(unsigned int count, const glm::vec3* vertices) = 0; 15 | virtual void setVertexArray(unsigned int count, const glm::vec4* vertices) = 0; 16 | 17 | virtual void drawArrays(GLenum mode, GLint first, GLsizei count) = 0; 18 | virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) = 0; 19 | virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices) = 0; 20 | virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices) = 0; 21 | 22 | virtual void begin(GLenum mode) = 0; 23 | virtual void vertex(unsigned int pos) = 0; 24 | virtual void end() = 0; 25 | 26 | void useVertexCacheAsVertexArray() 27 | { 28 | setVertexArray(_vertexCache.size(), &_vertexCache.front()); 29 | } 30 | 31 | std::vector _vertexCache; 32 | bool _treatVertexDataAsTemporary; 33 | }; 34 | 35 | template 36 | class TriangleIndexFunctor : public PrimitiveIndexFunctor, public T 37 | { 38 | public: 39 | 40 | 41 | virtual void setVertexArray(unsigned int, const glm::vec2*) 42 | { 43 | } 44 | 45 | virtual void setVertexArray(unsigned int, const glm::vec3*) 46 | { 47 | } 48 | 49 | virtual void setVertexArray(unsigned int, const glm::vec4*) 50 | { 51 | } 52 | 53 | virtual void begin(GLenum mode) 54 | { 55 | _modeCache = mode; 56 | _indexCache.clear(); 57 | } 58 | 59 | virtual void vertex(unsigned int vert) 60 | { 61 | _indexCache.push_back(vert); 62 | } 63 | 64 | virtual void end() 65 | { 66 | if (!_indexCache.empty()) 67 | { 68 | drawElements(_modeCache, _indexCache.size(), &_indexCache.front()); 69 | } 70 | } 71 | 72 | virtual void drawArrays(GLenum mode, GLint first, GLsizei count) 73 | { 74 | switch (mode) 75 | { 76 | case(GL_TRIANGLES) : 77 | { 78 | unsigned int pos = first; 79 | for (GLsizei i = 2; ioperator()(pos, pos + 1, pos + 2); 82 | } 83 | break; 84 | } 85 | case(GL_TRIANGLE_STRIP) : 86 | { 87 | unsigned int pos = first; 88 | for (GLsizei i = 2; ioperator()(pos, pos + 2, pos + 1); 91 | else this->operator()(pos, pos + 1, pos + 2); 92 | } 93 | break; 94 | } 95 | case(GL_QUADS) : 96 | { 97 | unsigned int pos = first; 98 | for (GLsizei i = 3; ioperator()(pos, pos + 1, pos + 2); 101 | this->operator()(pos, pos + 2, pos + 3); 102 | } 103 | break; 104 | } 105 | case(GL_QUAD_STRIP) : 106 | { 107 | unsigned int pos = first; 108 | for (GLsizei i = 3; ioperator()(pos, pos + 1, pos + 2); 111 | this->operator()(pos + 1, pos + 3, pos + 2); 112 | } 113 | break; 114 | } 115 | case(GL_POLYGON) : // treat polygons as GL_TRIANGLE_FAN 116 | case(GL_TRIANGLE_FAN) : 117 | { 118 | unsigned int pos = first + 1; 119 | for (GLsizei i = 2; ioperator()(first, pos, pos + 1); 122 | } 123 | break; 124 | } 125 | case(GL_POINTS) : 126 | case(GL_LINES) : 127 | case(GL_LINE_STRIP) : 128 | case(GL_LINE_LOOP) : 129 | default: 130 | // can't be converted into to triangles. 131 | break; 132 | } 133 | } 134 | 135 | virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) 136 | { 137 | if (indices == 0 || count == 0) return; 138 | 139 | typedef GLubyte Index; 140 | typedef const Index* IndexPointer; 141 | 142 | switch (mode) 143 | { 144 | case(GL_TRIANGLES) : 145 | { 146 | IndexPointer ilast = &indices[count]; 147 | for (IndexPointer iptr = indices; iptroperator()(*iptr, *(iptr + 1), *(iptr + 2)); 149 | break; 150 | } 151 | case(GL_TRIANGLE_STRIP) : 152 | { 153 | IndexPointer iptr = indices; 154 | for (GLsizei i = 2; ioperator()(*(iptr), *(iptr + 2), *(iptr + 1)); 157 | else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); 158 | } 159 | break; 160 | } 161 | case(GL_QUADS) : 162 | { 163 | IndexPointer iptr = indices; 164 | for (GLsizei i = 3; ioperator()(*(iptr), *(iptr + 1), *(iptr + 2)); 167 | this->operator()(*(iptr), *(iptr + 2), *(iptr + 3)); 168 | } 169 | break; 170 | } 171 | case(GL_QUAD_STRIP) : 172 | { 173 | IndexPointer iptr = indices; 174 | for (GLsizei i = 3; ioperator()(*(iptr), *(iptr + 1), *(iptr + 2)); 177 | this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2)); 178 | } 179 | break; 180 | } 181 | case(GL_POLYGON) : // treat polygons as GL_TRIANGLE_FAN 182 | case(GL_TRIANGLE_FAN) : 183 | { 184 | IndexPointer iptr = indices; 185 | Index first = *iptr; 186 | ++iptr; 187 | for (GLsizei i = 2; ioperator()(first, *(iptr), *(iptr + 1)); 190 | } 191 | break; 192 | } 193 | case(GL_POINTS) : 194 | case(GL_LINES) : 195 | case(GL_LINE_STRIP) : 196 | case(GL_LINE_LOOP) : 197 | default: 198 | // can't be converted into to triangles. 199 | break; 200 | } 201 | } 202 | 203 | virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices) 204 | { 205 | if (indices == 0 || count == 0) return; 206 | 207 | typedef GLushort Index; 208 | typedef const Index* IndexPointer; 209 | 210 | switch (mode) 211 | { 212 | case(GL_TRIANGLES) : 213 | { 214 | IndexPointer ilast = &indices[count]; 215 | for (IndexPointer iptr = indices; iptroperator()(*iptr, *(iptr + 1), *(iptr + 2)); 217 | break; 218 | } 219 | case(GL_TRIANGLE_STRIP) : 220 | { 221 | IndexPointer iptr = indices; 222 | for (GLsizei i = 2; ioperator()(*(iptr), *(iptr + 2), *(iptr + 1)); 225 | else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); 226 | } 227 | break; 228 | } 229 | case(GL_QUADS) : 230 | { 231 | IndexPointer iptr = indices; 232 | for (GLsizei i = 3; ioperator()(*(iptr), *(iptr + 1), *(iptr + 2)); 235 | this->operator()(*(iptr), *(iptr + 2), *(iptr + 3)); 236 | } 237 | break; 238 | } 239 | case(GL_QUAD_STRIP) : 240 | { 241 | IndexPointer iptr = indices; 242 | for (GLsizei i = 3; ioperator()(*(iptr), *(iptr + 1), *(iptr + 2)); 245 | this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2)); 246 | } 247 | break; 248 | } 249 | case(GL_POLYGON) : // treat polygons as GL_TRIANGLE_FAN 250 | case(GL_TRIANGLE_FAN) : 251 | { 252 | IndexPointer iptr = indices; 253 | Index first = *iptr; 254 | ++iptr; 255 | for (GLsizei i = 2; ioperator()(first, *(iptr), *(iptr + 1)); 258 | } 259 | break; 260 | } 261 | case(GL_POINTS) : 262 | case(GL_LINES) : 263 | case(GL_LINE_STRIP) : 264 | case(GL_LINE_LOOP) : 265 | default: 266 | // can't be converted into to triangles. 267 | break; 268 | } 269 | } 270 | 271 | virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices) 272 | { 273 | if (indices == 0 || count == 0) return; 274 | 275 | typedef GLuint Index; 276 | typedef const Index* IndexPointer; 277 | 278 | switch (mode) 279 | { 280 | case(GL_TRIANGLES) : 281 | { 282 | IndexPointer ilast = &indices[count]; 283 | for (IndexPointer iptr = indices; iptroperator()(*iptr, *(iptr + 1), *(iptr + 2)); 285 | break; 286 | } 287 | case(GL_TRIANGLE_STRIP) : 288 | { 289 | IndexPointer iptr = indices; 290 | for (GLsizei i = 2; ioperator()(*(iptr), *(iptr + 2), *(iptr + 1)); 293 | else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); 294 | } 295 | break; 296 | } 297 | case(GL_QUADS) : 298 | { 299 | IndexPointer iptr = indices; 300 | for (GLsizei i = 3; ioperator()(*(iptr), *(iptr + 1), *(iptr + 2)); 303 | this->operator()(*(iptr), *(iptr + 2), *(iptr + 3)); 304 | } 305 | break; 306 | } 307 | case(GL_QUAD_STRIP) : 308 | { 309 | IndexPointer iptr = indices; 310 | for (GLsizei i = 3; ioperator()(*(iptr), *(iptr + 1), *(iptr + 2)); 313 | this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2)); 314 | } 315 | break; 316 | } 317 | case(GL_POLYGON) : // treat polygons as GL_TRIANGLE_FAN 318 | case(GL_TRIANGLE_FAN) : 319 | { 320 | IndexPointer iptr = indices; 321 | Index first = *iptr; 322 | ++iptr; 323 | for (GLsizei i = 2; ioperator()(first, *(iptr), *(iptr + 1)); 326 | } 327 | break; 328 | } 329 | case(GL_POINTS) : 330 | case(GL_LINES) : 331 | case(GL_LINE_STRIP) : 332 | case(GL_LINE_LOOP) : 333 | default: 334 | // can't be converted into to triangles. 335 | break; 336 | } 337 | } 338 | 339 | GLenum _modeCache; 340 | std::vector _indexCache; 341 | }; -------------------------------------------------------------------------------- /Text3D/src/Glyph3D.cpp: -------------------------------------------------------------------------------- 1 | #include "..\include\Glyph3D.h" 2 | 3 | bool Glyph3D::removeElements(unsigned int b, unsigned int numToRemove) 4 | { 5 | if (numToRemove == 0) return false; 6 | if (b < _elements.size()) 7 | { 8 | if (b + numToRemove <= _elements.size()) 9 | { 10 | _elements.erase(_elements.begin() + b, _elements.begin() + b + numToRemove); 11 | } 12 | else 13 | { 14 | std::cout << "the range required to remove is out of range! remove to the end of ContourList." << std::endl; 15 | _elements.erase(_elements.begin() + b, _elements.end()); 16 | } 17 | return true; 18 | } 19 | } 20 | 21 | Vec3Array Glyph3D::getNormalArray() const 22 | { 23 | Vec3Array normal; 24 | for (const auto &norm : _normals) 25 | for (const auto &n : norm) 26 | normal.push_back(n); 27 | return normal; 28 | } 29 | 30 | ElementArray Glyph3D::getIndices() const 31 | { 32 | ElementArray indices; 33 | for (const auto &c : _elements) 34 | for (const auto &i : c) 35 | indices.push_back(i); 36 | 37 | return indices; 38 | } 39 | 40 | void Glyph3D::accept(PrimitiveIndexFunctor& functor) const 41 | { 42 | const Vec3Array* vertices = &_vertices; 43 | 44 | if (!vertices || vertices->size() == 0) return; 45 | 46 | for (unsigned int i = 0; i < _elements.size(); ++i) 47 | functor.drawElements(_modeList[i], _elements[i].size(), &(_elements[i].front())); 48 | } 49 | 50 | void Glyph3D::output() 51 | { 52 | std::ofstream fout; 53 | fout.open("verts_data_3d_I.txt"); 54 | if (!fout) 55 | std::cout << "verts_data.txt open failed!" << std::endl; 56 | 57 | fout << "Size of vertices: " << _vertices.size() << std::endl; 58 | fout << "Vertices: " << std::endl; 59 | for (auto vert : _vertices) 60 | fout << vert.x << ", " << vert.y << ", " << vert.z << std::endl; 61 | fout << std::endl; 62 | 63 | fout << "Indices:" << std::endl; 64 | ElementArray indices = getIndices(); 65 | fout << "Size of indices: " << indices.size() << std::endl; 66 | for (int i = 0; i < indices.size(); ++i) 67 | { 68 | if (i % 3 == 0) 69 | fout << std::endl; 70 | fout << indices[i] << ", "; 71 | } 72 | fout << std::endl << std::endl; 73 | 74 | fout << "Normals:" << std::endl; 75 | Vec3Array normals = getNormalArray(); 76 | fout << "Size of normals: " << normals.size() << std::endl; 77 | for (auto norm : normals) 78 | fout << norm.x << ", " << norm.y << ", " << norm.z << std::endl; 79 | fout << std::endl; 80 | 81 | fout.close(); 82 | } -------------------------------------------------------------------------------- /Text3D/src/Tessellator.cpp: -------------------------------------------------------------------------------- 1 | #include "..\include\Tessellator.h" 2 | 3 | Tessellator::Tessellator() : 4 | _numberVerts(0) 5 | { 6 | _tobj = gluNewTess(); 7 | if (_tobj) 8 | { 9 | gluTessCallback(_tobj, GLU_TESS_VERTEX_DATA, (GLU_TESS_CALLBACK)vertexCallback); 10 | gluTessCallback(_tobj, GLU_TESS_BEGIN_DATA, (GLU_TESS_CALLBACK)beginCallback); 11 | gluTessCallback(_tobj, GLU_TESS_END_DATA, (GLU_TESS_CALLBACK)endCallback); 12 | gluTessCallback(_tobj, GLU_TESS_COMBINE_DATA, (GLU_TESS_CALLBACK)combineCallback); 13 | gluTessCallback(_tobj, GLU_TESS_ERROR_DATA, (GLU_TESS_CALLBACK)errorCallback); 14 | } 15 | _errorCode = 0; 16 | _index = 0; 17 | } 18 | 19 | Tessellator::~Tessellator() 20 | { 21 | reset(); 22 | if (_tobj) 23 | { 24 | gluDeleteTess(_tobj); 25 | } 26 | } 27 | 28 | void Tessellator::beginTessellation() 29 | { 30 | reset(); 31 | 32 | if (_tobj) 33 | { 34 | gluTessProperty(_tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE); 35 | gluTessProperty(_tobj, GLU_TESS_BOUNDARY_ONLY, GL_FALSE); 36 | 37 | gluTessBeginPolygon(_tobj, this); 38 | } 39 | } 40 | 41 | void Tessellator::beginContour() 42 | { 43 | if (_tobj) 44 | { 45 | gluTessBeginContour(_tobj); 46 | } 47 | } 48 | 49 | void Tessellator::endContour() 50 | { 51 | if (_tobj) 52 | { 53 | gluTessEndContour(_tobj); 54 | } 55 | } 56 | 57 | void Tessellator::endTessellation() 58 | { 59 | if (_tobj) 60 | { 61 | gluTessEndPolygon(_tobj); 62 | 63 | if (_errorCode != 0) 64 | { 65 | const GLubyte *estring = gluErrorString((GLenum)_errorCode); 66 | std::cout << "Tessellation Error: " << estring << std::endl; 67 | } 68 | } 69 | } 70 | 71 | void Tessellator::reset() 72 | { 73 | for (Vec3dList::iterator i = _coordData.begin(); i != _coordData.end(); ++i) 74 | { 75 | delete (*i); 76 | } 77 | 78 | for (NewVertexList::iterator j = _newVertexList.begin(); j != _newVertexList.end(); ++j) 79 | { 80 | NewVertex& newVertex = (*j); 81 | delete newVertex._vpos; 82 | newVertex._vpos = NULL; 83 | } 84 | 85 | _coordData.clear(); 86 | _newVertexList.clear(); 87 | _primList.clear(); 88 | _errorCode = 0; 89 | } 90 | 91 | void Tessellator::retessellatePolygons(Glyph3D &geom) 92 | { 93 | // turn the contour list into primitives, a little like Tessellator does but more generally 94 | Vec3Array* vertices = geom.getVertexPos(); 95 | 96 | if (!vertices || vertices->empty() || geom._elements.empty()) return; 97 | 98 | _index = 0; // reset the counter for indexed vertices 99 | _extraPrimitives = 0; 100 | if (!_numberVerts) 101 | { 102 | _numberVerts = geom.getVertexPos()->size(); 103 | // save the contours for complex (winding rule) tessellations 104 | _Contours = geom._elements; 105 | } 106 | 107 | // remove the existing primitives. 108 | unsigned int nprimsetoriginal = geom.getElementsNum(); 109 | if (nprimsetoriginal) geom.removeElements(0, nprimsetoriginal); 110 | 111 | // the main difference from osgUtil::Tessellator for Glyph3D sets of multiple contours is that the begin/end tessellation 112 | // occurs around the whole set of contours. 113 | beginTessellation(); 114 | // process all the contours into the Tessellator 115 | int noContours = _Contours.size(); 116 | int currentPrimitive = 0; 117 | for (int primNo = 0; primNo < noContours; ++primNo) 118 | { 119 | ElementArray *primitive = &(_Contours[primNo]); 120 | 121 | addContour(primitive, vertices); 122 | } 123 | endTessellation(); 124 | collectTessellation(geom, 0); 125 | } 126 | 127 | void Tessellator::addContour(ElementArray* primitive, Vec3Array* vertices) 128 | { 129 | unsigned int idx = 0; 130 | 131 | beginContour(); 132 | ElementArray *drawElements = primitive; 133 | for (ElementArray::iterator indexItr = drawElements->begin(); 134 | indexItr != drawElements->end(); 135 | ++indexItr, idx++) 136 | { 137 | addVertex(&((*vertices)[*indexItr])); 138 | } 139 | endContour(); 140 | } 141 | 142 | void Tessellator::addVertex(glm::vec3* vertex) 143 | { 144 | if (_tobj) 145 | { 146 | if (vertex) 147 | { 148 | Vec3d* data = new Vec3d; 149 | _coordData.push_back(data); 150 | (*data)._v[0] = (*vertex)[0]; 151 | (*data)._v[1] = (*vertex)[1]; 152 | (*data)._v[2] = (*vertex)[2]; 153 | gluTessVertex(_tobj, (data->_v), vertex); 154 | } 155 | } 156 | } 157 | 158 | void Tessellator::handleNewVertices(Glyph3D& geom, VertexPtrToIndexMap &vertexPtrToIndexMap) 159 | { 160 | if (!_newVertexList.empty()) 161 | { 162 | Vec3Array* vertices = geom.getVertexPos(); 163 | 164 | // now add any new vertices that are required. 165 | for (NewVertexList::iterator itr = _newVertexList.begin(); 166 | itr != _newVertexList.end(); 167 | ++itr) 168 | { 169 | NewVertex& newVertex = (*itr); 170 | glm::vec3* vertex = newVertex._vpos; 171 | 172 | // assign vertex. 173 | vertexPtrToIndexMap[vertex] = vertices->size(); 174 | vertices->push_back(*vertex); 175 | } 176 | } 177 | } 178 | 179 | void Tessellator::begin(GLenum mode) 180 | { 181 | _primList.push_back(new Prim(mode)); 182 | } 183 | 184 | void Tessellator::vertex(glm::vec3* vertex) 185 | { 186 | if (!_primList.empty()) 187 | { 188 | Prim* prim = _primList.back(); 189 | prim->_vertices.push_back(vertex); 190 | } 191 | } 192 | 193 | void Tessellator::combine(glm::vec3* vertex, void* vertex_data[4], GLfloat weight[4]) 194 | { 195 | _newVertexList.push_back(NewVertex(vertex, 196 | weight[0], (glm::vec3*)vertex_data[0], 197 | weight[1], (glm::vec3*)vertex_data[1], 198 | weight[2], (glm::vec3*)vertex_data[2], 199 | weight[3], (glm::vec3*)vertex_data[3])); 200 | } 201 | 202 | void Tessellator::end() 203 | { 204 | // no need to do anything right now... 205 | } 206 | 207 | void Tessellator::error(GLenum errorCode) 208 | { 209 | _errorCode = errorCode; 210 | } 211 | 212 | void Tessellator::beginCallback(GLenum which, void* userData) 213 | { 214 | ((Tessellator*)userData)->begin(which); 215 | } 216 | 217 | void Tessellator::endCallback(void* userData) 218 | { 219 | ((Tessellator*)userData)->end(); 220 | } 221 | 222 | void Tessellator::vertexCallback(GLvoid *data, void* userData) 223 | { 224 | ((Tessellator*)userData)->vertex((glm::vec3*)data); 225 | } 226 | 227 | void Tessellator::combineCallback(GLdouble coords[3], void* vertex_data[4], 228 | GLfloat weight[4], void** outData, 229 | void* userData) 230 | { 231 | glm::vec3* newData = new glm::vec3(coords[0], coords[1], coords[2]); 232 | *outData = newData; 233 | ((Tessellator*)userData)->combine(newData, vertex_data, weight); 234 | } 235 | 236 | void Tessellator::errorCallback(GLenum errorCode, void* userData) 237 | { 238 | ((Tessellator*)userData)->error(errorCode); 239 | } 240 | 241 | void Tessellator::collectTessellation(Glyph3D &geom, unsigned int originalIndex) 242 | { 243 | Vec3Array* vertices = geom.getVertexPos(); 244 | VertexPtrToIndexMap vertexPtrToIndexMap; 245 | 246 | // populate the VertexPtrToIndexMap. 247 | for (unsigned int vi = 0; vi < vertices->size(); ++vi) 248 | { 249 | vertexPtrToIndexMap[&((*vertices)[vi])] = vi; 250 | } 251 | 252 | handleNewVertices(geom, vertexPtrToIndexMap); 253 | 254 | geom.clearModeList(); 255 | 256 | for (PrimList::iterator primItr = _primList.begin(); 257 | primItr != _primList.end(); 258 | ++primItr, ++_index) 259 | { 260 | Prim* prim = *primItr; 261 | 262 | ElementArray elements; 263 | for (Prim::VecList::iterator vitr = prim->_vertices.begin(); 264 | vitr != prim->_vertices.end(); 265 | ++vitr) 266 | { 267 | elements.push_back(vertexPtrToIndexMap[*vitr]); 268 | } 269 | geom.addMode(prim->_mode); 270 | // add to the drawn primitive list. 271 | geom.addPrimitiveSet(&elements); 272 | } 273 | } 274 | 275 | void computeGlyphGeometry(Glyph3D &glyph, float width) 276 | { 277 | Vec3Array *vertices = glyph.getVertexPos(); 278 | Vec3Array origin_vertices = *vertices; 279 | Vec3Array normals; 280 | std::vector origin_indices = glyph._elements; 281 | 282 | Tessellator ts; 283 | ts.retessellatePolygons(glyph); 284 | 285 | //generate triangle indices 286 | TriangleIndexFunctor ctif; 287 | glyph.accept(ctif); 288 | CollectTriangleIndicesFunctor::Indices& indices = ctif._indices; 289 | 290 | //rebuild the primitives of text3D, generate front face, wall face and back face 291 | glyph._elements.clear(); 292 | glyph.clearModeList(); 293 | 294 | if (indices.empty()) std::cout << "The new indices create failed!" << std::endl; 295 | 296 | //front face 297 | ElementArray front_face; 298 | for (unsigned int i = 0; i < indices.size(); ++i) 299 | { 300 | front_face.push_back(indices[i]); 301 | normals.push_back(glm::vec3(0.0f, 0.0f, 1.0f)); 302 | } 303 | 304 | glyph.addPrimitiveSet(&front_face); 305 | glyph.addNormalArray(normals); 306 | glyph.addMode(GL_TRIANGLES); 307 | 308 | //back face 309 | const unsigned int NULL_VALUE = UINT_MAX; 310 | ElementArray back_indices; 311 | back_indices.resize(vertices->size(), NULL_VALUE); 312 | glm::vec3 forward(0, 0, -width); 313 | normals.clear(); 314 | 315 | ElementArray back_face; 316 | for (unsigned int i = 0; i < indices.size() - 2;) 317 | { 318 | unsigned int p1 = indices[i++]; 319 | unsigned int p2 = indices[i++]; 320 | unsigned int p3 = indices[i++]; 321 | if (back_indices[p1] == NULL_VALUE) 322 | { 323 | back_indices[p1] = vertices->size(); 324 | vertices->push_back((*vertices)[p1] + forward); 325 | } 326 | 327 | if (back_indices[p2] == NULL_VALUE) 328 | { 329 | back_indices[p2] = vertices->size(); 330 | vertices->push_back((*vertices)[p2] + forward); 331 | } 332 | 333 | if (back_indices[p3] == NULL_VALUE) 334 | { 335 | 336 | back_indices[p3] = vertices->size(); 337 | vertices->push_back((*vertices)[p3] + forward); 338 | } 339 | 340 | back_face.push_back(back_indices[p1]); 341 | back_face.push_back(back_indices[p3]); 342 | back_face.push_back(back_indices[p2]); 343 | 344 | normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); 345 | normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); 346 | normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); 347 | } 348 | 349 | glyph.addPrimitiveSet(&back_face); 350 | glyph.addNormalArray(normals); 351 | glyph.addMode(GL_TRIANGLES); 352 | 353 | //wall face 354 | unsigned int orig_size = origin_vertices.size(); 355 | ElementArray frontedge_indices, backedge_indices; 356 | frontedge_indices.resize(orig_size, NULL_VALUE); 357 | backedge_indices.resize(orig_size, NULL_VALUE); 358 | 359 | for (ElementArray elements : origin_indices) 360 | { 361 | normals.clear(); 362 | 363 | ElementArray edging; 364 | if (!elements.empty()) 365 | { 366 | for (unsigned int i = 0; i < elements.size(); ++i) 367 | { 368 | unsigned int ei = elements[i]; 369 | if (frontedge_indices[ei] == NULL_VALUE) 370 | { 371 | frontedge_indices[ei] = vertices->size(); 372 | vertices->push_back(origin_vertices[ei]); 373 | } 374 | if (backedge_indices[ei] == NULL_VALUE) 375 | { 376 | backedge_indices[ei] = vertices->size(); 377 | vertices->push_back(origin_vertices[ei] + forward); 378 | } 379 | 380 | edging.push_back(backedge_indices[ei]); 381 | edging.push_back(frontedge_indices[ei]); 382 | } 383 | } 384 | 385 | std::vector wall_face; 386 | GLuint *iptr = &edging.front(); 387 | for (unsigned int i = 3; i < edging.size(); i += 2, iptr += 2) 388 | { 389 | glm::vec3 edge1((*vertices)[*(iptr + 1)] - (*vertices)[*iptr]); 390 | glm::vec3 edge2((*vertices)[*(iptr + 2)] - (*vertices)[*iptr]); 391 | glm::vec3 temp_normal = glm::cross(edge1, edge2); 392 | wall_face.push_back(*(iptr)); 393 | wall_face.push_back(*(iptr + 1)); 394 | wall_face.push_back(*(iptr + 2)); 395 | 396 | temp_normal = glm::normalize(temp_normal); 397 | 398 | normals.push_back(temp_normal); 399 | normals.push_back(temp_normal); 400 | normals.push_back(temp_normal); 401 | 402 | wall_face.push_back(*(iptr + 1)); 403 | wall_face.push_back(*(iptr + 3)); 404 | wall_face.push_back(*(iptr + 2)); 405 | 406 | normals.push_back(temp_normal); 407 | normals.push_back(temp_normal); 408 | normals.push_back(temp_normal); 409 | } 410 | glyph.addPrimitiveSet(&wall_face); 411 | glyph.addNormalArray(normals); 412 | glyph.addMode(GL_TRIANGLES); 413 | } 414 | } -------------------------------------------------------------------------------- /fonts/STXINWEI.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/fonts/STXINWEI.TTF -------------------------------------------------------------------------------- /fonts/arial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/fonts/arial.ttf -------------------------------------------------------------------------------- /fonts/simhei.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/fonts/simhei.ttf --------------------------------------------------------------------------------