Texturing; OBJ loader
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
objs/
|
objs/
|
||||||
oglpg
|
oglpg
|
||||||
|
*.trace
|
||||||
|
*~
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -1,8 +1,8 @@
|
|||||||
CXX=g++
|
CXX=g++
|
||||||
CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -std=c++11
|
CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -std=c++11
|
||||||
LDOPTS=-flto
|
LDOPTS=-flto
|
||||||
LIBS=-lGL -lSDL2
|
LIBS=-lGL -lSDL2 -lSDL2_image -lobj
|
||||||
CXXSRCS=main.cc
|
CXXSRCS=main.cc objectParser.cc
|
||||||
OBJS=$(addprefix objs/,$(CXXSRCS:.cc=.o))
|
OBJS=$(addprefix objs/,$(CXXSRCS:.cc=.o))
|
||||||
|
|
||||||
objs/%.o: %.cc
|
objs/%.o: %.cc
|
||||||
|
|||||||
121
common.hh
Normal file
121
common.hh
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_COMMON_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_COMMON_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
#define GL_GLEXT_PROTOTYPES
|
||||||
|
#include <GL/gl.h>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
class Exception {
|
||||||
|
public:
|
||||||
|
Exception() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Exception() {}
|
||||||
|
|
||||||
|
virtual std::string toString() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class POSIXException : public Exception {
|
||||||
|
public:
|
||||||
|
POSIXException(int err): Exception(), _err(err) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~POSIXException() {}
|
||||||
|
|
||||||
|
int getErr() const {return _err;}
|
||||||
|
|
||||||
|
virtual std::string toString() const {
|
||||||
|
return std::string("POSIXException: ") + std::strerror(_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _err;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GLException : public Exception {
|
||||||
|
public:
|
||||||
|
GLException(GLenum err) : Exception(), _err(err) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~GLException() {}
|
||||||
|
|
||||||
|
GLenum getErr() const {return _err;}
|
||||||
|
|
||||||
|
std::string errToString() const {
|
||||||
|
std::string ret;
|
||||||
|
if (_err == GL_INVALID_ENUM)
|
||||||
|
ret += "GL_INVALID_ENUM ";
|
||||||
|
if (_err == GL_INVALID_VALUE)
|
||||||
|
ret += "GL_INVALID_VALUE ";
|
||||||
|
if (_err == GL_INVALID_OPERATION)
|
||||||
|
ret += "GL_INVALID_OPERATION ";
|
||||||
|
if (_err == GL_INVALID_FRAMEBUFFER_OPERATION)
|
||||||
|
ret += "GL_INVALID_FRAMEBUFFER_OPERATION ";
|
||||||
|
if (_err == GL_OUT_OF_MEMORY)
|
||||||
|
ret += "GL_OUT_OF_MEMORY ";
|
||||||
|
if (_err == GL_STACK_UNDERFLOW)
|
||||||
|
ret += "GL_STACK_UNDERFLOW ";
|
||||||
|
if (_err == GL_STACK_OVERFLOW)
|
||||||
|
ret += "GL_STACK_OVERFLOW ";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string toString() const {
|
||||||
|
return "GLException: " + errToString() + "(" + std::to_string(_err) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLenum _err;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SDLException : public Exception {
|
||||||
|
public:
|
||||||
|
SDLException() : Exception(), _msg(SDL_GetError()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SDLException() {}
|
||||||
|
|
||||||
|
virtual std::string toString() const {
|
||||||
|
return "SDLException: " + _msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void checkGlError() {
|
||||||
|
GLenum err;
|
||||||
|
if ((err = glGetError()) != GL_NO_ERROR)
|
||||||
|
throw GLException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string fileToString(std::string const& name) {
|
||||||
|
std::FILE *file = std::fopen(name.c_str(), "r");
|
||||||
|
if (!file) {
|
||||||
|
throw POSIXException(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ret;
|
||||||
|
char buf[512];
|
||||||
|
std::size_t p;
|
||||||
|
while((p = std::fread(buf, 1, 511, file)) > 0) {
|
||||||
|
buf[p] = '\0';
|
||||||
|
ret.append(buf);
|
||||||
|
}
|
||||||
|
if (!std::feof(file)) {
|
||||||
|
std::fclose(file);
|
||||||
|
throw POSIXException(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fclose(file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
412
main.cc
412
main.cc
@@ -1,11 +1,12 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
|
||||||
#define GL_GLEXT_PROTOTYPES
|
#define GL_GLEXT_PROTOTYPES
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
//#include <GL/glext.h>
|
|
||||||
|
|
||||||
#define GLM_FORCE_RADIANS
|
#define GLM_FORCE_RADIANS
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
@@ -13,29 +14,59 @@
|
|||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
const char vertexShader[] = "#version 330 core\n"
|
#include "common.hh"
|
||||||
"uniform mat4 projection_matrix;\n"
|
#include "shaders.hh"
|
||||||
"uniform mat4 modelview_matrix;\n"
|
#include "texture.hh"
|
||||||
"\n"
|
#include "objectParser.hh"
|
||||||
"in vec3 vertex;\n"
|
|
||||||
"in vec3 vertexColor;\n"
|
|
||||||
"\n"
|
|
||||||
"out vec3 fragColor;\n"
|
|
||||||
"\n"
|
|
||||||
"void main(void) {\n"
|
|
||||||
"\tgl_Position = projection_matrix * modelview_matrix * vec4(vertex, 1.0);\n"
|
|
||||||
"\tfragColor = vertexColor;\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const char fragShader[] = "#version 330 core\n"
|
|
||||||
"in vec3 fragColor;\n"
|
const float cubeVertexData[][5] = {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
||||||
"\n"
|
{1.0f, 0.0f, 0.0f, 1.0f, 0.0f},
|
||||||
"out vec3 color;\n"
|
{0.0f, 1.0f, 0.0f ,0.0f, 1.0f},
|
||||||
"\n"
|
{1.0f, 1.0f, 0.0f, 1.0f, 1.0f},
|
||||||
"void main(void) {\n"
|
|
||||||
"\tcolor = fragColor;\n"
|
{1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
||||||
//"\tgl_FragDepth = gl_FragCoord.z;\n"
|
{1.0f, 0.0f, -1.0f, 1.0f, 0.0f},
|
||||||
"}\n";
|
{1.0f, 1.0f, 0.0f, 0.0f, 1.0f},
|
||||||
|
{1.0f, 1.0f, -1.0f, 1.0f, 1.0f},
|
||||||
|
|
||||||
|
{1.0f, 0.0f, -1.0f, 0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, -1.0f, 1.0f, 0.0f},
|
||||||
|
{1.0f, 1.0f, -1.0f, 0.0f, 1.0f},
|
||||||
|
{0.0f, 1.0f, -1.0f, 1.0f, 1.0f},
|
||||||
|
|
||||||
|
{0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},
|
||||||
|
{0.0f, 1.0f, -1.0f, 0.0f, 1.0f},
|
||||||
|
{0.0f, 1.0f, 0.0f, 1.0f, 1.0f},
|
||||||
|
|
||||||
|
{0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
|
||||||
|
{1.0f, 0.0f, -1.0f, 1.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
|
||||||
|
{1.0f, 0.0f, 0.0f, 1.0f, 1.0f},
|
||||||
|
|
||||||
|
{0.0f, 1.0f, 0.0f, 0.0f, 0.0f},
|
||||||
|
{1.0f, 1.0f, 0.0f, 1.0f, 0.0f},
|
||||||
|
{0.0f, 1.0f, -1.0f, 0.0f, 1.0f},
|
||||||
|
{1.0f, 1.0f, -1.0f, 1.0f, 1.0f}};
|
||||||
|
|
||||||
|
const uint8_t cubeIndices[] = {0, 1, 2,
|
||||||
|
1, 2, 3,
|
||||||
|
|
||||||
|
4, 5, 6,
|
||||||
|
5, 6, 7,
|
||||||
|
|
||||||
|
8, 9, 10,
|
||||||
|
9, 10, 11,
|
||||||
|
|
||||||
|
12, 13, 14,
|
||||||
|
13, 14, 15,
|
||||||
|
|
||||||
|
16, 17, 18,
|
||||||
|
17, 18, 19,
|
||||||
|
|
||||||
|
20, 21, 22,
|
||||||
|
21, 22, 23};
|
||||||
|
|
||||||
const float vertices[][3] = {{0.0f, 0.0f, 0.0f},
|
const float vertices[][3] = {{0.0f, 0.0f, 0.0f},
|
||||||
{1.0f, 0.0f, 0.0f},
|
{1.0f, 0.0f, 0.0f},
|
||||||
@@ -66,6 +97,21 @@ const float colors[][3] = {{1.0f, 0.0f, 1.0f},
|
|||||||
{0.0f, 1.0f, 0.0f},
|
{0.0f, 1.0f, 0.0f},
|
||||||
{1.0f, 0.0f, 0.0f}};
|
{1.0f, 0.0f, 0.0f}};
|
||||||
|
|
||||||
|
const float texcoords[][2] = {{0.0f, 0.0f},
|
||||||
|
{1.0f, 0.0f},
|
||||||
|
{0.0f, 1.0f},
|
||||||
|
{1.0f, 1.0f},
|
||||||
|
|
||||||
|
{1.0f, 1.0f},
|
||||||
|
{1.0f, 0.0f},
|
||||||
|
{0.0f, 1.0f},
|
||||||
|
{0.0f, 0.0f},
|
||||||
|
|
||||||
|
{0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f}};
|
||||||
|
|
||||||
const unsigned indices[] = {0, 2, 1,
|
const unsigned indices[] = {0, 2, 1,
|
||||||
2, 3, 1,
|
2, 3, 1,
|
||||||
|
|
||||||
@@ -90,236 +136,20 @@ const unsigned indicesPyramid[] = {8, 10, 9,
|
|||||||
8, 9, 11,
|
8, 9, 11,
|
||||||
9, 10, 11};
|
9, 10, 11};
|
||||||
|
|
||||||
class GLException{
|
|
||||||
public:
|
|
||||||
GLException(GLenum err) : _err(err) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~GLException() {}
|
|
||||||
|
|
||||||
int getErr() const {return _err;}
|
|
||||||
|
|
||||||
std::string errToString() const {
|
|
||||||
std::string ret;
|
|
||||||
if (_err == GL_INVALID_ENUM)
|
|
||||||
ret += "GL_INVALID_ENUM ";
|
|
||||||
if (_err == GL_INVALID_VALUE)
|
|
||||||
ret += "GL_INVALID_VALUE ";
|
|
||||||
if (_err == GL_INVALID_OPERATION)
|
|
||||||
ret += "GL_INVALID_OPERATION ";
|
|
||||||
if (_err == GL_INVALID_FRAMEBUFFER_OPERATION)
|
|
||||||
ret += "GL_INVALID_FRAMEBUFFER_OPERATION ";
|
|
||||||
if (_err == GL_OUT_OF_MEMORY)
|
|
||||||
ret += "GL_OUT_OF_MEMORY ";
|
|
||||||
if (_err == GL_STACK_UNDERFLOW)
|
|
||||||
ret += "GL_STACK_UNDERFLOW ";
|
|
||||||
if (_err == GL_STACK_OVERFLOW)
|
|
||||||
ret += "GL_STACK_OVERFLOW ";
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual std::string toString() const {
|
|
||||||
return "GLException: " + errToString() + "(" + std::to_string(_err) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
GLenum _err;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShaderException : public GLException {
|
|
||||||
public:
|
|
||||||
ShaderException(std::string const& msg) : GLException(glGetError()), _msg(msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ShaderException() {}
|
|
||||||
|
|
||||||
std::string const& getMsg() const {return _msg;}
|
|
||||||
|
|
||||||
virtual std::string toString() const {
|
|
||||||
return "ShaderException: " + _msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Program;
|
|
||||||
|
|
||||||
class Shader {
|
|
||||||
public:
|
|
||||||
Shader() : _shaderID(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader(std::string const& program, GLenum type) {
|
|
||||||
_shaderID = glCreateShader(type);
|
|
||||||
const char* const arr[] = {program.c_str()};
|
|
||||||
glShaderSource(_shaderID, 1, arr, NULL);
|
|
||||||
glCompileShader(_shaderID);
|
|
||||||
|
|
||||||
int state;
|
|
||||||
glGetShaderiv(_shaderID, GL_COMPILE_STATUS, &state);
|
|
||||||
|
|
||||||
if (!state) {
|
|
||||||
int logLength;
|
|
||||||
glGetShaderiv(_shaderID, GL_INFO_LOG_LENGTH, &logLength);
|
|
||||||
|
|
||||||
char *log = new char[logLength];
|
|
||||||
glGetShaderInfoLog(_shaderID, logLength, NULL, log);
|
|
||||||
std::string msg(log);
|
|
||||||
delete[] log;
|
|
||||||
|
|
||||||
glDeleteShader(_shaderID);
|
|
||||||
|
|
||||||
throw ShaderException(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Shader() {
|
|
||||||
glDeleteShader(_shaderID);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
unsigned getID() const {
|
|
||||||
return _shaderID;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend class Program;
|
|
||||||
|
|
||||||
unsigned int _shaderID;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class VertexShader : public Shader {
|
|
||||||
public:
|
|
||||||
VertexShader(std::string const& program) : Shader(program, GL_VERTEX_SHADER) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~VertexShader() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class FragmentShader : public Shader {
|
|
||||||
public:
|
|
||||||
FragmentShader(std::string const& program) : Shader(program, GL_FRAGMENT_SHADER) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~FragmentShader() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class GeometryShader : public Shader {
|
|
||||||
public:
|
|
||||||
GeometryShader(std::string const& program) : Shader(program, GL_GEOMETRY_SHADER) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~GeometryShader() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Program {
|
|
||||||
public:
|
|
||||||
Program(VertexShader& vertex, FragmentShader& frag) : _geom(nullptr), _vertex(vertex), _frag(frag) {
|
|
||||||
_progID = glCreateProgram();
|
|
||||||
|
|
||||||
glAttachShader(_progID, _vertex.getID());
|
|
||||||
glAttachShader(_progID, _frag.getID());
|
|
||||||
|
|
||||||
glLinkProgram(_progID);
|
|
||||||
|
|
||||||
int state;
|
|
||||||
glGetProgramiv(_progID, GL_LINK_STATUS, &state);
|
|
||||||
|
|
||||||
if (!state) {
|
|
||||||
int logLength;
|
|
||||||
glGetProgramiv(_progID, GL_INFO_LOG_LENGTH, &logLength);
|
|
||||||
|
|
||||||
char *log = new char[logLength];
|
|
||||||
glGetProgramInfoLog(_progID, logLength, NULL, log);
|
|
||||||
std::string msg(log);
|
|
||||||
delete[] log;
|
|
||||||
|
|
||||||
glDeleteProgram(_progID);
|
|
||||||
|
|
||||||
throw ShaderException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
glDetachShader(_progID, _vertex.getID());
|
|
||||||
glDetachShader(_progID, _frag.getID());
|
|
||||||
}
|
|
||||||
|
|
||||||
Program(GeometryShader& geom, VertexShader& vertex, FragmentShader& frag) : _geom(&geom), _vertex(vertex), _frag(frag) {
|
|
||||||
_progID = glCreateProgram();
|
|
||||||
|
|
||||||
glAttachShader(_progID, _geom->getID());
|
|
||||||
glAttachShader(_progID, _vertex.getID());
|
|
||||||
glAttachShader(_progID, _frag.getID());
|
|
||||||
|
|
||||||
glLinkProgram(_progID);
|
|
||||||
|
|
||||||
int state;
|
|
||||||
glGetProgramiv(_progID, GL_LINK_STATUS, &state);
|
|
||||||
|
|
||||||
if (!state) {
|
|
||||||
int logLength;
|
|
||||||
glGetProgramiv(_progID, GL_INFO_LOG_LENGTH, &logLength);
|
|
||||||
|
|
||||||
char *log = new char[logLength];
|
|
||||||
glGetProgramInfoLog(_progID, logLength, NULL, log);
|
|
||||||
std::string msg(log);
|
|
||||||
delete[] log;
|
|
||||||
|
|
||||||
glDeleteProgram(_progID);
|
|
||||||
|
|
||||||
throw ShaderException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
glDetachShader(_progID, _geom->getID());
|
|
||||||
glDetachShader(_progID, _vertex.getID());
|
|
||||||
glDetachShader(_progID, _frag.getID());
|
|
||||||
}
|
|
||||||
|
|
||||||
~Program() {
|
|
||||||
glDeleteProgram(_progID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void use() const {
|
|
||||||
glUseProgram(_progID);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getUniformLocation(std::string const& name) const {
|
|
||||||
int ret = glGetUniformLocation(_progID, name.c_str());
|
|
||||||
if (ret == -1)
|
|
||||||
throw GLException(glGetError());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getAttribLocation(std::string const& name) const {
|
|
||||||
int ret = glGetAttribLocation(_progID, name.c_str());
|
|
||||||
if (ret == -1)
|
|
||||||
throw GLException(glGetError());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
GeometryShader* _geom;
|
|
||||||
VertexShader& _vertex;
|
|
||||||
FragmentShader& _frag;
|
|
||||||
unsigned _progID;
|
|
||||||
};
|
|
||||||
|
|
||||||
void checkGlError() {
|
|
||||||
GLenum err;
|
|
||||||
if ((err = glGetError()) != GL_NO_ERROR)
|
|
||||||
throw GLException(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
int retcode = 0;
|
int retcode = 0;
|
||||||
|
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||||
|
std::printf("Could not init SDL: %s\n", SDL_GetError());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) == 0) {
|
||||||
|
std::printf("Could not init SDL_image: %s\n", SDL_GetError());
|
||||||
|
SDL_Quit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||||
@@ -327,7 +157,7 @@ int main(int argc, char *argv[])
|
|||||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
||||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||||
@@ -337,7 +167,7 @@ int main(int argc, char *argv[])
|
|||||||
SDL_WINDOWPOS_UNDEFINED,
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
640,
|
640,
|
||||||
480,
|
480,
|
||||||
/*SDL_WINDOW_FULLSCREEN_DESKTOP |*/ SDL_WINDOW_OPENGL);
|
SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL);
|
||||||
|
|
||||||
if (!window) {
|
if (!window) {
|
||||||
std::printf("Could not create window: %s\n", SDL_GetError());
|
std::printf("Could not create window: %s\n", SDL_GetError());
|
||||||
@@ -374,9 +204,7 @@ int main(int argc, char *argv[])
|
|||||||
0.1f, 100.0f);
|
0.1f, 100.0f);
|
||||||
glm::mat4 view = glm::lookAt(glm::vec3(0, 0, 10),
|
glm::mat4 view = glm::lookAt(glm::vec3(0, 0, 10),
|
||||||
glm::vec3(0, 0, 0),
|
glm::vec3(0, 0, 0),
|
||||||
glm::vec3(0, 1, 0));
|
glm::vec3(0, 1, 0));
|
||||||
// glm::mat4 model = glm::rotate(0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
|
||||||
|
|
||||||
// sf::Font font;
|
// sf::Font font;
|
||||||
// if (!font.loadFromFile("DejaVuSans.ttf")) {
|
// if (!font.loadFromFile("DejaVuSans.ttf")) {
|
||||||
// std::printf("Error loading font\n");
|
// std::printf("Error loading font\n");
|
||||||
@@ -391,16 +219,29 @@ int main(int argc, char *argv[])
|
|||||||
// sf::Clock clock;
|
// sf::Clock clock;
|
||||||
// sf::Time last = clock.getElapsedTime();
|
// sf::Time last = clock.getElapsedTime();
|
||||||
|
|
||||||
checkGlError();
|
Texture2D cubeTex("textures/Wood_Box_Texture.jpg");
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic"))
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4.0f);
|
||||||
|
else
|
||||||
|
std::printf("Warning: extension GL_EXT_texture_filter_anisotropic not supported\n");
|
||||||
|
|
||||||
|
auto box = readObject("objects/woodbox.obj");
|
||||||
|
|
||||||
VertexShader vs{vertexShader};
|
VertexShader vs{fileToString("shaders/textured.vs")};
|
||||||
FragmentShader fs{fragShader};
|
FragmentShader fs{fileToString("shaders/textured.fs")};
|
||||||
Program prog(vs, fs);
|
Program prog(vs, fs);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
prog.use();
|
prog.use();
|
||||||
glUniformMatrix4fv(prog.getUniformLocation("projection_matrix"), 1, GL_FALSE,
|
glUniformMatrix4fv(prog.getUniformLocation("projection_matrix"), 1, GL_FALSE,
|
||||||
glm::value_ptr(proj));
|
glm::value_ptr(proj));
|
||||||
|
glUniform1i(prog.getUniformLocation("texBase"), 0);
|
||||||
|
|
||||||
|
int vertexAL = prog.getAttribLocation("vertex");
|
||||||
|
//int vertexColorAL = prog.getAttribLocation("vertexColor");
|
||||||
|
int vertexTCAL = prog.getAttribLocation("vertexTC");
|
||||||
|
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
GLuint buf;
|
GLuint buf;
|
||||||
@@ -408,13 +249,35 @@ int main(int argc, char *argv[])
|
|||||||
glBindBuffer(GL_ARRAY_BUFFER, buf);
|
glBindBuffer(GL_ARRAY_BUFFER, buf);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, 2*sizeof(float)*3*12, NULL, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*24*5, NULL, GL_STATIC_DRAW);
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*12, (void*)vertices[0]);
|
//glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*24*5, (void*)cubeVertexData[0]);
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*3*12, sizeof(float)*3*12, (void*)colors[0]);
|
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(objVertexAttribs)*std::get<0>(box).size(),
|
||||||
|
(void*)std::get<0>(box).data());
|
||||||
|
|
||||||
GLuint arr;
|
// glBufferData(GL_ARRAY_BUFFER, sizeof(float)*(2*3*12+2*12), NULL, GL_STATIC_DRAW);
|
||||||
glGenVertexArrays(1, &arr);
|
// glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*12, (void*)vertices[0]);
|
||||||
glBindVertexArray(arr);
|
// glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*3*12, sizeof(float)*3*12, (void*)colors[0]);
|
||||||
|
// glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*2*3*12, sizeof(float)*2*12, (void*)texcoords[0]);
|
||||||
|
|
||||||
|
GLuint arr[2];
|
||||||
|
glGenVertexArrays(2, arr);
|
||||||
|
checkGlError();
|
||||||
|
|
||||||
|
// glBindVertexArray(arr[0]);
|
||||||
|
// glEnableVertexAttribArray(vertexAL);
|
||||||
|
// glEnableVertexAttribArray(vertexColorAL);
|
||||||
|
// glEnableVertexAttribArray(vertexTCAL);
|
||||||
|
// glVertexAttribPointer(vertexAL, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||||
|
// glVertexAttribPointer(vertexColorAL, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(float)*3*12));
|
||||||
|
// glVertexAttribPointer(vertexTCAL, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(float)*2*3*12));
|
||||||
|
|
||||||
|
glBindVertexArray(arr[0]);
|
||||||
|
glEnableVertexAttribArray(vertexAL);
|
||||||
|
glEnableVertexAttribArray(vertexTCAL);
|
||||||
|
glVertexAttribPointer(vertexAL, 3, GL_FLOAT, GL_FALSE, sizeof(objVertexAttribs), offsetof(objVertexAttribs, vertex));
|
||||||
|
glVertexAttribPointer(vertexTCAL, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(objVertexAttribs),
|
||||||
|
(void*)offsetof(objVertexAttribs, texCoords));
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
@@ -440,26 +303,19 @@ int main(int argc, char *argv[])
|
|||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
prog.use();
|
prog.use();
|
||||||
glm::mat4 model = glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f));
|
glm::mat4 model = glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
glUniformMatrix4fv(prog.getUniformLocation("modelview_matrix"), 1, GL_FALSE,
|
glUniformMatrix4fv(prog.getUniformLocation("modelview_matrix"), 1, GL_FALSE,
|
||||||
glm::value_ptr(view*model));
|
glm::value_ptr(view*model));
|
||||||
|
// glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, indices);
|
||||||
|
glDrawElements(GL_TRIANGLES, std::get<1>(box).size(), GL_UNSIGNED_SHORT, std::get<1>(box).data());
|
||||||
checkGlError();
|
checkGlError();
|
||||||
glEnableVertexAttribArray(prog.getAttribLocation("vertex"));
|
|
||||||
glEnableVertexAttribArray(prog.getAttribLocation("vertexColor"));
|
|
||||||
checkGlError();
|
|
||||||
glVertexAttribPointer(prog.getAttribLocation("vertex"), 3, GL_FLOAT, GL_FALSE, 0, 0);
|
|
||||||
checkGlError();
|
|
||||||
glVertexAttribPointer(prog.getAttribLocation("vertexColor"), 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(float)*3*12));
|
|
||||||
checkGlError();
|
|
||||||
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, indices);
|
|
||||||
checkGlError();
|
|
||||||
glUniformMatrix4fv(prog.getUniformLocation("modelview_matrix"), 1, GL_FALSE,
|
|
||||||
glm::value_ptr(view));
|
|
||||||
checkGlError();
|
|
||||||
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, indicesPyramid);
|
|
||||||
|
|
||||||
checkGlError();
|
// glUniformMatrix4fv(prog.getUniformLocation("modelview_matrix"), 1, GL_FALSE,
|
||||||
|
// glm::value_ptr(view));
|
||||||
|
// glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, indicesPyramid);
|
||||||
|
// checkGlError();
|
||||||
|
|
||||||
// glUseProgram(0);
|
// glUseProgram(0);
|
||||||
// sf::Time now = clock.getElapsedTime();
|
// sf::Time now = clock.getElapsedTime();
|
||||||
@@ -471,7 +327,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
SDL_GL_SwapWindow(window);
|
SDL_GL_SwapWindow(window);
|
||||||
}
|
}
|
||||||
}catch(GLException &ex) {
|
}catch(Exception &ex) {
|
||||||
std::printf("%s\n", ex.toString().c_str());
|
std::printf("%s\n", ex.toString().c_str());
|
||||||
retcode = 1;
|
retcode = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
281
objectParser.cc
Normal file
281
objectParser.cc
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
#include <map>
|
||||||
|
#include <tr1/functional>
|
||||||
|
#include <obj.hpp>
|
||||||
|
|
||||||
|
#include "objectParser.hh"
|
||||||
|
|
||||||
|
using namespace obj;
|
||||||
|
|
||||||
|
using std::tr1::placeholders::_1;
|
||||||
|
using std::tr1::placeholders::_2;
|
||||||
|
using std::tr1::placeholders::_3;
|
||||||
|
using std::tr1::placeholders::_4;
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectParser {
|
||||||
|
private:
|
||||||
|
void texture_vertex_callback(obj::float_type u, obj::float_type v)
|
||||||
|
{
|
||||||
|
if ((u > 1.0f) || (u < 0.0f))
|
||||||
|
std::cout << "Warning: Clamping texture coordinate U to [0.0, 1.0]" << std::endl;
|
||||||
|
uint16_t c_u = static_cast<uint16_t>(fminf(1.0f, fmaxf(0.0f, u))*65535.0);
|
||||||
|
if ((v > 1.0f) || (v < 0.0f))
|
||||||
|
std::cout << "Warning: Clamping texture coordinate V to [0.0, 1.0]" << std::endl;
|
||||||
|
uint16_t c_v = static_cast<uint16_t>(fminf(1.0f, fmaxf(0.0f, v))*65535.0);
|
||||||
|
_texCoords.emplace_back(c_u, c_v);
|
||||||
|
std::cout << "vt " << c_u << " " << c_v << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void geometric_vertex_callback(obj::float_type x, obj::float_type y, obj::float_type z)
|
||||||
|
{
|
||||||
|
_vertices.emplace_back(x, y, z);
|
||||||
|
std::cout << "v " << x << " " << y << " " << z << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex_normal_callback(obj::float_type x, obj::float_type y, obj::float_type z)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
signed :2;
|
||||||
|
signed z:10;
|
||||||
|
signed y:10;
|
||||||
|
signed x:10;
|
||||||
|
} bits;
|
||||||
|
uint32_t all;
|
||||||
|
} pack_10;
|
||||||
|
if ((x > 1.0f) || (x < -1.0f))
|
||||||
|
std::cout << "Warning: Clamping normal X to [-1.0, 1.0]" << std::endl;
|
||||||
|
pack_10.bits.x = static_cast<int>(fminf(1.0f, fmaxf(-1.0f, x))*511.0);
|
||||||
|
if ((y > 1.0f) || (y < -1.0f))
|
||||||
|
std::cout << "Warning: Clamping normal Y to [-1.0, 1.0]" << std::endl;
|
||||||
|
pack_10.bits.y = static_cast<int>(fminf(1.0f, fmaxf(-1.0f, y))*511.0);
|
||||||
|
if ((z > 1.0f) || (z < -1.0f))
|
||||||
|
std::cout << "Warning: Clamping normal Z to [-1.0, 1.0]" << std::endl;
|
||||||
|
pack_10.bits.z = static_cast<int>(fminf(1.0f, fmaxf(-1.0f, z))*511.0);
|
||||||
|
_normals.push_back(pack_10.all);
|
||||||
|
std::cout << "vn " << pack_10.bits.x << " " << pack_10.bits.y << " " << pack_10.bits.z << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void triangular_face_geometric_vertices_callback(index_type a, index_type b, index_type c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _find_vertex_tc_pair(index_2_tuple_type const& a)
|
||||||
|
{
|
||||||
|
auto pair_it = _vertex_tc_pairs_map.find(std::tuple<std::size_t, std::size_t>(std::tr1::get<0>(a), std::tr1::get<1>(a)));
|
||||||
|
size_t pair_pos;
|
||||||
|
if (pair_it != _vertex_tc_pairs_map.end())
|
||||||
|
pair_pos = pair_it->second;
|
||||||
|
else {
|
||||||
|
_vertex_tc_pairs.emplace_back(std::get<0>(_vertices[std::tr1::get<0>(a)-1]),
|
||||||
|
std::get<1>(_vertices[std::tr1::get<0>(a)-1]),
|
||||||
|
std::get<2>(_vertices[std::tr1::get<0>(a)-1]),
|
||||||
|
std::get<0>(_texCoords[std::tr1::get<1>(a)-1]),
|
||||||
|
std::get<1>(_texCoords[std::tr1::get<1>(a)-1]));
|
||||||
|
pair_pos = _vertex_tc_pairs.size()-1;
|
||||||
|
_vertex_tc_pairs_map.emplace(std::tuple<std::size_t, std::size_t>(std::tr1::get<0>(a), std::tr1::get<1>(a)),
|
||||||
|
pair_pos);
|
||||||
|
}
|
||||||
|
return pair_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void triangular_face_geometric_vertices_texture_vertices_callback(index_2_tuple_type a, index_2_tuple_type b, index_2_tuple_type c)
|
||||||
|
{
|
||||||
|
if ((_vertices.size() < std::tr1::get<0>(a)) ||
|
||||||
|
(_vertices.size() < std::tr1::get<0>(b)) ||
|
||||||
|
(_vertices.size() < std::tr1::get<0>(c)))
|
||||||
|
throw ParseException("Vertex Index out of range");
|
||||||
|
if ((_texCoords.size() < std::tr1::get<1>(a)) ||
|
||||||
|
(_texCoords.size() < std::tr1::get<1>(b)) ||
|
||||||
|
(_texCoords.size() < std::tr1::get<1>(c)))
|
||||||
|
throw ParseException("Texture Coordinate Index out of range");
|
||||||
|
|
||||||
|
std::cout << "f (" << std::tr1::get<0>(a) << ", " << std::tr1::get<1>(a) << ")" <<
|
||||||
|
" (" << std::tr1::get<0>(b) << ", " << std::tr1::get<1>(b) << ")" <<
|
||||||
|
" (" << std::tr1::get<0>(c) << ", " << std::tr1::get<1>(c) << ")\n";
|
||||||
|
|
||||||
|
size_t a_pos = _find_vertex_tc_pair(a);
|
||||||
|
size_t b_pos = _find_vertex_tc_pair(b);
|
||||||
|
size_t c_pos = _find_vertex_tc_pair(c);
|
||||||
|
_faces.emplace_back(a_pos, b_pos, c_pos);
|
||||||
|
|
||||||
|
std::cout << "\t [" << a_pos << ", " << b_pos << ", " << c_pos << "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void triangular_face_geometric_vertices_vertex_normals_callback(index_2_tuple_type a, index_2_tuple_type b, index_2_tuple_type c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void triangular_face_geometric_vertices_texture_vertices_vertex_normals_callback(index_3_tuple_type a, index_3_tuple_type b,
|
||||||
|
index_3_tuple_type c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void quadrilateral_face_geometric_vertices_callback(index_type a, index_type b, index_type c, index_type d)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void quadrilateral_face_geometric_vertices_texture_vertices_callback(index_2_tuple_type a, index_2_tuple_type b,
|
||||||
|
index_2_tuple_type c, index_2_tuple_type d)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void quadrilateral_face_geometric_vertices_vertex_normals_callback(index_2_tuple_type a, index_2_tuple_type b,
|
||||||
|
index_2_tuple_type c, index_2_tuple_type d)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void quadrilateral_face_geometric_vertices_texture_vertices_vertex_normals_callback(index_3_tuple_type a, index_3_tuple_type b,
|
||||||
|
index_3_tuple_type c, index_3_tuple_type d)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_begin_callback(index_type a, index_type b, index_type c)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_vertex_callback(index_type a)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_end_callback()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_texture_vertices_begin_callback(index_2_tuple_type a, index_2_tuple_type b,
|
||||||
|
index_2_tuple_type c)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_texture_vertices_vertex_callback(index_2_tuple_type a)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_texture_vertices_end_callback()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_vertex_normals_begin_callback(index_2_tuple_type a, index_2_tuple_type b,
|
||||||
|
index_2_tuple_type c)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_vertex_normals_vertex_callback(index_2_tuple_type a)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_vertex_normals_end_callback()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_texture_vertices_vertex_normals_begin_callback(index_3_tuple_type a, index_3_tuple_type b,
|
||||||
|
index_3_tuple_type c)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_texture_vertices_vertex_normals_vertex_callback(index_3_tuple_type a)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polygonal_face_geometric_vertices_texture_vertices_vertex_normals_end_callback()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::tuple<float, float, float> > _vertices;
|
||||||
|
std::vector<std::tuple<uint16_t, uint16_t> > _texCoords;
|
||||||
|
std::vector<uint32_t> _normals;
|
||||||
|
|
||||||
|
std::map<std::tuple<std::size_t, std::size_t>, std::size_t > _vertex_tc_pairs_map;
|
||||||
|
std::vector<std::tuple<float, float, float, uint16_t, uint16_t> > _vertex_tc_pairs;
|
||||||
|
std::vector<std::tuple<std::size_t, std::size_t, std::size_t> > _faces;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ObjectParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
~ObjectParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse(std::string const& filename)
|
||||||
|
{
|
||||||
|
obj::obj_parser obj_parser(obj::obj_parser::triangulate_faces | obj::obj_parser::translate_negative_indices);
|
||||||
|
obj_parser.geometric_vertex_callback(std::tr1::bind(&ObjectParser::geometric_vertex_callback, this, _1, _2, _3));
|
||||||
|
obj_parser.texture_vertex_callback(std::tr1::bind(&ObjectParser::texture_vertex_callback, this, _1, _2));
|
||||||
|
obj_parser.vertex_normal_callback(std::tr1::bind(&ObjectParser::vertex_normal_callback, this, _1, _2, _3));
|
||||||
|
obj_parser.face_callbacks
|
||||||
|
(std::tr1::bind(&ObjectParser::triangular_face_geometric_vertices_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::triangular_face_geometric_vertices_texture_vertices_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::triangular_face_geometric_vertices_vertex_normals_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::triangular_face_geometric_vertices_texture_vertices_vertex_normals_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::quadrilateral_face_geometric_vertices_callback, this, _1, _2, _3, _4),
|
||||||
|
std::tr1::bind(&ObjectParser::quadrilateral_face_geometric_vertices_texture_vertices_callback, this, _1, _2, _3, _4),
|
||||||
|
std::tr1::bind(&ObjectParser::quadrilateral_face_geometric_vertices_vertex_normals_callback, this, _1, _2, _3, _4),
|
||||||
|
std::tr1::bind(&ObjectParser::quadrilateral_face_geometric_vertices_texture_vertices_vertex_normals_callback, this, _1, _2, _3, _4),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_begin_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_vertex_callback, this, _1),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_end_callback, this),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_texture_vertices_begin_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_texture_vertices_vertex_callback, this, _1),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_texture_vertices_end_callback, this),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_vertex_normals_begin_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_vertex_normals_vertex_callback, this, _1),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_vertex_normals_end_callback, this),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_texture_vertices_vertex_normals_begin_callback, this, _1, _2, _3),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_texture_vertices_vertex_normals_vertex_callback, this, _1),
|
||||||
|
std::tr1::bind(&ObjectParser::polygonal_face_geometric_vertices_texture_vertices_vertex_normals_end_callback, this));
|
||||||
|
obj_parser.parse(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<objVertexAttribs> buildVA()
|
||||||
|
{
|
||||||
|
std::vector<objVertexAttribs> ret;
|
||||||
|
for (auto const& ent : _vertex_tc_pairs) {
|
||||||
|
objVertexAttribs tmp;
|
||||||
|
tmp.vertex[0] = std::get<0>(ent);
|
||||||
|
tmp.vertex[1] = std::get<1>(ent);
|
||||||
|
tmp.vertex[2] = std::get<2>(ent);
|
||||||
|
tmp.texCoords[0] = std::get<3>(ent);
|
||||||
|
tmp.texCoords[1] = std::get<4>(ent);
|
||||||
|
ret.push_back(tmp);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint16_t> buildIndices() const
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> ret;
|
||||||
|
for (auto const& ent : _faces) {
|
||||||
|
ret.push_back(std::get<0>(ent));
|
||||||
|
ret.push_back(std::get<1>(ent));
|
||||||
|
ret.push_back(std::get<2>(ent));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::tuple<std::vector<objVertexAttribs>, std::vector<uint16_t> > readObject(std::string const& filename)
|
||||||
|
{
|
||||||
|
ObjectParser parser;
|
||||||
|
parser.parse(filename);
|
||||||
|
return std::tuple<std::vector<objVertexAttribs>, std::vector<uint16_t> >(parser.buildVA(), parser.buildIndices());
|
||||||
|
}
|
||||||
34
objectParser.hh
Normal file
34
objectParser.hh
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_OBJECTPARSER_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_OBJECTPARSER_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common.hh"
|
||||||
|
|
||||||
|
class ParseException : public Exception {
|
||||||
|
public:
|
||||||
|
ParseException(std::string const& msg) : Exception(), _msg(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ParseException() {}
|
||||||
|
|
||||||
|
std::string const& getMsg() const {return _msg;}
|
||||||
|
|
||||||
|
virtual std::string toString() const {
|
||||||
|
return "ParseException: " + _msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct objVertexAttribs {
|
||||||
|
float vertex[3];
|
||||||
|
uint16_t texCoords[2];
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
|
||||||
|
std::tuple<std::vector<objVertexAttribs>, std::vector<uint16_t> > readObject(std::string const& filename);
|
||||||
|
|
||||||
|
#endif
|
||||||
33
objects/woodbox.obj
Normal file
33
objects/woodbox.obj
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
v 0.0 0.0 0.0
|
||||||
|
v 1.0 0.0 0.0
|
||||||
|
v 0.0 1.0 0.0
|
||||||
|
v 1.0 1.0 0.0
|
||||||
|
|
||||||
|
v 0.0 0.0 -1.0
|
||||||
|
v 1.0 0.0 -1.0
|
||||||
|
v 0.0 1.0 -1.0
|
||||||
|
v 1.0 1.0 -1.0
|
||||||
|
|
||||||
|
vt 0.0 0.0
|
||||||
|
vt 1.0 0.0
|
||||||
|
vt 0.0 1.0
|
||||||
|
vt 1.0 1.0
|
||||||
|
|
||||||
|
vn 0.0 0.0 1.0
|
||||||
|
vn 1.0 0.0 0.0
|
||||||
|
vn 0.0 0.0 -1.0
|
||||||
|
vn -1.0 0.0 0.0
|
||||||
|
vn 0.0 1.0 0.0
|
||||||
|
vn 0.0 -1.0 0.0
|
||||||
|
|
||||||
|
f 1/1 3/3 4/4 2/2
|
||||||
|
|
||||||
|
f 2/1 4/3 8/4 6/2
|
||||||
|
|
||||||
|
f 6/1 8/3 7/4 5/2
|
||||||
|
|
||||||
|
f 5/1 7/3 3/4 1/2
|
||||||
|
|
||||||
|
f 5/1 1/3 2/4 6/2
|
||||||
|
|
||||||
|
f 3/1 7/3 8/4 4/2
|
||||||
205
shaders.hh
Normal file
205
shaders.hh
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_SHADERS_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_SHADERS_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#define GL_GLEXT_PROTOTYPES
|
||||||
|
#include <GL/gl.h>
|
||||||
|
|
||||||
|
#include "common.hh"
|
||||||
|
|
||||||
|
class ShaderException : public GLException {
|
||||||
|
public:
|
||||||
|
ShaderException(std::string const& msg) : GLException(glGetError()), _msg(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ShaderException() {}
|
||||||
|
|
||||||
|
std::string const& getMsg() const {return _msg;}
|
||||||
|
|
||||||
|
virtual std::string toString() const {
|
||||||
|
return "ShaderException: " + _msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Program;
|
||||||
|
|
||||||
|
class Shader {
|
||||||
|
public:
|
||||||
|
Shader() : _shaderID(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader(std::string const& program, GLenum type) {
|
||||||
|
_shaderID = glCreateShader(type);
|
||||||
|
const char* const arr[] = {program.c_str()};
|
||||||
|
glShaderSource(_shaderID, 1, arr, NULL);
|
||||||
|
glCompileShader(_shaderID);
|
||||||
|
|
||||||
|
int state;
|
||||||
|
glGetShaderiv(_shaderID, GL_COMPILE_STATUS, &state);
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
int logLength;
|
||||||
|
glGetShaderiv(_shaderID, GL_INFO_LOG_LENGTH, &logLength);
|
||||||
|
|
||||||
|
char *log = new char[logLength];
|
||||||
|
glGetShaderInfoLog(_shaderID, logLength, NULL, log);
|
||||||
|
std::string msg(log);
|
||||||
|
delete[] log;
|
||||||
|
|
||||||
|
glDeleteShader(_shaderID);
|
||||||
|
|
||||||
|
throw ShaderException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Shader() {
|
||||||
|
glDeleteShader(_shaderID);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned getID() const {
|
||||||
|
return _shaderID;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class Program;
|
||||||
|
|
||||||
|
unsigned int _shaderID;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VertexShader : public Shader {
|
||||||
|
public:
|
||||||
|
VertexShader(std::string const& program) : Shader(program, GL_VERTEX_SHADER) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~VertexShader() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FragmentShader : public Shader {
|
||||||
|
public:
|
||||||
|
FragmentShader(std::string const& program) : Shader(program, GL_FRAGMENT_SHADER) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FragmentShader() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GeometryShader : public Shader {
|
||||||
|
public:
|
||||||
|
GeometryShader(std::string const& program) : Shader(program, GL_GEOMETRY_SHADER) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~GeometryShader() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Program {
|
||||||
|
public:
|
||||||
|
Program(VertexShader& vertex, FragmentShader& frag) : _geom(nullptr), _vertex(vertex), _frag(frag) {
|
||||||
|
_progID = glCreateProgram();
|
||||||
|
|
||||||
|
glAttachShader(_progID, _vertex.getID());
|
||||||
|
glAttachShader(_progID, _frag.getID());
|
||||||
|
|
||||||
|
glLinkProgram(_progID);
|
||||||
|
|
||||||
|
int state;
|
||||||
|
glGetProgramiv(_progID, GL_LINK_STATUS, &state);
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
int logLength;
|
||||||
|
glGetProgramiv(_progID, GL_INFO_LOG_LENGTH, &logLength);
|
||||||
|
|
||||||
|
char *log = new char[logLength];
|
||||||
|
glGetProgramInfoLog(_progID, logLength, NULL, log);
|
||||||
|
std::string msg(log);
|
||||||
|
delete[] log;
|
||||||
|
|
||||||
|
glDeleteProgram(_progID);
|
||||||
|
|
||||||
|
throw ShaderException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDetachShader(_progID, _vertex.getID());
|
||||||
|
glDetachShader(_progID, _frag.getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
Program(GeometryShader& geom, VertexShader& vertex, FragmentShader& frag) : _geom(&geom), _vertex(vertex), _frag(frag) {
|
||||||
|
_progID = glCreateProgram();
|
||||||
|
|
||||||
|
glAttachShader(_progID, _geom->getID());
|
||||||
|
glAttachShader(_progID, _vertex.getID());
|
||||||
|
glAttachShader(_progID, _frag.getID());
|
||||||
|
|
||||||
|
glLinkProgram(_progID);
|
||||||
|
|
||||||
|
int state;
|
||||||
|
glGetProgramiv(_progID, GL_LINK_STATUS, &state);
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
int logLength;
|
||||||
|
glGetProgramiv(_progID, GL_INFO_LOG_LENGTH, &logLength);
|
||||||
|
|
||||||
|
char *log = new char[logLength];
|
||||||
|
glGetProgramInfoLog(_progID, logLength, NULL, log);
|
||||||
|
std::string msg(log);
|
||||||
|
delete[] log;
|
||||||
|
|
||||||
|
glDeleteProgram(_progID);
|
||||||
|
|
||||||
|
throw ShaderException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDetachShader(_progID, _geom->getID());
|
||||||
|
glDetachShader(_progID, _vertex.getID());
|
||||||
|
glDetachShader(_progID, _frag.getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
~Program() {
|
||||||
|
glDeleteProgram(_progID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void use() const {
|
||||||
|
glUseProgram(_progID);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getUniformLocation(std::string const& name) const {
|
||||||
|
auto search = _uniformLocCache.find(name);
|
||||||
|
if (search != _uniformLocCache.end())
|
||||||
|
return search->second;
|
||||||
|
|
||||||
|
int ret = glGetUniformLocation(_progID, name.c_str());
|
||||||
|
if (ret == -1)
|
||||||
|
throw GLException(glGetError());
|
||||||
|
_uniformLocCache.emplace(name, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getAttribLocation(std::string const& name) const {
|
||||||
|
auto search = _attribLocCache.find(name);
|
||||||
|
if (search != _attribLocCache.end())
|
||||||
|
return search->second;
|
||||||
|
|
||||||
|
int ret = glGetAttribLocation(_progID, name.c_str());
|
||||||
|
if (ret == -1)
|
||||||
|
throw GLException(glGetError());
|
||||||
|
_attribLocCache.emplace(name, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GeometryShader* _geom;
|
||||||
|
VertexShader& _vertex;
|
||||||
|
FragmentShader& _frag;
|
||||||
|
unsigned _progID;
|
||||||
|
|
||||||
|
mutable std::unordered_map<std::string, GLint> _uniformLocCache, _attribLocCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
9
shaders/color.fs
Normal file
9
shaders/color.fs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 fragColor;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
color = vec4(fragColor, 1.0);
|
||||||
|
}
|
||||||
14
shaders/color.vs
Normal file
14
shaders/color.vs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform mat4 projection_matrix;
|
||||||
|
uniform mat4 modelview_matrix;
|
||||||
|
|
||||||
|
in vec3 vertex;
|
||||||
|
in vec3 vertecColor;
|
||||||
|
|
||||||
|
out vec3 fragColor;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = projection_matrix * modelview_matrix * vec4(vertex, 1.0);
|
||||||
|
fragColor = vertexColor;
|
||||||
|
}
|
||||||
11
shaders/textured.fs
Normal file
11
shaders/textured.fs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform sampler2D texBase;
|
||||||
|
|
||||||
|
in vec2 fragTC;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
color = texture(texBase, fragTC);
|
||||||
|
}
|
||||||
14
shaders/textured.vs
Normal file
14
shaders/textured.vs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform mat4 projection_matrix;
|
||||||
|
uniform mat4 modelview_matrix;
|
||||||
|
|
||||||
|
in vec3 vertex;
|
||||||
|
in vec2 vertexTC;
|
||||||
|
|
||||||
|
out vec2 fragTC;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = projection_matrix * modelview_matrix * vec4(vertex, 1.0);
|
||||||
|
fragTC = vertexTC;
|
||||||
|
}
|
||||||
95
texture.hh
Normal file
95
texture.hh
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_TEXTURE_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_TEXTURE_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
|
||||||
|
#define GL_GLEXT_PROTOTYPES
|
||||||
|
#include <GL/gl.h>
|
||||||
|
|
||||||
|
#include "common.hh"
|
||||||
|
|
||||||
|
static unsigned ilog2(unsigned in)
|
||||||
|
{
|
||||||
|
unsigned ret = 0u;
|
||||||
|
while (in >>= 1) ++ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Texture2D {
|
||||||
|
public:
|
||||||
|
Texture2D(unsigned width, unsigned height) {
|
||||||
|
_glCreate(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D(std::string const& file) {
|
||||||
|
SDL_Surface *surf = IMG_Load(file.c_str());
|
||||||
|
if (!surf)
|
||||||
|
throw SDLException();
|
||||||
|
|
||||||
|
try {
|
||||||
|
_glCreate(surf->w, surf->h);
|
||||||
|
try {
|
||||||
|
assert(surf->format->format == SDL_PIXELFORMAT_RGB24); // TODO: Proper support of many formats
|
||||||
|
if (SDL_MUSTLOCK(surf))
|
||||||
|
SDL_LockSurface(surf);
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surf->w, surf->h, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
checkGlError();
|
||||||
|
} catch(...) {
|
||||||
|
glDeleteTextures(1, &_texID);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(...) {
|
||||||
|
SDL_FreeSurface(surf);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
SDL_FreeSurface(surf);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Texture2D() {
|
||||||
|
glDeleteTextures(1, &_texID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind() {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _texID);
|
||||||
|
checkGlError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void _glCreate(unsigned width, unsigned height) {
|
||||||
|
glGenTextures(1, &_texID);
|
||||||
|
checkGlError();
|
||||||
|
try {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _texID);
|
||||||
|
unsigned logWidth = ilog2(width), logHeight = ilog2(height);
|
||||||
|
unsigned levels = std::max(logWidth,logHeight)+1u;
|
||||||
|
if(SDL_GL_ExtensionSupported("GL_ARB_texture_storage"))
|
||||||
|
glTexStorage2D(GL_TEXTURE_2D, levels, GL_RGB8, width, height);
|
||||||
|
else {
|
||||||
|
std::printf("Warning: extension GL_ARB_texture_storage not supported!\n");
|
||||||
|
for (unsigned i = 0u; i < levels; ++i)
|
||||||
|
{
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, i, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
width = std::max(1u, (width / 2u));
|
||||||
|
height = std::max(1u, (height / 2u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkGlError();
|
||||||
|
} catch(...) {
|
||||||
|
glDeleteTextures(1, &_texID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint _texID;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
BIN
textures/Wood_Box_Texture.jpg
Normal file
BIN
textures/Wood_Box_Texture.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
BIN
textures/Wood_Box_Texture_NRM.png
Normal file
BIN
textures/Wood_Box_Texture_NRM.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 306 KiB |
Reference in New Issue
Block a user