18 changed files with 38710 additions and 453 deletions
@ -1,6 +1,6 @@ |
|||||||
|
|
||||||
tags |
tags |
||||||
*.d |
*.d |
||||||
*.bin |
|
||||||
build/ |
build/ |
||||||
|
shader_testing |
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,35 @@ |
|||||||
|
|
||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
CXX = g++
|
CXX = g++
|
||||||
CXXFLAGS = -std=c++11 -g -ggdb3 -Wall -I/usr/include/SDL2
|
CXXFLAGS = -std=c++11 -g -ggdb3 -Wall -Isrc/ -I/usr/include/SDL2
|
||||||
LDFLAGS = -lSDL2 -lGLEW -lGL
|
LDFLAGS = -lSDL2 -lGLEW -lGL
|
||||||
|
OBJDIR = build
|
||||||
|
SRCDIR = src
|
||||||
BIN = shader_testing
|
BIN = shader_testing
|
||||||
|
|
||||||
all: |
|
||||||
$(CXX) $(CXXFLAGS) -MMD main.cpp -o $(BIN) $(LDFLAGS)
|
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
|
||||||
|
OBJECTS = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(SOURCES))
|
||||||
|
NDBG_SOURCES = $(wildcard $(SRCDIR)/*.cc)
|
||||||
|
NDBG_OBJS = $(patsubst $(SRCDIR)/%.cc, $(OBJDIR)/%.o, $(NDBG_SOURCES))
|
||||||
|
|
||||||
|
|
||||||
|
all: $(BIN) |
||||||
.PHONY: all |
.PHONY: all |
||||||
|
|
||||||
|
-include $(OBJDIR)/*.d |
||||||
|
|
||||||
|
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp |
||||||
|
$(CXX) $(CXXFLAGS) -c -MMD $< -o $@
|
||||||
|
|
||||||
|
$(NDBG_OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.cc |
||||||
|
$(CXX) -std=c++11 -Wall -Isrc/ -c $< -o $@
|
||||||
|
|
||||||
|
$(BIN): $(OBJECTS) $(NDBG_OBJS) |
||||||
|
$(CXX) -o $@ $(LDFLAGS) $^
|
||||||
|
|
||||||
clean: |
clean: |
||||||
rm -rf $(BIN)
|
rm -rf $(BIN)
|
||||||
|
rm -rf build/*
|
||||||
.PHONY: clean |
.PHONY: clean |
||||||
|
|
||||||
|
|||||||
@ -1,349 +0,0 @@ |
|||||||
|
|
||||||
#include <cassert> |
|
||||||
#include <cstdlib> |
|
||||||
#include <cstring> |
|
||||||
#include <string> |
|
||||||
#include <fstream> |
|
||||||
#include <iostream> |
|
||||||
|
|
||||||
#include <SDL2/SDL.h> |
|
||||||
#include <glm/geometric.hpp> |
|
||||||
#include <glm/gtc/matrix_transform.hpp> |
|
||||||
|
|
||||||
#include "shader_testing.h" |
|
||||||
|
|
||||||
#include "dumpShader.inl" |
|
||||||
|
|
||||||
|
|
||||||
SDL_Window *g_window = 0; |
|
||||||
SDL_GLContext g_glContext; |
|
||||||
SDL_DisplayMode g_display_mode; |
|
||||||
|
|
||||||
transforms g_xforms; |
|
||||||
mesh g_cube_mesh; |
|
||||||
const u32 NUM_CUBES = 4; |
|
||||||
gl_mesh g_gl_cubes[NUM_CUBES]; |
|
||||||
glm::vec3 g_cube_locs[NUM_CUBES] = { |
|
||||||
glm::vec3(-10, 10, 0), |
|
||||||
glm::vec3(-10, -10, 0), |
|
||||||
glm::vec3( 10, 10, 0), |
|
||||||
glm::vec3( 10, -10, 0), |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
void |
|
||||||
openglDebugCallback(GLenum source, |
|
||||||
GLenum type, |
|
||||||
GLuint id, |
|
||||||
GLenum severity, |
|
||||||
GLsizei length, |
|
||||||
const GLchar* message, |
|
||||||
const void* userParam) |
|
||||||
{ |
|
||||||
std::cout << ((type == GL_DEBUG_TYPE_ERROR) ? "Error" : "Debug") |
|
||||||
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "") |
|
||||||
<< ", type: " << type |
|
||||||
<< ", severity: " << severity |
|
||||||
<< ", message: " << message << "\n"; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
init() |
|
||||||
{ |
|
||||||
g_window = SDL_CreateWindow( |
|
||||||
"shader_testing", |
|
||||||
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
|
||||||
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
|
||||||
1280, |
|
||||||
720, |
|
||||||
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); |
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) { |
|
||||||
std::cout << "error, sdl init: " << SDL_GetError() << "\n"; |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
SDL_GL_SetSwapInterval(1); |
|
||||||
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_DOUBLEBUFFER, 1); |
|
||||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); |
|
||||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); |
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
|
||||||
SDL_GetCurrentDisplayMode(0, &g_display_mode); |
|
||||||
|
|
||||||
g_glContext = SDL_GL_CreateContext(g_window); |
|
||||||
|
|
||||||
if (!g_glContext) { |
|
||||||
std::cout << "error creating context\n"; |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (glewInit()) { |
|
||||||
std::cout << "error initializing opengl\n"; |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
std::cout << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; |
|
||||||
std::cout << "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; |
|
||||||
std::cout << "opengl version: " << glGetString(GL_VERSION) << "\n"; |
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST); |
|
||||||
glEnable(GL_LINE_SMOOTH); |
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
||||||
|
|
||||||
glEnable (GL_DEBUG_OUTPUT); |
|
||||||
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); |
|
||||||
|
|
||||||
return g_window != nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
const std::string |
|
||||||
dumpTextFile(const char* filepath) |
|
||||||
{ |
|
||||||
std::ifstream fs { filepath }; |
|
||||||
std::string s { |
|
||||||
std::istreambuf_iterator<char>(fs), |
|
||||||
std::istreambuf_iterator<char>()}; |
|
||||||
|
|
||||||
if (!fs || !fs.good()) |
|
||||||
std::cout << "error reading file, " << filepath << "\n"; |
|
||||||
|
|
||||||
return s; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
loadShaderProgram(const char* vs, const char* fs, shader* s) |
|
||||||
{ |
|
||||||
std::string vert = dumpTextFile(vs); |
|
||||||
std::string frag = dumpTextFile(fs); |
|
||||||
|
|
||||||
if (vert.size() > 0 && frag.size() > 0) { |
|
||||||
const char* vert_c = vert.c_str(); |
|
||||||
const char* frag_c = frag.c_str(); |
|
||||||
|
|
||||||
GLuint vs_id = glCreateShader(GL_VERTEX_SHADER); |
|
||||||
GLuint fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
|
||||||
glShaderSource(vs_id, 1, &vert_c, NULL); |
|
||||||
glShaderSource(fs_id, 1, &frag_c, NULL); |
|
||||||
glCompileShader(vs_id); |
|
||||||
glCompileShader(fs_id); |
|
||||||
s->prog_id = glCreateProgram(); |
|
||||||
glAttachShader(s->prog_id, vs_id); |
|
||||||
glAttachShader(s->prog_id, fs_id); |
|
||||||
|
|
||||||
glLinkProgram(s->prog_id); |
|
||||||
|
|
||||||
glDetachShader(s->prog_id, vs_id); |
|
||||||
glDetachShader(s->prog_id, fs_id); |
|
||||||
glDeleteShader(vs_id); |
|
||||||
glDeleteShader(fs_id); |
|
||||||
|
|
||||||
GLint is_linked = 0; |
|
||||||
glGetProgramiv(s->prog_id, GL_LINK_STATUS, &is_linked); |
|
||||||
return (is_linked == GL_TRUE); |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
transforms |
|
||||||
initXformUBO(shader* s, |
|
||||||
float fov, |
|
||||||
float near_clip_plane, |
|
||||||
float aspect_ratio, |
|
||||||
glm::vec3 cam_pos, |
|
||||||
glm::vec3 look_pos) |
|
||||||
{ |
|
||||||
transforms xforms; |
|
||||||
xforms.view_xform = glm::lookAt(cam_pos, look_pos, glm::vec3(0, 1, 0)); |
|
||||||
xforms.proj_xform = glm::infinitePerspective( |
|
||||||
glm::radians(fov), aspect_ratio, near_clip_plane); |
|
||||||
xforms.normal_xform = glm::mat4(1); |
|
||||||
|
|
||||||
glGenBuffers(1, &s->xforms_ubo_id); |
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, s->xforms_ubo_id); |
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, s->xforms_ubo_id); |
|
||||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(xforms), &xforms, GL_STATIC_DRAW); |
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, 0); |
|
||||||
|
|
||||||
return xforms; |
|
||||||
}; |
|
||||||
|
|
||||||
mesh |
|
||||||
initCubeMesh() |
|
||||||
{ |
|
||||||
mesh m = {0}; |
|
||||||
m.num_vertices = 8; |
|
||||||
m.vertices = (glm::vec3*) std::calloc(m.num_vertices, sizeof(glm::vec3)); |
|
||||||
m.vertices[0] = { -1, 1, -1 }; |
|
||||||
m.vertices[1] = { -1, -1, -1 }; |
|
||||||
m.vertices[2] = { 1, -1, -1 }; |
|
||||||
m.vertices[3] = { 1, 1, -1 }; |
|
||||||
m.vertices[4] = { -1, 1, 1 }; |
|
||||||
m.vertices[5] = { -1, -1, 1 }; |
|
||||||
m.vertices[6] = { 1, -1, 1 }; |
|
||||||
m.vertices[7] = { 1, 1, 1 }; |
|
||||||
|
|
||||||
m.num_indices = 36; // 6 sides, 2 tris per side, 3 verts per tri
|
|
||||||
m.indices = (u32*) std::calloc(m.num_indices, sizeof(u32)); |
|
||||||
u32 indices[36] = { |
|
||||||
0, 1, 2, 0, 2, 3, |
|
||||||
3, 2, 6, 3, 6, 7, |
|
||||||
7, 6, 5, 7, 5, 4, |
|
||||||
4, 5, 0, 4, 1, 0, |
|
||||||
0, 3, 4, 0, 3, 7, |
|
||||||
1, 2, 5, 2, 6, 5 |
|
||||||
}; |
|
||||||
std::memcpy(m.indices, indices, m.num_indices * sizeof(u32)); |
|
||||||
|
|
||||||
return m; |
|
||||||
} |
|
||||||
|
|
||||||
gl_mesh |
|
||||||
loadGLMesh(shader* s, const mesh& m, GLenum draw_mode, const glm::vec3& pos) |
|
||||||
{ |
|
||||||
gl_mesh gm = {0}; |
|
||||||
gm.num_indices = m.num_indices; |
|
||||||
gm.draw_mode = draw_mode; |
|
||||||
|
|
||||||
glUseProgram(s->prog_id); |
|
||||||
glGenVertexArrays(1, &gm.vao_id); |
|
||||||
glBindVertexArray(gm.vao_id); |
|
||||||
|
|
||||||
gm.model_xform = (glm::mat4*) std::calloc(1, sizeof(glm::mat4)); |
|
||||||
*gm.model_xform = glm::translate(glm::mat4(1), pos); |
|
||||||
|
|
||||||
gm.model_xform_id = glGetUniformLocation(s->prog_id, "model_xform"); |
|
||||||
glUniformMatrix4fv( |
|
||||||
gm.model_xform_id, 1, GL_FALSE, (float*) gm.model_xform); |
|
||||||
|
|
||||||
glGenBuffers(1, &gm.vert_buf_id); |
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, gm.vert_buf_id); |
|
||||||
glBufferData(GL_ARRAY_BUFFER, |
|
||||||
m.num_vertices * 3 * sizeof(GLfloat), |
|
||||||
m.vertices, |
|
||||||
GL_STATIC_DRAW); |
|
||||||
glVertexAttribPointer(POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0); |
|
||||||
glEnableVertexAttribArray(POSITION); |
|
||||||
|
|
||||||
glGenBuffers(1, &gm.idx_buf_id); |
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gm.idx_buf_id); |
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, |
|
||||||
m.num_indices * sizeof(GLuint), |
|
||||||
m.indices, |
|
||||||
GL_STATIC_DRAW); |
|
||||||
|
|
||||||
glBindVertexArray(0); |
|
||||||
glUseProgram(0); |
|
||||||
|
|
||||||
return gm; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
loop(shader* s, gl_mesh* mesh_arr, u32 num_meshes) |
|
||||||
{ |
|
||||||
u32 delay = 60; |
|
||||||
u32 frame_start, frame_time; |
|
||||||
bool running = true; |
|
||||||
SDL_Event e; |
|
||||||
|
|
||||||
while (running) { |
|
||||||
frame_start = SDL_GetTicks(); |
|
||||||
|
|
||||||
while (SDL_PollEvent(&e)) { |
|
||||||
if (e.type == SDL_QUIT || |
|
||||||
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) |
|
||||||
{ |
|
||||||
running = false; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
glClearColor(0.2, 0.2, 0.2, 1); |
|
||||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
|
||||||
glUseProgram(s->prog_id); |
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, s->xforms_ubo_id); |
|
||||||
|
|
||||||
for (u32 i = 0; i < num_meshes; i++) { |
|
||||||
const gl_mesh& m = mesh_arr[i]; |
|
||||||
*m.model_xform = glm::rotate( |
|
||||||
*m.model_xform, (float) M_PI / 60, glm::vec3(0, 1, 0)); |
|
||||||
glBindVertexArray(m.vao_id); |
|
||||||
glUniformMatrix4fv( |
|
||||||
m.model_xform_id, 1, GL_FALSE, (float*) m.model_xform); |
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.idx_buf_id); |
|
||||||
glDrawElements(m.draw_mode, m.num_indices, GL_UNSIGNED_INT, 0); |
|
||||||
glBindVertexArray(0); |
|
||||||
} |
|
||||||
|
|
||||||
SDL_GL_SwapWindow(g_window); |
|
||||||
glUseProgram(0); |
|
||||||
frame_time = SDL_GetTicks() - frame_start; |
|
||||||
|
|
||||||
if (delay > frame_time) |
|
||||||
SDL_Delay(delay - frame_time); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
quit(shader* s) |
|
||||||
{ |
|
||||||
glDeleteProgram(s->prog_id); |
|
||||||
SDL_GL_DeleteContext(g_glContext); |
|
||||||
SDL_DestroyWindow(g_window); |
|
||||||
SDL_Quit(); |
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_CUBES; i++) { |
|
||||||
gl_mesh& c = g_gl_cubes[i]; |
|
||||||
|
|
||||||
if (c.model_xform != nullptr) |
|
||||||
free(c.model_xform); |
|
||||||
} |
|
||||||
|
|
||||||
mesh& cm = g_cube_mesh; |
|
||||||
|
|
||||||
if (cm.vertices != nullptr) |
|
||||||
free(cm.vertices); |
|
||||||
if (cm.normals != nullptr) |
|
||||||
free(cm.normals); |
|
||||||
if (cm.uvs != nullptr) |
|
||||||
free(cm.uvs); |
|
||||||
if (cm.colors != nullptr) |
|
||||||
free(cm.colors); |
|
||||||
if (cm.indices != nullptr) |
|
||||||
free(cm.indices); |
|
||||||
} |
|
||||||
|
|
||||||
int |
|
||||||
main() |
|
||||||
{ |
|
||||||
if (!init()) |
|
||||||
return 1; |
|
||||||
|
|
||||||
shader s = {0}; |
|
||||||
|
|
||||||
if (loadShaderProgram("data/shader.vert", "data/shader.frag", &s)) { |
|
||||||
dumpShader(s.prog_id); |
|
||||||
g_xforms = initXformUBO(&s); |
|
||||||
g_cube_mesh = initCubeMesh(); |
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_CUBES; i++) { |
|
||||||
g_gl_cubes[i] = |
|
||||||
loadGLMesh(&s, g_cube_mesh, GL_TRIANGLES, g_cube_locs[i]); |
|
||||||
assert(g_gl_cubes[i].vao_id != 0); |
|
||||||
} |
|
||||||
|
|
||||||
loop(&s, g_gl_cubes, NUM_CUBES); |
|
||||||
quit(&s); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
std::cout << "error loading shader program\n"; |
|
||||||
quit(&s); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
Binary file not shown.
@ -1,96 +0,0 @@ |
|||||||
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include <GL/glew.h> |
|
||||||
#include <glm/glm.hpp> |
|
||||||
|
|
||||||
|
|
||||||
typedef uint32_t u32; |
|
||||||
|
|
||||||
|
|
||||||
struct gl_uniform |
|
||||||
{ |
|
||||||
GLenum id; |
|
||||||
GLenum data_type; |
|
||||||
}; |
|
||||||
|
|
||||||
struct gl_buffer |
|
||||||
{ |
|
||||||
GLuint id; |
|
||||||
GLenum data_type; |
|
||||||
}; |
|
||||||
|
|
||||||
struct shader |
|
||||||
{ |
|
||||||
GLuint prog_id; |
|
||||||
GLuint xforms_ubo_id; |
|
||||||
}; |
|
||||||
|
|
||||||
struct transforms |
|
||||||
{ |
|
||||||
glm::mat4 view_xform; |
|
||||||
glm::mat4 proj_xform; |
|
||||||
glm::mat4 normal_xform; |
|
||||||
}; |
|
||||||
|
|
||||||
enum glsl_layout |
|
||||||
{ |
|
||||||
POSITION, |
|
||||||
NORMAL, |
|
||||||
UV, |
|
||||||
COLOR |
|
||||||
}; |
|
||||||
|
|
||||||
struct mesh |
|
||||||
{ |
|
||||||
u32 num_vertices; |
|
||||||
u32 num_indices; |
|
||||||
glm::vec3* vertices; |
|
||||||
glm::vec3* normals; |
|
||||||
glm::vec3* uvs; |
|
||||||
glm::vec3* colors; |
|
||||||
u32* indices; |
|
||||||
}; |
|
||||||
|
|
||||||
struct gl_mesh |
|
||||||
{ |
|
||||||
u32 num_indices; |
|
||||||
GLuint vao_id; |
|
||||||
GLuint tex_id; |
|
||||||
GLenum draw_mode; |
|
||||||
|
|
||||||
glm::mat4* model_xform; |
|
||||||
GLuint model_xform_id; |
|
||||||
|
|
||||||
GLuint vert_buf_id; |
|
||||||
GLuint norm_buf_id; |
|
||||||
GLuint uv_buf_id; |
|
||||||
GLuint clr_buf_id; |
|
||||||
GLuint idx_buf_id; |
|
||||||
}; |
|
||||||
|
|
||||||
struct node; |
|
||||||
struct entity; |
|
||||||
struct light; |
|
||||||
struct animation; |
|
||||||
|
|
||||||
|
|
||||||
const float DEFAULT_FOV = 60.f; |
|
||||||
const float NEAR_CLIP_PLANE = 5.f; |
|
||||||
const float DEFAULT_ASPECT_RATIO = 16.f / 9.f; |
|
||||||
const glm::vec3 DEFAULT_CAM_POS = { 0, 0, -30.f }; |
|
||||||
const glm::vec3 DEFAULT_LOOK_POS = { 0, 0, 0 }; |
|
||||||
|
|
||||||
|
|
||||||
bool loadShaderProgram(const char* vs, const char* fs, shader* s); |
|
||||||
transforms initXformUBO(shader* s, |
|
||||||
float fov = DEFAULT_FOV, |
|
||||||
float near_clip_plane = NEAR_CLIP_PLANE, |
|
||||||
float aspect_ratio = DEFAULT_ASPECT_RATIO, |
|
||||||
glm::vec3 cam_pos = DEFAULT_CAM_POS, |
|
||||||
glm::vec3 look_pos = DEFAULT_LOOK_POS); |
|
||||||
mesh initCubeMesh(); |
|
||||||
gl_mesh loadGLMesh(shader* s, const mesh& m, const glm::vec3& pos); |
|
||||||
void loop(shader* s, gl_mesh* mesh_arr, u32 num_meshes); |
|
||||||
void quit(shader* s); |
|
||||||
|
|
||||||
@ -0,0 +1,442 @@ |
|||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <cstring> |
||||||
|
|
||||||
|
#include <glm/ext/matrix_transform.hpp> |
||||||
|
#include "tiny_gltf.h" |
||||||
|
|
||||||
|
#include "asset.h" |
||||||
|
#include "dumbLog.h" |
||||||
|
|
||||||
|
|
||||||
|
//-----------------
|
||||||
|
// Hashing
|
||||||
|
|
||||||
|
uint64_t |
||||||
|
utilFNV64a_str(const char* str, uint64_t hval) |
||||||
|
{ |
||||||
|
unsigned char* s = (unsigned char *)str; // unsigned string
|
||||||
|
|
||||||
|
// FNV-1a hash each octet of the string
|
||||||
|
while (*s) { |
||||||
|
// xor the bottom with the current octet
|
||||||
|
hval ^= (uint64_t)*s++; |
||||||
|
// multiply by the 64 bit FNV magic prime mod 2^64
|
||||||
|
hval *= FNV_64_PRIME; |
||||||
|
} |
||||||
|
|
||||||
|
return hval; |
||||||
|
} |
||||||
|
|
||||||
|
//-----------------
|
||||||
|
// Memory allocation
|
||||||
|
|
||||||
|
memory_arena* |
||||||
|
arenaInit(size_t initial_size) |
||||||
|
{ |
||||||
|
uint sz = sizeof(memory_arena); |
||||||
|
memory_arena* arena = |
||||||
|
(memory_arena*) std::calloc(initial_size + sz, sizeof(u8)); |
||||||
|
arena->head = arena->next_free = (uint8_t*) arena + sz; |
||||||
|
arena->max_size = initial_size; |
||||||
|
return arena; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
arenaFree(memory_arena*& arena) |
||||||
|
{ |
||||||
|
if (arena != nullptr) { |
||||||
|
std::free(arena); |
||||||
|
arena = nullptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint |
||||||
|
arenaGetFreeSize(memory_arena* arena) |
||||||
|
{ |
||||||
|
return (uint8_t*) arena->head |
||||||
|
+ arena->max_size |
||||||
|
- (uint8_t*) arena->next_free; |
||||||
|
} |
||||||
|
|
||||||
|
void* |
||||||
|
arenaAllocateBlock(memory_arena* arena, size_t block_size) |
||||||
|
{ |
||||||
|
// TODO: resizable memory arena
|
||||||
|
assert(arenaGetFreeSize(arena) >= block_size); |
||||||
|
void* ret = arena->next_free; |
||||||
|
arena->next_free = (uint8_t*) arena->next_free + block_size; |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
|
void dumpNodes(tinygltf::Model t_mdl); |
||||||
|
model* initModel(model_assets* assets, |
||||||
|
memory_arena* arena, |
||||||
|
tinygltf::Model t_mdl, |
||||||
|
const char* filename); |
||||||
|
bool parseMeshNode(mesh* m, |
||||||
|
texture_assets* textures, |
||||||
|
memory_arena* arena, |
||||||
|
const tinygltf::Node& node, |
||||||
|
const tinygltf::Model& t_mdl); |
||||||
|
|
||||||
|
|
||||||
|
// interface
|
||||||
|
|
||||||
|
model_assets* |
||||||
|
assetInitModelBlock(memory_arena* arena, uint asset_count) |
||||||
|
{ |
||||||
|
model_assets* assets = |
||||||
|
(model_assets*) arenaAllocateBlock(arena, sizeof(model_assets)); |
||||||
|
assets->models = |
||||||
|
(model*) arenaAllocateBlock(arena, asset_count * sizeof(model)); |
||||||
|
assets->max = asset_count; |
||||||
|
|
||||||
|
return assets; |
||||||
|
} |
||||||
|
|
||||||
|
texture_assets* |
||||||
|
assetInitTextureBlock(memory_arena* arena, uint asset_count) |
||||||
|
{ |
||||||
|
texture_assets* assets = |
||||||
|
(texture_assets*) arenaAllocateBlock(arena, sizeof(texture_assets)); |
||||||
|
assets->images = (util_image*) arenaAllocateBlock( |
||||||
|
arena, asset_count * sizeof(util_image)); |
||||||
|
assets->max = asset_count; |
||||||
|
|
||||||
|
return assets; |
||||||
|
} |
||||||
|
|
||||||
|
// FIXME: move to internal when finished
|
||||||
|
util_image* |
||||||
|
copyDiffuseTexture(texture_assets* textures, |
||||||
|
memory_arena* arena, |
||||||
|
const tinygltf::Model& t_mdl) |
||||||
|
{ |
||||||
|
// NOTE: assuming material[0] since we're using pallete texture
|
||||||
|
assert(t_mdl.materials.size() == 1 |
||||||
|
&& t_mdl.textures.size() == 1 |
||||||
|
&& t_mdl.images.size() == 1 |
||||||
|
&& t_mdl.images[0].image.size() > 0); |
||||||
|
tinygltf::Image t_img = t_mdl.images[0]; |
||||||
|
|
||||||
|
// TODO: re-alloc array when out of space
|
||||||
|
assert(textures->count < textures->max && arena != nullptr); |
||||||
|
util_image* dtex = &textures->images[textures->count]; |
||||||
|
textures->count++; |
||||||
|
|
||||||
|
dtex->w = t_img.width; |
||||||
|
dtex->h = t_img.height; |
||||||
|
dtex->bits_per_channel = t_img.bits; |
||||||
|
dtex->num_channels = t_img.component; |
||||||
|
dtex->data_len = t_img.image.size(); |
||||||
|
dtex->pixels = (u8*) arenaAllocateBlock(arena, dtex->data_len); |
||||||
|
// FIXME: overwriting memory here... also fix in libTangerine
|
||||||
|
std::strncpy(dtex->file_path, t_img.uri.c_str(), t_img.uri.size()); |
||||||
|
dtex->filepath_hash = utilFNV64a_str(t_img.uri.c_str()); |
||||||
|
std::memcpy(dtex->pixels, t_img.image.data(), dtex->data_len); |
||||||
|
|
||||||
|
return dtex; |
||||||
|
} |
||||||
|
|
||||||
|
model* |
||||||
|
assetLoadFromFile(model_assets* assets, |
||||||
|
texture_assets* textures, |
||||||
|
memory_arena* arena, |
||||||
|
const char* filename) |
||||||
|
{ |
||||||
|
tinygltf::Model t_mdl; |
||||||
|
tinygltf::TinyGLTF gltf_ctx; |
||||||
|
std::string err; |
||||||
|
std::string warn; |
||||||
|
|
||||||
|
LOG(Info) << "Loading model: " << filename << "\n"; |
||||||
|
|
||||||
|
if (!gltf_ctx.LoadASCIIFromFile(&t_mdl, &err, &warn, filename)) { |
||||||
|
LOG(Error) << "Error loading file: " << filename |
||||||
|
<< " , msg: " << err << "\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
#if 0 |
||||||
|
dumpNodes(t_mdl); |
||||||
|
#endif |
||||||
|
// NOTE: assume we're working with a single buffer
|
||||||
|
assert(t_mdl.buffers.size() == 1); |
||||||
|
model* mdl = initModel(assets, arena, t_mdl, filename); |
||||||
|
#if 0 |
||||||
|
// FIXME: probably a bug here with overwriting arena memory
|
||||||
|
mdl->diffuse_texture = copyDiffuseTexture(textures, arena, t_mdl); |
||||||
|
if (mdl->diffuse_texture == nullptr) { |
||||||
|
LOG(Error) << "Error Loading diffuse texture\n"; |
||||||
|
// TODO: reclaim arena memory
|
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
uint mesh_idx = 0; |
||||||
|
|
||||||
|
for (tinygltf::Node node : t_mdl.nodes) { |
||||||
|
if (node.mesh >= 0) { |
||||||
|
if (!parseMeshNode(&mdl->meshes[mesh_idx++], |
||||||
|
textures, arena, node, t_mdl)) |
||||||
|
{ |
||||||
|
LOG(Error) << "Error parsing node\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return mdl; |
||||||
|
} |
||||||
|
|
||||||
|
model* |
||||||
|
assetGetCached(model_assets* assets, uint64_t path_hash) |
||||||
|
{ |
||||||
|
for (uint i = 0; i < assets->count; i++) { |
||||||
|
if (assets->models[i].filepath_hash == path_hash) |
||||||
|
return &assets->models[i]; |
||||||
|
} |
||||||
|
|
||||||
|
LOG(Debug) << "asset not cached: " << path_hash << "\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// internal
|
||||||
|
|
||||||
|
model* |
||||||
|
initModel(model_assets* assets, |
||||||
|
memory_arena* arena, |
||||||
|
tinygltf::Model t_mdl, |
||||||
|
const char* filename) |
||||||
|
{ |
||||||
|
// TODO: re-alloc array when out of space
|
||||||
|
assert(assets->count < assets->max && arena != nullptr); |
||||||
|
model* mdl = &assets->models[assets->count]; |
||||||
|
assets->count++; |
||||||
|
|
||||||
|
uint buf_count = t_mdl.bufferViews.size(); |
||||||
|
mdl->meshes = (mesh*) arenaAllocateBlock(arena, buf_count * sizeof(mesh)); |
||||||
|
mdl->num_meshes = t_mdl.meshes.size(); |
||||||
|
uint name_len = std::strlen(filename); |
||||||
|
assert(name_len < MAX_PATH_SIZE); |
||||||
|
mdl->filepath = (char*) arenaAllocateBlock(arena, name_len + 1); |
||||||
|
std::strncpy(mdl->filepath, filename, name_len); |
||||||
|
mdl->filepath_hash = utilFNV64a_str(mdl->filepath); |
||||||
|
|
||||||
|
return mdl; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
copyBuffer(uint8_t*& buffer_ref, |
||||||
|
memory_arena* arena, |
||||||
|
int acc_idx, |
||||||
|
const tinygltf::Model& t_mdl) |
||||||
|
{ |
||||||
|
const tinygltf::Accessor& acc = t_mdl.accessors[acc_idx]; |
||||||
|
const tinygltf::BufferView& bv = t_mdl.bufferViews[acc.bufferView]; |
||||||
|
const tinygltf::Buffer& t_buf = t_mdl.buffers[bv.buffer]; |
||||||
|
|
||||||
|
// TODO: clean up validation
|
||||||
|
if (bv.target == TINYGLTF_TARGET_ARRAY_BUFFER) { |
||||||
|
assert(acc.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT |
||||||
|
&& (acc.type == TINYGLTF_TYPE_VEC3 |
||||||
|
|| acc.type == TINYGLTF_TYPE_VEC2)); |
||||||
|
} else if (bv.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) { |
||||||
|
assert(acc.type == TINYGLTF_TYPE_SCALAR |
||||||
|
&& acc.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT); |
||||||
|
} else { |
||||||
|
LOG(Error) << "unknown target\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
assert(bv.byteStride == 0); |
||||||
|
buffer_ref = (uint8_t*) arenaAllocateBlock(arena, bv.byteLength); |
||||||
|
std::memcpy(buffer_ref, &t_buf.data[bv.byteOffset], bv.byteLength); |
||||||
|
|
||||||
|
return buffer_ref != nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
// FIXME: need to implement tree structure for blender models to work properly
|
||||||
|
glm::mat4* |
||||||
|
parseNodeTransform(memory_arena* arena, const tinygltf::Node* node) |
||||||
|
{ |
||||||
|
if (node->rotation.size() == 4 |
||||||
|
&& node->scale.size() == 3 |
||||||
|
&& node->translation.size() == 3) |
||||||
|
{ |
||||||
|
glm::mat4* xform = |
||||||
|
(glm::mat4*) arenaAllocateBlock(arena, sizeof(glm::mat4)); |
||||||
|
*xform = glm::mat4(1.0f); |
||||||
|
*xform = glm::rotate(*xform, (float) node->rotation[3], |
||||||
|
glm::vec3((float) node->rotation[0], |
||||||
|
(float) node->rotation[1], |
||||||
|
(float) node->rotation[2]) |
||||||
|
); |
||||||
|
*xform = glm::scale(*xform, |
||||||
|
glm::vec3((float) node->scale[0], |
||||||
|
(float) node->scale[0], |
||||||
|
(float) node->scale[0]) |
||||||
|
); |
||||||
|
*xform = glm::translate(*xform, |
||||||
|
glm::vec3((float) node->translation[0], |
||||||
|
(float) node->translation[0], |
||||||
|
(float) node->translation[0]) |
||||||
|
); |
||||||
|
|
||||||
|
return xform; |
||||||
|
} |
||||||
|
|
||||||
|
LOG(Error) << "Unknown transform\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
parseMeshNode(mesh* m, |
||||||
|
texture_assets* textures, |
||||||
|
memory_arena* arena, |
||||||
|
const tinygltf::Node& node, |
||||||
|
const tinygltf::Model& t_mdl) |
||||||
|
{ |
||||||
|
tinygltf::Mesh t_mesh = t_mdl.meshes[node.mesh]; |
||||||
|
// NOTE: assume only 1 primitive object per mesh
|
||||||
|
assert(t_mdl.meshes[node.mesh].primitives.size() == 1); |
||||||
|
tinygltf::Primitive prim = t_mesh.primitives[0]; |
||||||
|
|
||||||
|
// NOTE: verify assumptions about input
|
||||||
|
assert(prim.attributes.find("POSITION") != prim.attributes.end() |
||||||
|
&& prim.attributes.find("NORMAL") != prim.attributes.end() |
||||||
|
&& prim.attributes.find("TEXCOORD_0") != prim.attributes.end() |
||||||
|
&& prim.indices >= 0); |
||||||
|
|
||||||
|
const tinygltf::Accessor& vert_acc = |
||||||
|
t_mdl.accessors[prim.attributes["POSITION"]]; |
||||||
|
const tinygltf::Accessor& index_acc = t_mdl.accessors[prim.indices]; |
||||||
|
m->num_vertices = vert_acc.count; |
||||||
|
m->num_indices = index_acc.count; |
||||||
|
m->draw_mode = prim.mode; |
||||||
|
m->usage = GL_STATIC_DRAW; // TODO: logic for updating dynamic meshes
|
||||||
|
// FIXME: the node transforms from blender only work as part of a node tree
|
||||||
|
#if 1 |
||||||
|
m->xform = (glm::mat4*) arenaAllocateBlock(arena, sizeof(glm::mat4)); |
||||||
|
*m->xform = glm::mat4(1.0f); |
||||||
|
#else |
||||||
|
m->xform = parseNodeTransform(arena, &node); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (m->xform != nullptr |
||||||
|
&& copyBuffer((uint8_t*&) m->vertices, arena, |
||||||
|
prim.attributes["POSITION"], t_mdl) |
||||||
|
&& copyBuffer((uint8_t*&) m->normals, arena, |
||||||
|
prim.attributes["NORMAL"], t_mdl) |
||||||
|
&& copyBuffer((uint8_t*&) m->uvs, arena, |
||||||
|
prim.attributes["TEXCOORD_0"], t_mdl) |
||||||
|
&& copyBuffer((uint8_t*&) m->indices, arena, |
||||||
|
prim.indices, t_mdl)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
const char* |
||||||
|
getTargetStr(int target) |
||||||
|
{ |
||||||
|
switch (target) { |
||||||
|
case 34962: return "GL_ARRAY_BUFFER"; |
||||||
|
case 34963: return "GL_ELEMENT_ARRAY_BUFFER"; |
||||||
|
default: return "UNKNOWN"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const char* |
||||||
|
getElementType(int type) |
||||||
|
{ |
||||||
|
switch (type) { |
||||||
|
case 2: return "TINYGLTF_TYPE_VEC2"; |
||||||
|
case 3: return "TINYGLTF_TYPE_VEC3"; |
||||||
|
case 4: return "TINYGLTF_TYPE_VEC4"; |
||||||
|
case 65: return "TINYGLTF_TYPE_SCALAR"; |
||||||
|
default: return "UNKNOWN"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const char* |
||||||
|
getComponentType(int componentType) |
||||||
|
{ |
||||||
|
switch (componentType) { |
||||||
|
case 5123: return "TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT"; |
||||||
|
case 5124: return "TINYGLTF_COMPONENT_TYPE_INT"; |
||||||
|
case 5125: return "TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT"; |
||||||
|
case 5126: return "TINYGLTF_COMPONENT_TYPE_FLOAT"; |
||||||
|
case 5130: return "TINYGLTF_COMPONENT_TYPE_DOUBLE"; |
||||||
|
default: return "UNKOWN"; } |
||||||
|
} |
||||||
|
|
||||||
|
const char* |
||||||
|
getDrawMode(int drawMode) |
||||||
|
{ |
||||||
|
switch (drawMode) { |
||||||
|
case 0: return "TINYGLTF_MODE_POINTS"; |
||||||
|
case 1: return "TINYGLTF_MODE_LINE"; |
||||||
|
case 2: return "TINYGLTF_MODE_LINE_LOOP"; |
||||||
|
case 3: return "TINYGLTF_MODE_LINE_STRIP"; |
||||||
|
case 4: return "TINYGLTF_MODE_TRIANGLES"; |
||||||
|
case 5: return "TINYGLTF_MODE_TRIANGLE_STRIP"; |
||||||
|
case 6: return "TINYGLTF_MODE_TRIANGLE_FAN"; |
||||||
|
default: return "UNKOWN MODE"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
dumpBuffer(tinygltf::Model model, tinygltf::Accessor acc) |
||||||
|
{ |
||||||
|
size_t bv_idx = acc.bufferView; |
||||||
|
assert(bv_idx >= 0 && bv_idx < model.bufferViews.size()); |
||||||
|
tinygltf::BufferView bv = model.bufferViews[bv_idx]; |
||||||
|
|
||||||
|
LOG(Debug) << "-----------------------\n"; |
||||||
|
LOG(Debug) << "buf idx: " << bv_idx << "\n"; |
||||||
|
LOG(Debug) << "buf target: " << getTargetStr(bv.target) << "\n"; |
||||||
|
LOG(Debug) << "buf len: " << bv.byteLength << "\n"; |
||||||
|
LOG(Debug) << "buf offset: " << bv.byteOffset << "\n"; |
||||||
|
LOG(Debug) << "buf stride: " << bv.byteStride << "\n"; |
||||||
|
LOG(Debug) << "acc component type: " |
||||||
|
<< getComponentType(acc.componentType) << "\n"; |
||||||
|
LOG(Debug) << "acc element type: " |
||||||
|
<< getElementType(acc.type) << "\n"; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
dumpNodes(tinygltf::Model t_mdl) |
||||||
|
{ |
||||||
|
for (tinygltf::Node node : t_mdl.nodes) { |
||||||
|
LOG(Debug) << "##################\n"; |
||||||
|
LOG(Debug) << "node name: " << node.name << "\n"; |
||||||
|
LOG(Debug) << "node mesh idx: " << node.mesh << "\n"; |
||||||
|
|
||||||
|
if (node.mesh >= 0) { |
||||||
|
tinygltf::Mesh t_mesh = t_mdl.meshes[node.mesh]; |
||||||
|
LOG(Debug) << "node mesh name: " << t_mesh.name << "\n"; |
||||||
|
// NOTE: assume only 1 primitive object per mesh
|
||||||
|
assert(t_mdl.meshes[node.mesh].primitives.size() == 1); |
||||||
|
tinygltf::Primitive prim = t_mesh.primitives[0]; |
||||||
|
LOG(Debug) << "node draw mode: " << getDrawMode(prim.mode) << "\n"; |
||||||
|
|
||||||
|
for (auto& att : prim.attributes) { |
||||||
|
LOG(Debug) << "dumping buffer: " << att.first << "\n"; |
||||||
|
dumpBuffer(t_mdl, t_mdl.accessors[att.second]); |
||||||
|
} |
||||||
|
|
||||||
|
LOG(Debug) << "dumping index buffer\n"; |
||||||
|
dumpBuffer(t_mdl, t_mdl.accessors[prim.indices]); |
||||||
|
} else { |
||||||
|
LOG(Debug) << "Not a mesh node\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,113 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <GL/glew.h> |
||||||
|
#include <glm/glm.hpp> |
||||||
|
|
||||||
|
|
||||||
|
typedef uint8_t u8; |
||||||
|
typedef int32_t i32; |
||||||
|
typedef uint64_t u64; |
||||||
|
|
||||||
|
//-----------------
|
||||||
|
// Hashing
|
||||||
|
|
||||||
|
// NOTE: FNV1a hashing algorithm http://www.isthe.com/chongo/tech/comp/fnv/
|
||||||
|
#define FNV1_64_INIT ((u64) 0xcbf29ce484222325ULL) |
||||||
|
#define FNV_64_PRIME ((u64) 0x100000001b3ULL) |
||||||
|
u64 |
||||||
|
utilFNV64a_str(const char *str, u64 hval = FNV1_64_INIT); |
||||||
|
|
||||||
|
//-----------------
|
||||||
|
// Memory allocation
|
||||||
|
|
||||||
|
struct memory_arena |
||||||
|
{ |
||||||
|
size_t max_size; |
||||||
|
void* head; |
||||||
|
void* next_free; |
||||||
|
}; |
||||||
|
|
||||||
|
#define DEFAULT_ARENA_SIZE 10 * 1024 * 1024 // 10MB
|
||||||
|
memory_arena* arenaInit(size_t initial_size = DEFAULT_ARENA_SIZE); |
||||||
|
|
||||||
|
void arenaFree(memory_arena*& arena); |
||||||
|
|
||||||
|
uint arenaGetFreeSize(memory_arena* arena); |
||||||
|
|
||||||
|
void* arenaAllocateBlock(memory_arena* arena, size_t block_size); |
||||||
|
|
||||||
|
// NOTE: wrapper for stb_image
|
||||||
|
struct util_image |
||||||
|
{ |
||||||
|
i32 w; |
||||||
|
i32 h; |
||||||
|
i32 bits_per_channel; |
||||||
|
i32 num_channels; |
||||||
|
uint data_len; |
||||||
|
u8* pixels; |
||||||
|
u64 filepath_hash; |
||||||
|
// FIXME: should use a pointer here, and just add the length of file_path
|
||||||
|
// onto the allocation for util_image
|
||||||
|
char file_path[256]; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// NOTE: wrapper for tinygltf https://github.com/syoyo/tinygltf
|
||||||
|
// https://github.com/KhronosGroup/glTF
|
||||||
|
|
||||||
|
struct mesh |
||||||
|
{ |
||||||
|
GLenum draw_mode; // NOTE: GL_LINES, GL_TRIANGLES
|
||||||
|
GLenum usage; // NOTE: GL_STATIC_DRAW, GL_DYNAMIC_DRAW
|
||||||
|
uint num_vertices; |
||||||
|
uint num_indices; |
||||||
|
glm::vec3* vertices; |
||||||
|
glm::vec3* normals; |
||||||
|
glm::vec2* uvs; |
||||||
|
glm::vec3* colors; |
||||||
|
uint* indices; |
||||||
|
glm::mat4* xform; |
||||||
|
}; |
||||||
|
|
||||||
|
#define MAX_PATH_SIZE 256 |
||||||
|
struct model |
||||||
|
{ |
||||||
|
char* filepath; |
||||||
|
u64 filepath_hash; |
||||||
|
uint num_meshes; |
||||||
|
mesh* meshes; |
||||||
|
util_image* diffuse_texture; |
||||||
|
}; |
||||||
|
|
||||||
|
struct model_assets |
||||||
|
{ |
||||||
|
model* models; |
||||||
|
uint count; |
||||||
|
uint max; |
||||||
|
}; |
||||||
|
|
||||||
|
struct texture_assets |
||||||
|
{ |
||||||
|
util_image* images; |
||||||
|
uint count; |
||||||
|
uint max; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// TODO: would be nice to make these init functions generic
|
||||||
|
model_assets* |
||||||
|
assetInitModelBlock(memory_arena* arena, uint asset_count); |
||||||
|
|
||||||
|
texture_assets* |
||||||
|
assetInitTextureBlock(memory_arena* arena, uint asset_count); |
||||||
|
|
||||||
|
model* |
||||||
|
assetLoadFromFile(model_assets* assets, |
||||||
|
texture_assets* textures, |
||||||
|
memory_arena* arena, |
||||||
|
const char* filename); |
||||||
|
|
||||||
|
model* |
||||||
|
assetGetCached(model_assets* assets, u64 path_hash); |
||||||
|
|
||||||
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#include <ctime> |
||||||
|
#include <chrono> |
||||||
|
|
||||||
|
#include "dumbLog.h" |
||||||
|
|
||||||
|
void dumbLog::setOutputStream(std::ostream* out) |
||||||
|
{ |
||||||
|
this->OUT = out; |
||||||
|
} |
||||||
|
|
||||||
|
const char* |
||||||
|
dumbLog::logLevelToString(log_level level) |
||||||
|
{ |
||||||
|
switch (level) { |
||||||
|
case log_level::Error: return "Error"; |
||||||
|
case log_level::Warning: return "Warning"; |
||||||
|
case log_level::Info: return "Info"; |
||||||
|
default: return "Potato"; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
std::tm* |
||||||
|
dumbLog::getCurrentTime() |
||||||
|
{ |
||||||
|
auto now = std::chrono::system_clock::now(); |
||||||
|
auto t_c = std::chrono::system_clock::to_time_t(now); |
||||||
|
return std::localtime(&t_c); |
||||||
|
} |
||||||
|
|
||||||
|
int |
||||||
|
dumbLog::getCurrentMS() |
||||||
|
{ |
||||||
|
auto now = std::chrono::system_clock::now(); |
||||||
|
long long total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count(); |
||||||
|
return int(total_ms % 1000); |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,30 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <iomanip> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
|
||||||
|
enum log_level { |
||||||
|
Error, |
||||||
|
Warning, |
||||||
|
Info, |
||||||
|
Debug |
||||||
|
}; |
||||||
|
|
||||||
|
struct dumbLog |
||||||
|
{ |
||||||
|
std::ostream* OUT = &std::cout; |
||||||
|
void setOutputStream(std::ostream* out); |
||||||
|
const char* logLevelToString(log_level level); |
||||||
|
std::tm* getCurrentTime(); |
||||||
|
int getCurrentMS(); |
||||||
|
}; |
||||||
|
|
||||||
|
static dumbLog logger; |
||||||
|
|
||||||
|
#define LOG(level) *logger.OUT \ |
||||||
|
<< std::put_time(logger.getCurrentTime(), "%F %T.") << logger.getCurrentMS() << " " \
|
||||||
|
<< "[" << logger.logLevelToString(level) << "] " \
|
||||||
|
<< "(" << __FUNCTION__ << ") " |
||||||
|
|
||||||
@ -0,0 +1,601 @@ |
|||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <cstddef> |
||||||
|
#include <cstdlib> |
||||||
|
#include <cstring> |
||||||
|
#include <string> |
||||||
|
#include <fstream> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
#include <SDL2/SDL.h> |
||||||
|
#include <glm/geometric.hpp> |
||||||
|
#include <glm/gtc/matrix_transform.hpp> |
||||||
|
|
||||||
|
#include "asset.h" |
||||||
|
#include "shader_testing.h" |
||||||
|
|
||||||
|
#include "dumpShader.inl" |
||||||
|
|
||||||
|
|
||||||
|
const float DEFAULT_FOV = 60.f; |
||||||
|
const float NEAR_CLIP_PLANE = 5.f; |
||||||
|
const float DEFAULT_ASPECT_RATIO = 16.f / 9.f; |
||||||
|
const glm::vec3 DEFAULT_CAM_POS = { 0, 0, -30.f }; |
||||||
|
const glm::vec3 DEFAULT_LOOK_POS = { 0, 0, 0 }; |
||||||
|
|
||||||
|
SDL_Window *g_window = 0; |
||||||
|
SDL_GLContext g_glContext; |
||||||
|
SDL_DisplayMode g_display_mode; |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
openglDebugCallback(GLenum source, |
||||||
|
GLenum type, |
||||||
|
GLuint id, |
||||||
|
GLenum severity, |
||||||
|
GLsizei length, |
||||||
|
const GLchar* message, |
||||||
|
const void* userParam) |
||||||
|
{ |
||||||
|
std::cout << ((type == GL_DEBUG_TYPE_ERROR) ? "Error" : "Debug") |
||||||
|
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "") |
||||||
|
<< ", type: " << type |
||||||
|
<< ", severity: " << severity |
||||||
|
<< ", message: " << message << "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
initGraphics() |
||||||
|
{ |
||||||
|
g_window = SDL_CreateWindow( |
||||||
|
"shader_testing", |
||||||
|
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
||||||
|
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
||||||
|
1280, |
||||||
|
720, |
||||||
|
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); |
||||||
|
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) { |
||||||
|
std::cout << "error, sdl init: " << SDL_GetError() << "\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
SDL_GL_SetSwapInterval(1); |
||||||
|
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_DOUBLEBUFFER, 1); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
||||||
|
SDL_GetCurrentDisplayMode(0, &g_display_mode); |
||||||
|
|
||||||
|
g_glContext = SDL_GL_CreateContext(g_window); |
||||||
|
|
||||||
|
if (!g_glContext) { |
||||||
|
std::cout << "error creating context\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (glewInit()) { |
||||||
|
std::cout << "error initializing opengl\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; |
||||||
|
std::cout << "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; |
||||||
|
std::cout << "opengl version: " << glGetString(GL_VERSION) << "\n"; |
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST); |
||||||
|
glEnable(GL_LINE_SMOOTH); |
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
||||||
|
|
||||||
|
glEnable (GL_DEBUG_OUTPUT); |
||||||
|
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); |
||||||
|
|
||||||
|
return g_window != nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const std::string |
||||||
|
dumpTextFile(const char* filepath) |
||||||
|
{ |
||||||
|
std::ifstream fs { filepath }; |
||||||
|
std::string s { |
||||||
|
std::istreambuf_iterator<char>(fs), |
||||||
|
std::istreambuf_iterator<char>()}; |
||||||
|
|
||||||
|
if (!fs || !fs.good()) |
||||||
|
std::cout << "error reading file, " << filepath << "\n"; |
||||||
|
|
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
parseShaderUniforms(memory_arena* arena, shader_program* s, GLContext* gl_ctx) |
||||||
|
{ |
||||||
|
GLint unif_count; |
||||||
|
glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORMS, &unif_count); |
||||||
|
s->num_uniforms = unif_count; |
||||||
|
s->uniforms = (gl_uniform*) arenaAllocateBlock( |
||||||
|
arena, s->num_uniforms * sizeof(gl_uniform)); |
||||||
|
|
||||||
|
for (u32 i = 0; i < s->num_uniforms; i++) { |
||||||
|
GLchar unif_name[256] = {0}; |
||||||
|
GLsizei length; |
||||||
|
GLint size; |
||||||
|
GLenum type; |
||||||
|
|
||||||
|
glGetActiveUniform(s->prog_id, i, sizeof(unif_name), |
||||||
|
&length, &size, &type, unif_name); |
||||||
|
|
||||||
|
s->uniforms[i].data_type = type; |
||||||
|
s->uniforms[i].name = (char*) arenaAllocateBlock( |
||||||
|
arena, (length + 1) * sizeof(char)); |
||||||
|
std::strncpy(s->uniforms[i].name, unif_name, length); |
||||||
|
|
||||||
|
glGetActiveUniformsiv(s->prog_id, 1, &i, |
||||||
|
GL_UNIFORM_BLOCK_INDEX, &s->uniforms[i].block_idx); |
||||||
|
|
||||||
|
// FIXME: testing idx == unif location
|
||||||
|
s->uniforms[i].idx = i; |
||||||
|
GLuint location = glGetUniformLocation(s->prog_id, s->uniforms[i].name); |
||||||
|
printf("idx: %d, loc: %d, block idx: %d \n", |
||||||
|
i, location, s->uniforms[i].block_idx); |
||||||
|
|
||||||
|
|
||||||
|
// TODO: get uniform.data_size from data_type
|
||||||
|
//printf("i: %d, uniform name: %s, l: %d \n", i, unif_name, length);
|
||||||
|
} |
||||||
|
|
||||||
|
GLint max_vert_ublocks; |
||||||
|
GLint max_frag_ublocks; |
||||||
|
GLint max_ublock_size; |
||||||
|
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &max_vert_ublocks); |
||||||
|
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &max_frag_ublocks); |
||||||
|
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_ublock_size); |
||||||
|
printf("max vert uniform blocks: %d, max frag uniform blocks: %d," |
||||||
|
"max uniform block size: %d bytes \n", |
||||||
|
max_vert_ublocks, max_frag_ublocks, max_ublock_size); |
||||||
|
GLint max_ubo_bindings; |
||||||
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &max_ubo_bindings); |
||||||
|
printf("max uniform buffer object binding points: %d \n", max_ubo_bindings); |
||||||
|
} |
||||||
|
|
||||||
|
gl_buffer |
||||||
|
initTransforms(transforms* xforms, |
||||||
|
GLContext* gl_ctx, |
||||||
|
float fov = DEFAULT_FOV, |
||||||
|
float near_clip_plane = NEAR_CLIP_PLANE, |
||||||
|
float aspect_ratio = DEFAULT_ASPECT_RATIO, |
||||||
|
glm::vec3 cam_pos = DEFAULT_CAM_POS, |
||||||
|
glm::vec3 look_pos = DEFAULT_LOOK_POS) |
||||||
|
{ |
||||||
|
xforms->view_xform = glm::lookAt(cam_pos, look_pos, glm::vec3(0, 1, 0)); |
||||||
|
xforms->proj_xform = glm::infinitePerspective( |
||||||
|
glm::radians(fov), aspect_ratio, near_clip_plane); |
||||||
|
|
||||||
|
gl_buffer ubo = {0}; |
||||||
|
glGenBuffers(1, &ubo.id); |
||||||
|
ubo.target = GL_UNIFORM_BUFFER; |
||||||
|
ubo.data_type = GL_FLOAT; |
||||||
|
ubo.data_size = sizeof(*xforms); |
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo.id); |
||||||
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(*xforms), xforms, GL_DYNAMIC_DRAW); |
||||||
|
|
||||||
|
// NOTE: need to manually keep track of block bindings
|
||||||
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &gl_ctx->max_binding_points); |
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, gl_ctx->binding_count, ubo.id); |
||||||
|
ubo.binding_idx = gl_ctx->binding_count; |
||||||
|
gl_ctx->binding_count++; |
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, 0); |
||||||
|
|
||||||
|
return ubo; |
||||||
|
} |
||||||
|
|
||||||
|
mesh |
||||||
|
initCubeMesh() |
||||||
|
{ |
||||||
|
mesh m = {0}; |
||||||
|
m.num_vertices = 8; |
||||||
|
m.vertices = (glm::vec3*) std::calloc(m.num_vertices, sizeof(glm::vec3)); |
||||||
|
m.vertices[0] = { -1, 1, -1 }; |
||||||
|
m.vertices[1] = { -1, -1, -1 }; |
||||||
|
m.vertices[2] = { 1, -1, -1 }; |
||||||
|
m.vertices[3] = { 1, 1, -1 }; |
||||||
|
m.vertices[4] = { -1, 1, 1 }; |
||||||
|
m.vertices[5] = { -1, -1, 1 }; |
||||||
|
m.vertices[6] = { 1, -1, 1 }; |
||||||
|
m.vertices[7] = { 1, 1, 1 }; |
||||||
|
|
||||||
|
m.num_indices = 36; // 6 sides, 2 tris per side, 3 verts per tri
|
||||||
|
m.indices = (u32*) std::calloc(m.num_indices, sizeof(u32)); |
||||||
|
u32 indices[36] = { |
||||||
|
0, 1, 2, 0, 2, 3, |
||||||
|
3, 2, 6, 3, 6, 7, |
||||||
|
7, 6, 5, 7, 5, 4, |
||||||
|
4, 5, 0, 4, 1, 0, |
||||||
|
0, 3, 4, 0, 3, 7, |
||||||
|
1, 2, 5, 2, 6, 5 |
||||||
|
}; |
||||||
|
std::memcpy(m.indices, indices, m.num_indices * sizeof(u32)); |
||||||
|
|
||||||
|
return m; |
||||||
|
} |
||||||
|
|
||||||
|
gl_mesh |
||||||
|
loadGLMesh(shader_program* s, |
||||||
|
const mesh& m, |
||||||
|
GLenum draw_mode, |
||||||
|
const glm::vec3& pos) |
||||||
|
{ |
||||||
|
gl_mesh gm = {0}; |
||||||
|
gm.num_indices = m.num_indices; |
||||||
|
gm.draw_mode = draw_mode; |
||||||
|
// NOTE: okay to store shader_program pointer on gl_mesh here because we
|
||||||
|
// won't ever delete the shader
|
||||||
|
gm.shader = s; |
||||||
|
|
||||||
|
glUseProgram(s->prog_id); |
||||||
|
glGenVertexArrays(1, &gm.vao_id); |
||||||
|
glBindVertexArray(gm.vao_id); |
||||||
|
|
||||||
|
gm.model_xform = (glm::mat4*) std::calloc(1, sizeof(glm::mat4)); |
||||||
|
*gm.model_xform = glm::translate(glm::mat4(1), pos); |
||||||
|
glUniformMatrix4fv( |
||||||
|
s->model_xform_id, 1, GL_FALSE, (float*) gm.model_xform); |
||||||
|
|
||||||
|
glGenBuffers(1, &gm.vert_buf_id); |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, gm.vert_buf_id); |
||||||
|
glBufferData(GL_ARRAY_BUFFER, |
||||||
|
m.num_vertices * 3 * sizeof(GLfloat), |
||||||
|
m.vertices, |
||||||
|
GL_STATIC_DRAW); |
||||||
|
glVertexAttribPointer(POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0); |
||||||
|
glEnableVertexAttribArray(POSITION); |
||||||
|
|
||||||
|
glGenBuffers(1, &gm.idx_buf_id); |
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gm.idx_buf_id); |
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, |
||||||
|
m.num_indices * sizeof(GLuint), |
||||||
|
m.indices, |
||||||
|
GL_STATIC_DRAW); |
||||||
|
|
||||||
|
glBindVertexArray(0); |
||||||
|
glUseProgram(0); |
||||||
|
|
||||||
|
return gm; |
||||||
|
} |
||||||
|
|
||||||
|
shader_program* |
||||||
|
getShaderByID(ShaderArray* sarr, GLuint prog_id) |
||||||
|
{ |
||||||
|
for (u32 i = 0; i < sarr->count; i++) { |
||||||
|
if (sarr->shaders[i].prog_id) |
||||||
|
return &sarr->shaders[i]; |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "Error, shader not found\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
loop(RenderState* rs) |
||||||
|
{ |
||||||
|
u32 delay = 60; |
||||||
|
u32 frame_start, frame_time; |
||||||
|
bool running = true; |
||||||
|
SDL_Event e; |
||||||
|
|
||||||
|
while (running) { |
||||||
|
frame_start = SDL_GetTicks(); |
||||||
|
|
||||||
|
while (SDL_PollEvent(&e)) { |
||||||
|
if (e.type == SDL_QUIT || |
||||||
|
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) |
||||||
|
{ |
||||||
|
running = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
glClearColor(0.2, 0.2, 0.2, 1); |
||||||
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
||||||
|
|
||||||
|
for (u32 i = 0; i < rs->gl_mesh_arr->count; i++) { |
||||||
|
gl_mesh& glm = rs->gl_mesh_arr->gl_meshes[i]; |
||||||
|
*glm.model_xform = glm::rotate( |
||||||
|
*glm.model_xform, (float) M_PI / 60, glm::vec3(0, 1, 0)); |
||||||
|
|
||||||
|
glUseProgram(glm.shader->prog_id); |
||||||
|
glBindVertexArray(glm.vao_id); |
||||||
|
glUniformMatrix4fv(glm.shader->model_xform_id, 1, GL_FALSE, |
||||||
|
(float*) glm.model_xform); |
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glm.idx_buf_id); |
||||||
|
glDrawElements( |
||||||
|
glm.draw_mode, glm.num_indices, GL_UNSIGNED_INT, 0); |
||||||
|
glBindVertexArray(0); |
||||||
|
} |
||||||
|
|
||||||
|
SDL_GL_SwapWindow(g_window); |
||||||
|
glUseProgram(0); |
||||||
|
frame_time = SDL_GetTicks() - frame_start; |
||||||
|
|
||||||
|
if (delay > frame_time) |
||||||
|
SDL_Delay(delay - frame_time); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
quit(RenderState* rs) |
||||||
|
{ |
||||||
|
if (rs->arena) |
||||||
|
std::free(rs->arena); |
||||||
|
|
||||||
|
SDL_GL_DeleteContext(g_glContext); |
||||||
|
SDL_DestroyWindow(g_window); |
||||||
|
SDL_Quit(); |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: equivalent to rgAppend() in libTangerine
|
||||||
|
model* |
||||||
|
getModel(RenderState* rs, const char* filepath) |
||||||
|
{ |
||||||
|
model* mdl = assetGetCached(rs->assets, utilFNV64a_str(filepath)); |
||||||
|
|
||||||
|
if (!mdl) |
||||||
|
mdl = assetLoadFromFile(rs->assets, rs->textures, rs->arena, filepath); |
||||||
|
|
||||||
|
return mdl; |
||||||
|
} |
||||||
|
|
||||||
|
ShaderArray* |
||||||
|
initShaderArray(memory_arena* arena, u32 count) |
||||||
|
{ |
||||||
|
ShaderArray* arr = |
||||||
|
(ShaderArray*) arenaAllocateBlock(arena, sizeof(ShaderArray)); |
||||||
|
arr->max = count; |
||||||
|
arr->count = 0; |
||||||
|
arr->shaders = (shader_program*) arenaAllocateBlock( |
||||||
|
arena, count * sizeof(shader_program)); |
||||||
|
|
||||||
|
return arr; |
||||||
|
} |
||||||
|
|
||||||
|
gl_mesh_array* |
||||||
|
initGLMeshArray(memory_arena* arena, u32 count) |
||||||
|
{ |
||||||
|
gl_mesh_array* gma = |
||||||
|
(gl_mesh_array*) arenaAllocateBlock(arena, sizeof(gl_mesh_array)); |
||||||
|
gma->max = count; |
||||||
|
gma->count = 0; |
||||||
|
gma->gl_meshes = |
||||||
|
(gl_mesh*) arenaAllocateBlock(arena, count * sizeof(gl_mesh)); |
||||||
|
|
||||||
|
return gma; |
||||||
|
} |
||||||
|
|
||||||
|
GLContext* |
||||||
|
initGLContext(memory_arena* arena) |
||||||
|
{ |
||||||
|
GLContext* gl_ctx = |
||||||
|
(GLContext*) arenaAllocateBlock(arena, sizeof(GLContext)); |
||||||
|
gl_ctx->shader_arr = initShaderArray(arena, 256); |
||||||
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &gl_ctx->max_binding_points); |
||||||
|
gl_ctx->binding_count = 0; |
||||||
|
|
||||||
|
return gl_ctx; |
||||||
|
} |
||||||
|
|
||||||
|
RenderState* |
||||||
|
initRenderState(memory_arena* arena) |
||||||
|
{ |
||||||
|
RenderState* rs = |
||||||
|
(RenderState*) arenaAllocateBlock(arena, sizeof(RenderState)); |
||||||
|
|
||||||
|
if (rs) { |
||||||
|
rs->arena = arena; |
||||||
|
rs->assets = assetInitModelBlock(arena, 256); |
||||||
|
rs->textures = assetInitTextureBlock(arena, 256); |
||||||
|
rs->gl_ctx = initGLContext(arena); |
||||||
|
rs->xforms = |
||||||
|
(transforms*) arenaAllocateBlock(arena, sizeof(transforms)); |
||||||
|
rs->gl_mesh_arr = initGLMeshArray(arena, 256); |
||||||
|
} |
||||||
|
|
||||||
|
return rs; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: clean up this function
|
||||||
|
bool |
||||||
|
addShaderProgram(memory_arena* arena, |
||||||
|
GLContext* gl_ctx, |
||||||
|
const char* vs, |
||||||
|
const char* fs, |
||||||
|
const char* name) |
||||||
|
{ |
||||||
|
if (gl_ctx->shader_arr->count >= gl_ctx->shader_arr->max) { |
||||||
|
std::cout << "ShaderArray full\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
shader_program* s = &gl_ctx->shader_arr->shaders[gl_ctx->shader_arr->count]; |
||||||
|
gl_ctx->shader_arr->count++; |
||||||
|
|
||||||
|
u32 name_len = std::strlen(name) + 1; |
||||||
|
s->name = (char*) arenaAllocateBlock(arena, name_len); |
||||||
|
std::strncpy(s->name, name, name_len); |
||||||
|
|
||||||
|
const u32 max_len = 256; |
||||||
|
std::string hash_str = vs; |
||||||
|
hash_str += fs; |
||||||
|
s->hash = utilFNV64a_str(hash_str.substr(0, max_len).c_str()); |
||||||
|
// FIXME: should probably check the hash here against other shaders loaded
|
||||||
|
|
||||||
|
std::string vert = dumpTextFile(vs); |
||||||
|
std::string frag = dumpTextFile(fs); |
||||||
|
|
||||||
|
if (vert.size() > 0 && frag.size() > 0) { |
||||||
|
const char* vert_c = vert.c_str(); |
||||||
|
const char* frag_c = frag.c_str(); |
||||||
|
|
||||||
|
GLuint vs_id = glCreateShader(GL_VERTEX_SHADER); |
||||||
|
GLuint fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
||||||
|
glShaderSource(vs_id, 1, &vert_c, NULL); |
||||||
|
glShaderSource(fs_id, 1, &frag_c, NULL); |
||||||
|
// TODO: both of these can fail
|
||||||
|
glCompileShader(vs_id); |
||||||
|
glCompileShader(fs_id); |
||||||
|
s->prog_id = glCreateProgram(); |
||||||
|
glAttachShader(s->prog_id, vs_id); |
||||||
|
glAttachShader(s->prog_id, fs_id); |
||||||
|
|
||||||
|
glLinkProgram(s->prog_id); |
||||||
|
GLint is_linked = 0; |
||||||
|
glGetProgramiv(s->prog_id, GL_LINK_STATUS, &is_linked); |
||||||
|
|
||||||
|
if (is_linked) { |
||||||
|
s->model_xform_id = glGetUniformLocation(s->prog_id, "model_xform"); |
||||||
|
|
||||||
|
// FIXME: testing
|
||||||
|
// NOTE: set block binding for shader xforms uniform block
|
||||||
|
GLuint block_idx = glGetUniformBlockIndex(s->prog_id, "matrices"); |
||||||
|
glUniformBlockBinding(s->prog_id, |
||||||
|
block_idx, |
||||||
|
gl_ctx->xform_ubo.binding_idx); |
||||||
|
|
||||||
|
// FIXME: fill out new shader extra uniforms and buffers here
|
||||||
|
dumpShader(s->prog_id); |
||||||
|
printf("offset of RenderState.xforms: %lu bytes\n", |
||||||
|
offsetof(RenderState, xforms)); |
||||||
|
parseShaderUniforms(arena, s, gl_ctx); |
||||||
|
///
|
||||||
|
|
||||||
|
glDetachShader(s->prog_id, vs_id); |
||||||
|
glDetachShader(s->prog_id, fs_id); |
||||||
|
glDeleteShader(vs_id); |
||||||
|
glDeleteShader(fs_id); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
printf("%s(), Error linking shader\n", __FUNCTION__); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
addGLMesh(gl_mesh_array* gma, gl_mesh* gm) |
||||||
|
{ |
||||||
|
if (gma->count < gma->max) { |
||||||
|
// FIXME: wtf was I doing here?
|
||||||
|
gma->count++; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
shader_program* |
||||||
|
getShaderByName(const char* name, ShaderArray* s_arr) |
||||||
|
{ |
||||||
|
u64 hash = utilFNV64a_str(name); |
||||||
|
|
||||||
|
for (u32 i = 0; i < s_arr->count; i++) { |
||||||
|
if (utilFNV64a_str(s_arr->shaders[i].name) == hash) |
||||||
|
return &s_arr->shaders[i]; |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "shader not found, " << name << "\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
gl_mesh* |
||||||
|
getGLMesh(gl_mesh_array* gma) |
||||||
|
{ |
||||||
|
if (gma->count < gma->max) { |
||||||
|
gl_mesh* glm = &gma->gl_meshes[gma->count]; |
||||||
|
gma->count++; |
||||||
|
|
||||||
|
return glm; |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "Error, gl_mesh_array is full\n"; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
loadScene(RenderState* rs) |
||||||
|
{ |
||||||
|
mesh cube = initCubeMesh(); |
||||||
|
const u32 NUM_CUBES = 4; |
||||||
|
glm::vec3 cube_locs[NUM_CUBES] = { |
||||||
|
glm::vec3(-10, 10, 0), |
||||||
|
glm::vec3(-10, -10, 0), |
||||||
|
glm::vec3( 10, 10, 0), |
||||||
|
glm::vec3( 10, -10, 0), |
||||||
|
}; |
||||||
|
|
||||||
|
// TODO: full lighting model
|
||||||
|
//model* tex_cube = getModel(rs, "data/textured_cube.gltf");
|
||||||
|
|
||||||
|
// TODO: load debug shader from libTangerine for textured_cube
|
||||||
|
shader_program* s = getShaderByName("default", rs->gl_ctx->shader_arr); |
||||||
|
|
||||||
|
if (!s) |
||||||
|
return false; |
||||||
|
|
||||||
|
for (u32 i = 0; i < NUM_CUBES; i++) { |
||||||
|
gl_mesh* gmesh = getGLMesh(rs->gl_mesh_arr); |
||||||
|
|
||||||
|
if (!gmesh) |
||||||
|
return false; |
||||||
|
|
||||||
|
*gmesh = loadGLMesh(s, cube, GL_TRIANGLES, cube_locs[i]); |
||||||
|
|
||||||
|
if (gmesh->vao_id == 0) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
int |
||||||
|
main() |
||||||
|
{ |
||||||
|
if (!initGraphics()) |
||||||
|
return 1; |
||||||
|
|
||||||
|
memory_arena* arena = arenaInit(DEFAULT_ARENA_SIZE); |
||||||
|
RenderState* rs = initRenderState(arena); |
||||||
|
|
||||||
|
if (rs) { |
||||||
|
rs->gl_ctx->xform_ubo = initTransforms(rs->xforms, rs->gl_ctx); |
||||||
|
|
||||||
|
if (!addShaderProgram(arena, |
||||||
|
rs->gl_ctx, |
||||||
|
"data/shader.vert", |
||||||
|
"data/shader.frag", |
||||||
|
"default")) |
||||||
|
{ |
||||||
|
std::cout << "error loading shader program\n"; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (!loadScene(rs)) { |
||||||
|
std::cout << "error loading scene\n"; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
loop(rs); |
||||||
|
quit(rs); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "error loading scene\n"; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,134 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <GL/glew.h> |
||||||
|
#include <glm/glm.hpp> |
||||||
|
|
||||||
|
|
||||||
|
typedef uint32_t u32; |
||||||
|
|
||||||
|
|
||||||
|
// FIXME: won't need this enum when dynamic shader parsing is working
|
||||||
|
enum glsl_layout |
||||||
|
{ |
||||||
|
POSITION, |
||||||
|
NORMAL, |
||||||
|
UV |
||||||
|
}; |
||||||
|
|
||||||
|
struct gl_buffer |
||||||
|
{ |
||||||
|
GLuint id; |
||||||
|
GLenum target; |
||||||
|
GLenum data_type; |
||||||
|
GLuint data_size; |
||||||
|
GLuint binding_idx; // NOTE: set when used as backing for UBO
|
||||||
|
}; |
||||||
|
|
||||||
|
struct gl_uniform |
||||||
|
{ |
||||||
|
GLuint idx; // NOTE: seems to map to location if not part of uniform block
|
||||||
|
//GLuint location;
|
||||||
|
GLint block_idx; |
||||||
|
GLenum data_type; |
||||||
|
GLint data_size; |
||||||
|
char* name; |
||||||
|
}; |
||||||
|
|
||||||
|
struct shader_program |
||||||
|
{ |
||||||
|
GLuint prog_id; |
||||||
|
GLuint xforms_ubo_id; |
||||||
|
GLuint model_xform_id; |
||||||
|
|
||||||
|
u32 num_uniforms; |
||||||
|
gl_uniform* uniforms; |
||||||
|
|
||||||
|
u32 num_buffers; |
||||||
|
gl_buffer* buffers; |
||||||
|
|
||||||
|
char* name; |
||||||
|
u64 hash; // NOTE: hash of vs filpath + fs filepath concat
|
||||||
|
}; |
||||||
|
|
||||||
|
// TODO: is it a good idea to merge this into GLContext?
|
||||||
|
struct ShaderArray |
||||||
|
{ |
||||||
|
u32 count; |
||||||
|
u32 max; |
||||||
|
shader_program* shaders; |
||||||
|
}; |
||||||
|
|
||||||
|
struct GLContext |
||||||
|
{ |
||||||
|
gl_buffer xform_ubo; |
||||||
|
// TODO: keep an array of uniform buffer objects store on ctx?
|
||||||
|
|
||||||
|
GLuint binding_count; |
||||||
|
GLint max_binding_points; |
||||||
|
|
||||||
|
ShaderArray* shader_arr; |
||||||
|
}; |
||||||
|
|
||||||
|
struct transforms |
||||||
|
{ |
||||||
|
glm::mat4 view_xform; |
||||||
|
glm::mat4 proj_xform; |
||||||
|
glm::mat4 normal_xform; |
||||||
|
}; |
||||||
|
|
||||||
|
struct gl_mesh |
||||||
|
{ |
||||||
|
u32 num_indices; |
||||||
|
GLuint vao_id; |
||||||
|
GLuint tex_id; |
||||||
|
GLenum draw_mode; // NOTE: GL_LINES, GL_TRIANGLES
|
||||||
|
GLenum usage; // NOTE: GL_STATIC_DRAW, GL_DYNAMIC_DRAW
|
||||||
|
|
||||||
|
shader_program* shader; |
||||||
|
glm::mat4* model_xform; |
||||||
|
|
||||||
|
GLuint vert_buf_id; |
||||||
|
GLuint norm_buf_id; |
||||||
|
GLuint uv_buf_id; |
||||||
|
GLuint clr_buf_id; |
||||||
|
GLuint idx_buf_id; |
||||||
|
}; |
||||||
|
|
||||||
|
struct gl_mesh_array |
||||||
|
{ |
||||||
|
u32 count; |
||||||
|
u32 max; |
||||||
|
gl_mesh* gl_meshes; |
||||||
|
}; |
||||||
|
|
||||||
|
struct point_light |
||||||
|
{ |
||||||
|
glm::vec3 position; |
||||||
|
glm::vec3 color; |
||||||
|
u32 intensity; |
||||||
|
}; |
||||||
|
|
||||||
|
struct light_array |
||||||
|
{ |
||||||
|
u32 count; |
||||||
|
point_light* lights; |
||||||
|
}; |
||||||
|
|
||||||
|
struct RenderState |
||||||
|
{ |
||||||
|
memory_arena* arena; |
||||||
|
model_assets* assets; |
||||||
|
texture_assets* textures; |
||||||
|
|
||||||
|
transforms* xforms; // NOTE: would be part of camera in libTangerine
|
||||||
|
|
||||||
|
GLContext* gl_ctx; |
||||||
|
gl_mesh_array* gl_mesh_arr; |
||||||
|
light_array lights; |
||||||
|
}; |
||||||
|
|
||||||
|
struct node; |
||||||
|
struct entity; |
||||||
|
struct animation; |
||||||
|
|
||||||
@ -0,0 +1,4 @@ |
|||||||
|
#define STB_IMAGE_IMPLEMENTATION |
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION |
||||||
|
#define TINYGLTF_IMPLEMENTATION |
||||||
|
#include "tiny_gltf.h" |
||||||
Loading…
Reference in new issue