Can display simple objects with single texture.

This commit is contained in:
Themaister 2013-05-10 22:33:22 +02:00
parent 2082adee2c
commit 787596da57
12 changed files with 367 additions and 47 deletions

BIN
blockDiamond.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

8
gl.hpp
View File

@ -4,6 +4,7 @@
#define GL_GLEXT_PROTOTYPES
#if defined(GLES)
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GL/gl.h>
#include <GL/glext.h>
@ -17,11 +18,18 @@
namespace GL
{
// If true, GL context has been reset and all
// objects are invalid. Do not free their resources
// in destructors.
extern bool dead_state;
typedef std::map<std::string, retro_proc_address_t> SymMap;
SymMap& symbol_map();
void init_symbol_map();
// Discover and cache GL symbols on-the-fly.
// Avoids things like GLEW, and avoids typing out a billion symbol declarations.
void set_function_cb(retro_hw_get_proc_address_t);
retro_proc_address_t get_symbol(const std::string& str);

View File

@ -2,6 +2,8 @@
namespace GL
{
bool dead_state;
static SymMap map;
SymMap& symbol_map()
{

View File

@ -1,6 +1,7 @@
#include "libretro.h"
#include "gl.hpp"
#include "mesh.hpp"
#include "object.hpp"
#include <cstring>
#include <string>
#include <iostream>
@ -11,9 +12,7 @@ using namespace glm;
static struct retro_hw_render_callback hw_render;
static std::string mesh_path;
static std::shared_ptr<Mesh> mesh;
static std::shared_ptr<Texture> texture;
static std::shared_ptr<Shader> shader;
static std::vector<std::shared_ptr<Mesh>> meshes;
void retro_init(void)
{}
@ -95,31 +94,25 @@ void retro_run(void)
SYM(glClearColor)(0.2f, 0.2f, 0.2f, 1.0f);
SYM(glClear)(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
SYM(glEnable)(GL_DEPTH_TEST);
SYM(glFrontFace)(GL_CW); // When we flip vertically, orientation changes.
SYM(glEnable)(GL_CULL_FACE);
SYM(glEnable)(GL_BLEND);
SYM(glViewport)(0, 0, 640, 480);
mesh->render();
for (auto& mesh : meshes)
mesh->render();
SYM(glDisable)(GL_BLEND);
//auto error = SYM(glGetError)();
//std::cerr << "GL error: " << error << std::endl;
video_cb(RETRO_HW_FRAME_BUFFER_VALID, 640, 480, 0);
}
static void init_mesh(const std::string&)
static void init_mesh(const std::string& path)
{
std::vector<Vertex> triangle = {
{
{ -0.5, -0.5, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0 }
},
{
{ +0.5, -0.5, 0.0 }, { 0.0, 0.0, 1.0 }, { 1.0, 0.0 }
},
{
{ +0.0, +0.5, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.5, 1.0 }
},
};
mesh = std::make_shared<Mesh>();
mesh->set_vertices(std::move(triangle));
static const std::string vertex_shader =
"uniform mat4 uModel;\n"
"uniform mat4 uMVP;\n"
@ -128,30 +121,51 @@ static void init_mesh(const std::string&)
"attribute vec2 aTex;\n"
"varying vec4 vNormal;\n"
"varying vec2 vTex;\n"
"varying vec4 vPos;\n"
"void main() {\n"
" gl_Position = uMVP * aVertex;\n"
" vTex = aTex;\n"
" vTex = vec2(aTex.x, 1.0 - aTex.y);\n"
" vPos = uModel * aVertex;\n"
" vNormal = uModel * vec4(aNormal, 0.0);\n"
"}";
static const std::string fragment_shader =
"varying vec2 vTex;\n"
"varying vec4 vNormal;\n"
"varying vec4 vPos;\n"
"uniform sampler2D sTexture;\n"
"void main() {\n"
" gl_FragColor = vec4(0.5);\n"
" vec4 color = texture2D(sTexture, vTex);\n"
" vec3 normal = normalize(vNormal.xyz);\n"
" vec3 dist = vPos.xyz - vec3(20.0, 40.0, -30.0);\n"
" float directivity = dot(normalize(dist), -normal);\n"
" float diffuse = clamp(directivity, 0.0, 1.0) + 0.4;\n"
" gl_FragColor = vec4(diffuse * color.rgb, color.a);\n"
"}";
shader = std::make_shared<Shader>(vertex_shader, fragment_shader);
mesh->set_shader(shader);
auto shader = std::make_shared<Shader>(vertex_shader, fragment_shader);
mesh->set_projection(scale(mat4(1.0), vec3(1, -1, 1)));
meshes = OBJ::load_from_file(path);
mat4 translation = translate(mat4(1.0), vec3(0, 0, -40));
mat4 scaler = scale(translation, vec3(15, 15, 15));
mat4 rotater = rotate(scaler, 0.0f, vec3(0, 1, 0));
mat4 projection = scale(perspective(45.0f, 640.0f / 480.0f, 1.0f, 100.0f),
vec3(1, -1, 1));
for (auto& mesh : meshes)
{
mesh->set_model(rotater);
mesh->set_projection(projection);
mesh->set_shader(shader);
}
}
static void context_reset(void)
{
mesh.reset();
texture.reset();
shader.reset();
dead_state = true;
meshes.clear();
dead_state = false;
GL::set_function_cb(hw_render.get_proc_address);
GL::init_symbol_map();
@ -184,7 +198,9 @@ bool retro_load_game(const struct retro_game_info *info)
}
void retro_unload_game(void)
{}
{
dead_state = true;
}
unsigned retro_get_region(void)
{

View File

@ -1,5 +1,7 @@
#include "mesh.hpp"
using namespace glm;
namespace GL
{
Mesh::Mesh() :
@ -13,17 +15,26 @@ namespace GL
}
Mesh::~Mesh()
{}
void Mesh::set_vertices(std::vector<Vertex> vertex, GLenum type)
{
set_vertices(std::make_shared<std::vector<Vertex>>(std::move(vertex)), type);
if (dead_state)
return;
SYM(glDeleteBuffers)(1, &vbo);
}
void Mesh::set_vertices(const std::shared_ptr<std::vector<Vertex>>& vertex, GLenum type)
void Mesh::set_vertices(std::vector<Vertex> vertex)
{
set_vertices(std::make_shared<std::vector<Vertex>>(std::move(vertex)));
}
void Mesh::set_vertex_type(GLenum type)
{
vertex_type = type;
}
void Mesh::set_vertices(const std::shared_ptr<std::vector<Vertex>>& vertex)
{
this->vertex = vertex;
vertex_type = type;
SYM(glBindBuffer)(GL_ARRAY_BUFFER, vbo);
SYM(glBufferData)(GL_ARRAY_BUFFER, vertex->size() * sizeof(Vertex),
@ -69,10 +80,12 @@ namespace GL
shader->use();
SYM(glUniform1i)(shader->uniform("sTexture"), 0);
SYM(glUniformMatrix4fv)(shader->uniform("uModel"),
1, GL_FALSE, &model[0][0]);
1, GL_FALSE, value_ptr(model));
SYM(glUniformMatrix4fv)(shader->uniform("uMVP"),
1, GL_FALSE, &mvp[0][0]);
1, GL_FALSE, value_ptr(mvp));
GLint aVertex = shader->attrib("aVertex");
GLint aNormal = shader->attrib("aNormal");

View File

@ -9,14 +9,15 @@
#include <memory>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
namespace GL
{
struct Vertex
{
GLfloat vert[3];
GLfloat normal[3];
GLfloat tex[2];
glm::vec3 vert;
glm::vec3 normal;
glm::vec2 tex;
};
class Mesh
@ -25,9 +26,9 @@ namespace GL
Mesh();
~Mesh();
void set_vertices(std::vector<Vertex> vertex, GLenum vertex_type = GL_TRIANGLES);
void set_vertices(const std::shared_ptr<std::vector<Vertex>>& vertex,
GLenum vertex_type = GL_TRIANGLES);
void set_vertices(std::vector<Vertex> vertex);
void set_vertices(const std::shared_ptr<std::vector<Vertex>>& vertex);
void set_vertex_type(GLenum type);
void set_texture(const std::shared_ptr<Texture>& tex);
void set_shader(const std::shared_ptr<Shader>& shader);

148
object.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "object.hpp"
#include "util.hpp"
#include <iostream>
#include <fstream>
#include <string>
using namespace GL;
using namespace glm;
namespace OBJ
{
template<typename T>
inline T parse_line(const std::string& data);
template<>
inline vec2 parse_line(const std::string& data)
{
float x = 0, y = 0;
auto split = String::split(data, " ");
if (split.size() >= 2)
{
x = std::stof(split[0]);
y = std::stof(split[1]);
}
return vec2(x, y);
}
template<>
inline vec3 parse_line(const std::string& data)
{
float x = 0, y = 0, z = 0;
auto split = String::split(data, " ");
if (split.size() >= 3)
{
x = std::stof(split[0]);
y = std::stof(split[1]);
z = std::stof(split[2]);
}
return vec3(x, y, z);
}
static void parse_vertex(const std::string& data,
std::vector<Vertex>& vertices_buffer,
const std::vector<vec3>& vertex,
const std::vector<vec3>& normal,
const std::vector<vec2>& tex)
{
auto vertices = String::split(data, " ");
if (vertices.size() > 3)
vertices.resize(3);
std::vector<std::vector<std::string>> verts;
for (auto& vert : vertices)
{
Vertex out_vertex{};
auto coords = String::split(vert, "/", true);
if (coords.size() == 1) // Vertex only
{
std::size_t coord = std::stoi(coords[0]);
if (coord && vertex.size() >= coord)
out_vertex.vert = vertex[coord - 1];
}
else if (coords.size() == 2) // Vertex/Texcoord
{
std::size_t coord_vert = std::stoi(coords[0]);
std::size_t coord_tex = std::stoi(coords[1]);
if (coord_vert && vertex.size() >= coord_vert)
out_vertex.vert = vertex[coord_vert - 1];
if (coord_tex && tex.size() >= coord_tex)
out_vertex.tex = tex[coord_tex - 1];
}
else if (coords.size() == 3 && coords[1].size()) // Vertex/Texcoord/Normal
{
std::size_t coord_vert = std::stoi(coords[0]);
std::size_t coord_tex = std::stoi(coords[1]);
std::size_t coord_normal = std::stoi(coords[2]);
if (coord_vert && vertex.size() >= coord_vert)
out_vertex.vert = vertex[coord_vert - 1];
if (coord_tex && tex.size() >= coord_tex)
out_vertex.tex = tex[coord_tex - 1];
if (coord_normal && normal.size() >= coord_normal)
out_vertex.normal = normal[coord_normal - 1];
}
else if (coords.size() == 3 && !coords[1].size()) // Vertex//Normal
{
std::size_t coord_vert = std::stoi(coords[0]);
std::size_t coord_normal = std::stoi(coords[2]);
if (coord_vert && vertex.size() >= coord_vert)
out_vertex.vert = vertex[coord_vert - 1];
if (coord_normal && normal.size() >= coord_normal)
out_vertex.normal = normal[coord_normal - 1];
}
vertices_buffer.push_back(out_vertex);
}
}
std::vector<std::shared_ptr<Mesh>> load_from_file(const std::string& path)
{
std::ifstream file(path, std::ios::in);
std::vector<std::shared_ptr<Mesh>> meshes;
if (!file.is_open())
return meshes;
std::vector<vec3> vertex;
std::vector<vec3> normal;
std::vector<vec2> tex;
std::vector<Vertex> vertices;
std::shared_ptr<Texture> texture;
for (std::string line; getline(file, line); )
{
line = line.substr(0, line.find_first_of('\r'));
auto split_point = line.find_first_of(' ');
auto type = line.substr(0, split_point);
auto data = split_point != std::string::npos ? line.substr(split_point + 1) : std::string();
if (type == "v")
vertex.push_back(parse_line<vec3>(data));
else if (type == "vn")
normal.push_back(parse_line<vec3>(data));
else if (type == "vt")
tex.push_back(parse_line<vec2>(data));
else if (type == "f")
parse_vertex(data, vertices, vertex, normal, tex);
else if (type == "texture") // Not standard OBJ, but do it like this for simplicity ...
{
auto texture_path = Path::join(Path::basedir(path), data + ".png");
texture = std::make_shared<Texture>(texture_path);
}
}
std::cerr << "Got " << vertices.size() << " vertices!" << std::endl;
auto mesh = std::make_shared<Mesh>();
mesh->set_vertices(std::move(vertices));
mesh->set_texture(texture);
meshes.push_back(mesh);
return meshes;
}
}

15
object.hpp Normal file
View File

@ -0,0 +1,15 @@
#ifndef OBJECT_HPP__
#define OBJECT_HPP__
#include "mesh.hpp"
#include <string>
#include <vector>
#include <memory>
namespace OBJ
{
std::vector<std::shared_ptr<GL::Mesh>> load_from_file(const std::string& path);
}
#endif

View File

@ -1,4 +1,5 @@
#include "shader.hpp"
#include <vector>
namespace GL
{
@ -9,9 +10,27 @@ namespace GL
GLuint vertex = compile_shader(GL_VERTEX_SHADER, vertex_src);
GLuint frag = compile_shader(GL_FRAGMENT_SHADER, fragment_src);
SYM(glAttachShader)(prog, vertex);
SYM(glAttachShader)(prog, frag);
if (vertex)
SYM(glAttachShader)(prog, vertex);
if (frag)
SYM(glAttachShader)(prog, frag);
SYM(glLinkProgram)(prog);
GLint status = 0;
SYM(glGetProgramiv)(prog, GL_LINK_STATUS, &status);
if (!status)
{
GLint len = 0;
SYM(glGetProgramiv)(prog, GL_INFO_LOG_LENGTH, &len);
if (len > 0)
{
std::vector<char> buf(len);
GLsizei out_len;
SYM(glGetProgramInfoLog)(prog, len, &out_len, buf.data());
std::cerr << "Link error: " << buf.data() << std::endl;
}
}
}
GLuint Shader::compile_shader(GLenum type, const std::string& source)
@ -22,12 +41,44 @@ namespace GL
SYM(glShaderSource)(shader, 1, &src, nullptr);
SYM(glCompileShader)(shader);
GLint status = 0;
SYM(glGetShaderiv)(shader, GL_COMPILE_STATUS, &status);
if (!status)
{
GLint len = 0;
SYM(glGetShaderiv)(shader, GL_INFO_LOG_LENGTH, &len);
if (len > 0)
{
std::vector<char> buf(len);
GLsizei out_len;
SYM(glGetShaderInfoLog)(shader, len, &out_len, buf.data());
std::cerr << "Shader error: " << buf.data() << std::endl;
}
SYM(glDeleteShader)(shader);
return 0;
}
return shader;
}
Shader::~Shader()
{
// Don't release shader.
if (dead_state)
return;
GLsizei count;
GLuint shaders[2];
SYM(glGetAttachedShaders)(prog, 2, &count, shaders);
for (GLsizei i = 0; i < count; i++)
{
SYM(glDetachShader)(prog, shaders[i]);
SYM(glDeleteShader)(shaders[i]);
}
SYM(glDeleteProgram)(prog);
}
void Shader::use()

11
simple.obj Normal file
View File

@ -0,0 +1,11 @@
texture blockDiamond
v -1.0 -1.0 0.0
v 1.0 -1.0 0.0
v 0.0 1.0 0.0
vn 0.0 0.0 -1.0
vt 0.0 0.0
vt 1.0 0.0
vt 0.5 1.0
f 1/1/1 2/2/1 3/3/1

View File

@ -47,7 +47,12 @@ namespace GL
}
Texture::~Texture()
{}
{
if (dead_state)
return;
SYM(glDeleteTextures)(1, &tex);
}
void Texture::bind(unsigned unit)
{

50
util.hpp Normal file
View File

@ -0,0 +1,50 @@
#ifndef UTIL_HPP__
#define UTIL_HPP__
#include <string>
namespace Path
{
inline std::string basedir(const std::string& path)
{
auto last = path.find_last_of("/\\");
if (last != std::string::npos)
return path.substr(0, last);
else
return ".";
}
inline std::string join(const std::string& dir, const std::string& path)
{
char last = dir.size() ? dir.back() : '\0';
std::string sep;
if (last != '/' && last != '\\')
sep = "/";
return dir + sep + path;
}
}
namespace String
{
inline std::vector<std::string> split(const std::string& str, const std::string& splitter, bool keep_empty = false)
{
std::vector<std::string> list;
for (std::size_t pos = 0, endpos = 0;
endpos != std::string::npos; pos = endpos + 1)
{
endpos = str.find_first_of(splitter, pos);
if (endpos - pos)
list.push_back(str.substr(pos, endpos - pos));
else if (keep_empty)
list.push_back("");
}
return list;
}
}
#endif