mirror of
https://github.com/libretro/modelviewer-libretro.git
synced 2025-02-21 16:20:55 +00:00
Can display simple objects with single texture.
This commit is contained in:
parent
2082adee2c
commit
787596da57
BIN
blockDiamond.png
Normal file
BIN
blockDiamond.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 237 B |
8
gl.hpp
8
gl.hpp
@ -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);
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace GL
|
||||
{
|
||||
bool dead_state;
|
||||
|
||||
static SymMap map;
|
||||
SymMap& symbol_map()
|
||||
{
|
||||
|
74
libretro.cpp
74
libretro.cpp
@ -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)
|
||||
{
|
||||
|
29
mesh.cpp
29
mesh.cpp
@ -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");
|
||||
|
13
mesh.hpp
13
mesh.hpp
@ -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
148
object.cpp
Normal 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
15
object.hpp
Normal 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
|
||||
|
57
shader.cpp
57
shader.cpp
@ -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
11
simple.obj
Normal 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
|
||||
|
@ -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
50
util.hpp
Normal 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user