18 changed files with 38710 additions and 453 deletions
@ -1,15 +1,35 @@
|
||||
|
||||
SHELL = /bin/sh
|
||||
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
|
||||
OBJDIR = build
|
||||
SRCDIR = src
|
||||
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 |
||||
|
||||
-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: |
||||
rm -rf $(BIN)
|
||||
rm -rf build/*
|
||||
.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