53 changed files with 2594 additions and 3725 deletions
@ -1,72 +0,0 @@
|
||||
|
||||
#include <cstdio> // snprintf |
||||
#include <iostream> // std::cout |
||||
|
||||
#include <assimp/cimport.h> |
||||
#include <assimp/scene.h> |
||||
#include <assimp/postprocess.h> |
||||
|
||||
#include "util.h" |
||||
#include "dumbLog.h" |
||||
#include "mesh.h" |
||||
|
||||
|
||||
void |
||||
debugParseNode(aiNode* node, aiMatrix4x4 xform, uint depth=0) |
||||
{ |
||||
depth++; |
||||
char tabs[256]; |
||||
snprintf(tabs, 256, "%*s", depth * 4, " "); |
||||
std::cout << tabs << node->mName.C_Str() << ", has meshes: " << (node->mNumMeshes > 0) << "\n"; |
||||
|
||||
if (node->mNumMeshes > 0) { |
||||
for (uint i = 0; i < node->mNumMeshes; i++) |
||||
std::cout << tabs << " mesh index: " << node->mMeshes[i] << "\n"; |
||||
} |
||||
|
||||
for (uint i = 0; i < node->mNumChildren; i++) |
||||
debugParseNode(node->mChildren[i], xform, depth); |
||||
} |
||||
|
||||
void |
||||
debugParseAnimation(aiAnimation* anim) |
||||
{ |
||||
std::cout << "Animation, ticks/s: " << anim->mTicksPerSecond |
||||
<< ", duration: " << anim->mDuration |
||||
<< ", channels: " << anim->mNumChannels |
||||
<< "\n"; |
||||
|
||||
for (uint i = 0; i < anim->mNumChannels; i++) { |
||||
aiNodeAnim* chan = anim->mChannels[i]; |
||||
std::cout << " channel " << i |
||||
<< ", node name: " << chan->mNodeName.C_Str() |
||||
<< ", mNumPositionKeys: " << chan->mNumPositionKeys |
||||
<< ", mNumRotationKeys: " << chan->mNumRotationKeys |
||||
<< ", mNumScalingKeys: " << chan->mNumScalingKeys |
||||
<< "\n"; |
||||
} |
||||
} |
||||
|
||||
void |
||||
logDebugAnimationInfo(const char* model_name) |
||||
{ |
||||
const aiScene* scene = aiImportFile(model_name, aiProcessPreset_TargetRealtime_MaxQuality); |
||||
|
||||
if (!scene) { |
||||
LOG(Error) << "Error loading file: " << model_name << "\n"; |
||||
return; |
||||
} |
||||
|
||||
std::cout << "\n\n--------------------------------------\n"; |
||||
std::cout << "Nodes:\n"; |
||||
aiMatrix4x4 identity_mat; |
||||
debugParseNode(scene->mRootNode, identity_mat); |
||||
|
||||
std::cout << "--------------------------------------\n"; |
||||
std::cout << "Animations:\n"; |
||||
|
||||
if (scene->HasAnimations()) |
||||
debugParseAnimation(scene->mAnimations[0]); |
||||
|
||||
std::cout << "--------------------------------------\n\n\n"; |
||||
} |
||||
@ -1,85 +0,0 @@
|
||||
|
||||
#include <SDL2/SDL.h> |
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "camera.h" |
||||
#include "dumbLog.h" |
||||
#include "input.h" |
||||
#include "entity.h" |
||||
#include "renderer.h" |
||||
#include "shader_program.h" |
||||
|
||||
|
||||
void |
||||
doFrameCallback(render_state* rs) |
||||
{ |
||||
static input_state is = {}; |
||||
inputProcessEvents(&is); |
||||
|
||||
if (is.window_closed || is.escape) { |
||||
rs->running = false; |
||||
return; |
||||
} |
||||
|
||||
int rotate_mod = 0; |
||||
|
||||
if (is.left) |
||||
rotate_mod = -1; |
||||
if (is.right) |
||||
rotate_mod = 1; |
||||
|
||||
// NOTE: rotate mesh on z-axis every frame
|
||||
entity& e = rs->render_groups[0]->entities[0]; |
||||
static float angle = (float) M_PI_2 / 33; |
||||
static glm::vec3 axis(0, 0, 1); |
||||
entRotate(e, angle * rotate_mod, axis); |
||||
} |
||||
|
||||
// TODO: remove/refactor this when we get animation working
|
||||
#include "animation_testing.cpp" |
||||
|
||||
int |
||||
main() |
||||
{ |
||||
render_state* rs = renInit("assimp loading"); |
||||
rs->render_groups = UTIL_ALLOC(256, render_group*); |
||||
|
||||
if (rs == nullptr) { |
||||
LOG(Error) << "Error Initialzing renderer\n"; |
||||
return 1; |
||||
} |
||||
|
||||
// TODO: this needs to be more convenient
|
||||
shader_wrapper sw = { DEFAULT_SHADER, rs->default_shader, nullptr }; |
||||
rs->render_groups[0] = renAllocateGroup(1, sw); |
||||
rs->render_group_count = 1; |
||||
entity& spaceship = rs->render_groups[0]->entities[0]; |
||||
|
||||
cameraInitPerspective( |
||||
rs->cam, |
||||
glm::vec3(200, -150, 150), |
||||
glm::vec3(0, 0, 0), |
||||
glm::vec3(0,0,1) |
||||
); |
||||
|
||||
renAddLight(rs, glm::vec3(200, -150, 150)); |
||||
|
||||
// TODO: look into setting up git-annex for large files. git-lfs works fine
|
||||
// for gitlab, but has no real implementation for self-hosting:
|
||||
// https://github.com/git-lfs/git-lfs/issues/1044
|
||||
// https://git-annex.branchable.com/
|
||||
//
|
||||
// NOTE: testing assimp animation info
|
||||
logDebugAnimationInfo("../data/spaceship.glb"); |
||||
|
||||
if (entInitModel(spaceship, "../data/spaceship.glb")) { |
||||
entScale(spaceship, glm::vec3(20, 20, 20)); |
||||
renDoRenderLoop(rs, 60, doFrameCallback); |
||||
} else { |
||||
LOG(Error) << "Error initializing entity, exiting\n"; |
||||
} |
||||
|
||||
renShutdown(rs); |
||||
return 0; |
||||
} |
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,133 +0,0 @@
|
||||
{ |
||||
"asset" : { |
||||
"generator" : "Khronos glTF Blender I/O v1.6.16", |
||||
"version" : "2.0" |
||||
}, |
||||
"scene" : 0, |
||||
"scenes" : [ |
||||
{ |
||||
"name" : "Scene", |
||||
"nodes" : [ |
||||
0 |
||||
] |
||||
} |
||||
], |
||||
"nodes" : [ |
||||
{ |
||||
"mesh" : 0, |
||||
"name" : "Icosphere" |
||||
} |
||||
], |
||||
"materials" : [ |
||||
{ |
||||
"doubleSided" : true, |
||||
"name" : "Material.001", |
||||
"pbrMetallicRoughness" : { |
||||
"baseColorTexture" : { |
||||
"index" : 0 |
||||
}, |
||||
"metallicFactor" : 0, |
||||
"roughnessFactor" : 0.5 |
||||
} |
||||
} |
||||
], |
||||
"meshes" : [ |
||||
{ |
||||
"name" : "Icosphere", |
||||
"primitives" : [ |
||||
{ |
||||
"attributes" : { |
||||
"POSITION" : 0, |
||||
"NORMAL" : 1, |
||||
"TEXCOORD_0" : 2 |
||||
}, |
||||
"indices" : 3, |
||||
"material" : 0 |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"textures" : [ |
||||
{ |
||||
"sampler" : 0, |
||||
"source" : 0 |
||||
} |
||||
], |
||||
"images" : [ |
||||
{ |
||||
"mimeType" : "image/png", |
||||
"name" : "Color Palette 140", |
||||
"uri" : "Color%20Palette%20140.png" |
||||
} |
||||
], |
||||
"accessors" : [ |
||||
{ |
||||
"bufferView" : 0, |
||||
"componentType" : 5126, |
||||
"count" : 960, |
||||
"max" : [ |
||||
1, |
||||
1, |
||||
0.9999999403953552 |
||||
], |
||||
"min" : [ |
||||
-0.9999999403953552, |
||||
-1, |
||||
-0.9999999403953552 |
||||
], |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 1, |
||||
"componentType" : 5126, |
||||
"count" : 960, |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 2, |
||||
"componentType" : 5126, |
||||
"count" : 960, |
||||
"type" : "VEC2" |
||||
}, |
||||
{ |
||||
"bufferView" : 3, |
||||
"componentType" : 5123, |
||||
"count" : 960, |
||||
"type" : "SCALAR" |
||||
} |
||||
], |
||||
"bufferViews" : [ |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 11520, |
||||
"byteOffset" : 0 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 11520, |
||||
"byteOffset" : 11520 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 7680, |
||||
"byteOffset" : 23040 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1920, |
||||
"byteOffset" : 30720 |
||||
} |
||||
], |
||||
"samplers" : [ |
||||
{ |
||||
"magFilter" : 9729, |
||||
"minFilter" : 9987 |
||||
} |
||||
], |
||||
"buffers" : [ |
||||
{ |
||||
"byteLength" : 32640, |
||||
"uri" : "icosphere.bin" |
||||
} |
||||
] |
||||
} |
||||
Binary file not shown.
Binary file not shown.
@ -1,453 +0,0 @@
|
||||
{ |
||||
"asset" : { |
||||
"generator" : "Khronos glTF Blender I/O v1.6.16", |
||||
"version" : "2.0" |
||||
}, |
||||
"scene" : 0, |
||||
"scenes" : [ |
||||
{ |
||||
"name" : "Scene", |
||||
"nodes" : [ |
||||
4 |
||||
] |
||||
} |
||||
], |
||||
"nodes" : [ |
||||
{ |
||||
"mesh" : 0, |
||||
"name" : "Spaceship_Rear_Hatch", |
||||
"rotation" : [ |
||||
2.8049830902432404e-08, |
||||
8.798715533941959e-09, |
||||
0.7938249707221985, |
||||
0.6081464290618896 |
||||
], |
||||
"scale" : [ |
||||
0.17620019614696503, |
||||
0.1893281489610672, |
||||
0.1893281191587448 |
||||
], |
||||
"translation" : [ |
||||
0.9305203557014465, |
||||
-0.8287604479340512, |
||||
-1.1537752442336568e-07 |
||||
] |
||||
}, |
||||
{ |
||||
"children" : [ |
||||
0 |
||||
], |
||||
"name" : "Bone.001", |
||||
"rotation" : [ |
||||
-1.9689133878841858e-08, |
||||
-6.542580166524203e-08, |
||||
-0.12394285947084427, |
||||
0.9922893643379211 |
||||
], |
||||
"scale" : [ |
||||
1, |
||||
1.0000001192092896, |
||||
1 |
||||
], |
||||
"translation" : [ |
||||
9.194034422677078e-17, |
||||
1.0409293174743652, |
||||
5.551115123125783e-17 |
||||
] |
||||
}, |
||||
{ |
||||
"mesh" : 1, |
||||
"name" : "Spaceship", |
||||
"rotation" : [ |
||||
-3.244472424057676e-08, |
||||
-1.6142790215667446e-08, |
||||
0.7123286724090576, |
||||
0.7018461227416992 |
||||
], |
||||
"scale" : [ |
||||
0.17620022594928741, |
||||
0.1893281489610672, |
||||
0.1893281191587448 |
||||
], |
||||
"translation" : [ |
||||
0.6980774998664856, |
||||
0.008746981620788574, |
||||
2.8927825468372248e-08 |
||||
] |
||||
}, |
||||
{ |
||||
"children" : [ |
||||
1, |
||||
2 |
||||
], |
||||
"name" : "Bone", |
||||
"rotation" : [ |
||||
1.2074783839466363e-08, |
||||
2.1153852003408247e-08, |
||||
-0.007412227801978588, |
||||
0.9999725222587585 |
||||
], |
||||
"scale" : [ |
||||
0.9999998807907104, |
||||
1, |
||||
1 |
||||
] |
||||
}, |
||||
{ |
||||
"children" : [ |
||||
3 |
||||
], |
||||
"name" : "Armature", |
||||
"rotation" : [ |
||||
0, |
||||
0, |
||||
-0.7071067690849304, |
||||
0.7071068286895752 |
||||
] |
||||
} |
||||
], |
||||
"animations" : [ |
||||
{ |
||||
"channels" : [ |
||||
{ |
||||
"sampler" : 0, |
||||
"target" : { |
||||
"node" : 3, |
||||
"path" : "translation" |
||||
} |
||||
}, |
||||
{ |
||||
"sampler" : 1, |
||||
"target" : { |
||||
"node" : 3, |
||||
"path" : "rotation" |
||||
} |
||||
}, |
||||
{ |
||||
"sampler" : 2, |
||||
"target" : { |
||||
"node" : 3, |
||||
"path" : "scale" |
||||
} |
||||
}, |
||||
{ |
||||
"sampler" : 3, |
||||
"target" : { |
||||
"node" : 1, |
||||
"path" : "translation" |
||||
} |
||||
}, |
||||
{ |
||||
"sampler" : 4, |
||||
"target" : { |
||||
"node" : 1, |
||||
"path" : "rotation" |
||||
} |
||||
}, |
||||
{ |
||||
"sampler" : 5, |
||||
"target" : { |
||||
"node" : 1, |
||||
"path" : "scale" |
||||
} |
||||
} |
||||
], |
||||
"name" : "ArmatureAction.001", |
||||
"samplers" : [ |
||||
{ |
||||
"input" : 8, |
||||
"interpolation" : "LINEAR", |
||||
"output" : 9 |
||||
}, |
||||
{ |
||||
"input" : 8, |
||||
"interpolation" : "LINEAR", |
||||
"output" : 10 |
||||
}, |
||||
{ |
||||
"input" : 8, |
||||
"interpolation" : "LINEAR", |
||||
"output" : 11 |
||||
}, |
||||
{ |
||||
"input" : 8, |
||||
"interpolation" : "LINEAR", |
||||
"output" : 12 |
||||
}, |
||||
{ |
||||
"input" : 8, |
||||
"interpolation" : "LINEAR", |
||||
"output" : 13 |
||||
}, |
||||
{ |
||||
"input" : 8, |
||||
"interpolation" : "LINEAR", |
||||
"output" : 14 |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"materials" : [ |
||||
{ |
||||
"doubleSided" : true, |
||||
"name" : "Material.001", |
||||
"pbrMetallicRoughness" : { |
||||
"baseColorTexture" : { |
||||
"index" : 0 |
||||
}, |
||||
"metallicFactor" : 0, |
||||
"roughnessFactor" : 0.5 |
||||
} |
||||
} |
||||
], |
||||
"meshes" : [ |
||||
{ |
||||
"name" : "Cube.001", |
||||
"primitives" : [ |
||||
{ |
||||
"attributes" : { |
||||
"POSITION" : 0, |
||||
"NORMAL" : 1, |
||||
"TEXCOORD_0" : 2 |
||||
}, |
||||
"indices" : 3, |
||||
"material" : 0 |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name" : "Cube.002", |
||||
"primitives" : [ |
||||
{ |
||||
"attributes" : { |
||||
"POSITION" : 4, |
||||
"NORMAL" : 5, |
||||
"TEXCOORD_0" : 6 |
||||
}, |
||||
"indices" : 7, |
||||
"material" : 0 |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"textures" : [ |
||||
{ |
||||
"sampler" : 0, |
||||
"source" : 0 |
||||
} |
||||
], |
||||
"images" : [ |
||||
{ |
||||
"mimeType" : "image/png", |
||||
"name" : "Color Palette 140", |
||||
"uri" : "Color%20Palette%20140.png" |
||||
} |
||||
], |
||||
"accessors" : [ |
||||
{ |
||||
"bufferView" : 0, |
||||
"componentType" : 5126, |
||||
"count" : 56, |
||||
"max" : [ |
||||
8.864760398864746, |
||||
3.6763174533843994, |
||||
2.2041707038879395 |
||||
], |
||||
"min" : [ |
||||
5.919982433319092, |
||||
2.8251190185546875, |
||||
-2.2041707038879395 |
||||
], |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 1, |
||||
"componentType" : 5126, |
||||
"count" : 56, |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 2, |
||||
"componentType" : 5126, |
||||
"count" : 56, |
||||
"type" : "VEC2" |
||||
}, |
||||
{ |
||||
"bufferView" : 3, |
||||
"componentType" : 5123, |
||||
"count" : 90, |
||||
"type" : "SCALAR" |
||||
}, |
||||
{ |
||||
"bufferView" : 4, |
||||
"componentType" : 5126, |
||||
"count" : 1487, |
||||
"max" : [ |
||||
9.09161376953125, |
||||
4.241826057434082, |
||||
5.432835578918457 |
||||
], |
||||
"min" : [ |
||||
-9.08266544342041, |
||||
0.25478607416152954, |
||||
-5.432835578918457 |
||||
], |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 5, |
||||
"componentType" : 5126, |
||||
"count" : 1487, |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 6, |
||||
"componentType" : 5126, |
||||
"count" : 1487, |
||||
"type" : "VEC2" |
||||
}, |
||||
{ |
||||
"bufferView" : 7, |
||||
"componentType" : 5123, |
||||
"count" : 2334, |
||||
"type" : "SCALAR" |
||||
}, |
||||
{ |
||||
"bufferView" : 8, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"max" : [ |
||||
4.583333333333333 |
||||
], |
||||
"min" : [ |
||||
0.041666666666666664 |
||||
], |
||||
"type" : "SCALAR" |
||||
}, |
||||
{ |
||||
"bufferView" : 9, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 10, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"type" : "VEC4" |
||||
}, |
||||
{ |
||||
"bufferView" : 11, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 12, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"type" : "VEC3" |
||||
}, |
||||
{ |
||||
"bufferView" : 13, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"type" : "VEC4" |
||||
}, |
||||
{ |
||||
"bufferView" : 14, |
||||
"componentType" : 5126, |
||||
"count" : 110, |
||||
"type" : "VEC3" |
||||
} |
||||
], |
||||
"bufferViews" : [ |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 672, |
||||
"byteOffset" : 0 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 672, |
||||
"byteOffset" : 672 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 448, |
||||
"byteOffset" : 1344 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 180, |
||||
"byteOffset" : 1792 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 17844, |
||||
"byteOffset" : 1972 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 17844, |
||||
"byteOffset" : 19816 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 11896, |
||||
"byteOffset" : 37660 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 4668, |
||||
"byteOffset" : 49556 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 440, |
||||
"byteOffset" : 54224 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1320, |
||||
"byteOffset" : 54664 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1760, |
||||
"byteOffset" : 55984 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1320, |
||||
"byteOffset" : 57744 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1320, |
||||
"byteOffset" : 59064 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1760, |
||||
"byteOffset" : 60384 |
||||
}, |
||||
{ |
||||
"buffer" : 0, |
||||
"byteLength" : 1320, |
||||
"byteOffset" : 62144 |
||||
} |
||||
], |
||||
"samplers" : [ |
||||
{ |
||||
"magFilter" : 9729, |
||||
"minFilter" : 9987 |
||||
} |
||||
], |
||||
"buffers" : [ |
||||
{ |
||||
"byteLength" : 63464, |
||||
"uri" : "spaceship.bin" |
||||
} |
||||
] |
||||
} |
||||
Binary file not shown.
Binary file not shown.
@ -1,16 +0,0 @@
|
||||
|
||||
#include "renderer.h" |
||||
|
||||
|
||||
int |
||||
main() |
||||
{ |
||||
render_state* rs = renInit("Hello World"); |
||||
|
||||
if (rs == nullptr) |
||||
return 1; |
||||
|
||||
renDoRenderLoop(rs); |
||||
|
||||
return 0; |
||||
} |
||||
@ -0,0 +1,331 @@
|
||||
|
||||
#include <cassert> |
||||
#include <cmath> |
||||
|
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#include "tangerine.h" |
||||
|
||||
|
||||
bool |
||||
loadLights(RenderState* rs) |
||||
{ |
||||
LightsBuffer* lb = rs->lights_buf; |
||||
u32 idx = 0; |
||||
|
||||
#if 0 |
||||
// NOTE: add a directional light
|
||||
idx = (*lb->active_d_lights)++; |
||||
lb->dl_directions[idx] = glm::vec4(-2, 1, 3, 1); |
||||
lb->dl_colors[idx] = glm::vec4(0.5, 0.5, 0.3, 1); |
||||
lb->dl_intensities[idx] = glm::uvec4(1, 0, 0, 0); |
||||
#endif |
||||
// NOTE: add a point light
|
||||
idx = (*lb->active_p_lights)++; |
||||
lb->pl_positions[idx] = glm::vec4(-10, 0, -10, 1); |
||||
lb->pl_colors[idx] = glm::vec4(1, 1, 0.8, 1); |
||||
lb->pl_intensities[idx] = glm::vec4(3, 0, 0, 0); |
||||
|
||||
GLBuffer* lights_ubo = getUBOByName(rs->gl_ctx, "lights"); |
||||
assert(lights_ubo != nullptr); |
||||
updateGLBuffer(lights_ubo, lb->buffer); |
||||
|
||||
// NOTE: add a debug mesh to view the point light source
|
||||
RenderGroup* debug_group = getFreeRenderGroup(rs); |
||||
assert(debug_group); |
||||
ShaderProgram* s_debug = getShaderByName("debug", rs->gl_ctx); |
||||
assert(s_debug); |
||||
initRenderGroup(debug_group, rs->rg_arena, s_debug, 256, "debug_lights"); |
||||
Entity* e = getFreeEntity(debug_group); |
||||
assert(e); |
||||
if (!initEntity(e, |
||||
rs->gl_ctx, |
||||
rs->rg_arena, |
||||
&rs->assets.models[0], // tex_cube from loadScene()
|
||||
s_debug->num_vertex_attribs, |
||||
s_debug->attrib_mappings, |
||||
"debug_light")) |
||||
{ |
||||
LOGF(Error, "Error initializing debug entity for light\n"); |
||||
return false; |
||||
} |
||||
|
||||
setEntityPosition(e, glm::vec3(lb->pl_positions[idx])); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
loadCubes(RenderState* rs) |
||||
{ |
||||
// NOTE: load model
|
||||
Model* tex_cube = getModelByPath(&rs->assets, "../data/textured_cube.gltf"); |
||||
if (!tex_cube) return false; |
||||
|
||||
// NOTE: load new shader, or get one of the defaults
|
||||
// NOTE: the default shaders already have their attribute mappings created
|
||||
// in initRenderState, but if you use a custom shader, you will need to
|
||||
// create a GLBufferToAttribMapping manually
|
||||
ShaderProgram* shader_lit = getShaderByName("full_lighting", rs->gl_ctx); |
||||
if (!shader_lit) return false; |
||||
|
||||
// NOTE: init new render group
|
||||
RenderGroup* textured_cubes = getFreeRenderGroup(rs); |
||||
assert(textured_cubes); |
||||
initRenderGroup(textured_cubes, rs->rg_arena, shader_lit, 256, |
||||
"textured_cubes"); |
||||
|
||||
// NOTE: init entities
|
||||
const u32 NUM_CUBES = 5; |
||||
glm::vec3 cube_locs[NUM_CUBES] = { |
||||
glm::vec3( 0, 0, 0), |
||||
glm::vec3(-10, 10, 0), |
||||
glm::vec3(-10, -10, 0), |
||||
glm::vec3( 10, 10, 0), |
||||
glm::vec3( 10, -10, 0), |
||||
}; |
||||
|
||||
for (u32 i = 0; i < NUM_CUBES; i++) { |
||||
Entity* e = getFreeEntity(textured_cubes); |
||||
assert(e); |
||||
char cube_name[256] = {0}; |
||||
snprintf(cube_name, 256, "textured_cube%d", i); |
||||
|
||||
if (!initEntity(e, |
||||
rs->gl_ctx, |
||||
rs->rg_arena, |
||||
tex_cube, |
||||
shader_lit->num_vertex_attribs, |
||||
shader_lit->attrib_mappings, |
||||
cube_name)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
setEntityPosition(e, cube_locs[i]); |
||||
scaleEntity(e, 3); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
// NOTE: test an entity with multiple meshes
|
||||
bool |
||||
loadSpaceShip(RenderState* rs) |
||||
{ |
||||
Model* ship = getModelByPath(&rs->assets, "../data/spaceship.gltf"); |
||||
if (!ship) |
||||
return false; |
||||
|
||||
ShaderProgram* shader_lit = getShaderByName("full_lighting", rs->gl_ctx); |
||||
if (!shader_lit) |
||||
return false; |
||||
|
||||
// load model into gl
|
||||
RenderGroup* rg = getFreeRenderGroup(rs); |
||||
assert(rs); |
||||
initRenderGroup(rg, rs->rg_arena, shader_lit, 256, "ships"); |
||||
Entity* e = getFreeEntity(rg); |
||||
assert(e); |
||||
|
||||
if (initEntity(e, rs->gl_ctx, |
||||
rs->rg_arena, |
||||
ship, |
||||
shader_lit->num_vertex_attribs, |
||||
shader_lit->attrib_mappings, |
||||
"ship 01")) |
||||
{ |
||||
setEntityPosition(e, glm::vec3(0, -10, -15)); |
||||
} else { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
testColoredVertices(RenderState* rs) |
||||
{ |
||||
Mesh m = {0}; |
||||
m.num_vertices = 5; |
||||
m.num_indices = 12; |
||||
|
||||
glm::vec3 vertices[m.num_vertices] = { |
||||
{ 0, 1, 0 }, |
||||
{ -1, -1, -1 }, |
||||
{ -1, -1, 1 }, |
||||
{ 1, -1, 0.5 }, |
||||
}; |
||||
|
||||
u16 indices[m.num_indices] = { |
||||
0, 1, 2, |
||||
0, 2, 3, |
||||
0, 3, 1, |
||||
1, 2, 3 |
||||
}; |
||||
|
||||
glm::vec3 colors[m.num_vertices] = { |
||||
{ 1, 0, 0 }, |
||||
{ 0, 1, 0 }, |
||||
{ 0, 0, 1 }, |
||||
{ 1, 1, 0 } |
||||
}; |
||||
|
||||
m.vertices = vertices; |
||||
m.colors = colors; |
||||
m.indices = indices; |
||||
glm::mat4 xform = glm::mat4(1); |
||||
m.xform = &xform; |
||||
Model mdl = {0}; |
||||
mdl.num_meshes = 1; |
||||
mdl.meshes = &m; |
||||
|
||||
ShaderProgram* shader = getShaderByName("colored_vertices", rs->gl_ctx); |
||||
RenderGroup* rg = getFreeRenderGroup(rs); |
||||
assert(shader && rg); |
||||
initRenderGroup(rg, rs->rg_arena, shader, 256, "colored_pyramids"); |
||||
Entity* e = getFreeEntity(rg); |
||||
assert(e); |
||||
|
||||
if (initEntity(e, rs->gl_ctx, |
||||
rs->rg_arena, |
||||
&mdl, |
||||
shader->num_vertex_attribs, |
||||
shader->attrib_mappings, |
||||
"colored pyramid 01")) |
||||
{ |
||||
setEntityPosition(e, glm::vec3(0, -10, 15)); |
||||
} else { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
loadScene(RenderState* rs) |
||||
{ |
||||
if (!loadCubes(rs)) { |
||||
LOGF(Error, "Error loading cubes\n"); |
||||
return false; |
||||
} |
||||
|
||||
if (!testColoredVertices(rs)) { |
||||
LOGF(Error, "Error loading colored vertices\n"); |
||||
return false; |
||||
} |
||||
#if 1 |
||||
if (!loadSpaceShip(rs)) |
||||
return false; |
||||
#endif |
||||
|
||||
// NOTE: initilize the 'camera'
|
||||
glm::vec3 cam_pos = { 0, 15, 40 }; |
||||
glm::vec3 look_pos = { 0, 0, 0 }; |
||||
glm::vec3 up = { 0, 1, 0 }; |
||||
rs->xforms->view_xform = glm::lookAt(cam_pos, look_pos, up); |
||||
GLBuffer* xforms_ubo = getUBOByName(rs->gl_ctx, "matrices"); |
||||
assert(xforms_ubo); |
||||
updateGLBuffer(xforms_ubo, rs->xforms); |
||||
|
||||
return loadLights(rs); |
||||
} |
||||
|
||||
void |
||||
orbitPositionZ0(glm::vec4* pos, float angle) |
||||
{ |
||||
// get radius length
|
||||
float r = sqrt(pow(abs(pos->x), 2) + pow(abs(pos->z), 2)); |
||||
// get current angle about z axis
|
||||
float a = atan2f(pos->z, pos->x); |
||||
// apply increment
|
||||
a += angle; |
||||
// get new x/z components
|
||||
pos->x = r * cos(a); |
||||
pos->z = r * sin(a); |
||||
} |
||||
|
||||
void |
||||
render_cb_pre(RenderState* rs) |
||||
{ |
||||
SDL_Event e; |
||||
|
||||
while (SDL_PollEvent(&e)) { |
||||
if (e.type == SDL_QUIT || |
||||
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) |
||||
{ |
||||
rs->running = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// NOTE: orbit point light
|
||||
LightsBuffer* lb = rs->lights_buf; |
||||
orbitPositionZ0(&lb->pl_positions[0], 2 * M_PI / 180); |
||||
RenderGroup* rg = getRenderGroupByName(rs, "debug_lights"); |
||||
Entity* ent = &rg->entities[0]; |
||||
setEntityPosition(ent, glm::vec3(lb->pl_positions[0])); |
||||
|
||||
GLBuffer* lights_ubo = getUBOByName(rs->gl_ctx, "lights"); |
||||
assert(lights_ubo); |
||||
updateGLBuffer(lights_ubo, lb->buffer); |
||||
|
||||
// NOTE: orbit camera
|
||||
static glm::vec4 cam_pos; |
||||
static glm::vec3 look_pos; |
||||
static glm::vec3 up; |
||||
static bool initialized = false; |
||||
|
||||
if (!initialized) { |
||||
cam_pos = { 0, 15, 40, 1 }; |
||||
look_pos = { 0, 0, 0 }; |
||||
up = { 0, 1, 0 }; |
||||
initialized = true; |
||||
} |
||||
|
||||
orbitPositionZ0(&cam_pos, 0.5 * M_PI / 180); |
||||
rs->xforms->view_xform = |
||||
glm::lookAt(glm::vec3(cam_pos), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); |
||||
|
||||
static GLBuffer* xform_ubo = nullptr; |
||||
|
||||
if (!xform_ubo) { |
||||
xform_ubo = getUBOByName(rs->gl_ctx, "matrices"); |
||||
assert(xform_ubo != nullptr); |
||||
} |
||||
|
||||
updateGLBuffer(xform_ubo, rs->xforms); |
||||
|
||||
// NOTE: rotate cubes
|
||||
RenderGroup* rg2 = getRenderGroupByName(rs, "textured_cubes"); |
||||
|
||||
for (u32 j = 0; j < rg2->num_entities; j++) { |
||||
Entity& e = rg2->entities[j]; |
||||
float direction = (j % 2 == 0) ? 1 : -1; |
||||
rotateEntity(&e, |
||||
glm::vec3(0, 1, 0), |
||||
direction * (float) M_PI / (3 * 60)); |
||||
} |
||||
} |
||||
|
||||
int |
||||
main() |
||||
{ |
||||
RenderState* rs = initRenderState(); |
||||
|
||||
if (rs) { |
||||
if (!loadScene(rs)) { |
||||
LOGF(Error, "error loading scene\n"); |
||||
return 1; |
||||
} |
||||
|
||||
doRenderLoop(rs, 60, render_cb_pre, nullptr); |
||||
|
||||
freeRenderState(rs); |
||||
return 0; |
||||
} |
||||
|
||||
LOGF(Error, "error loading scene\n"); |
||||
return 1; |
||||
} |
||||
|
||||
@ -1,233 +0,0 @@
|
||||
|
||||
#include <cstdlib> |
||||
#include <ctime> |
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "asset.h" |
||||
#include "dumbLog.h" |
||||
#include "input.h" |
||||
#include "mesh.h" |
||||
#include "renderer.h" |
||||
|
||||
|
||||
simple_mesh* |
||||
makeSquareMesh() |
||||
{ |
||||
// NOTE: vertices arranged for GL_LINE_LOOP, non-indexed
|
||||
uint num_vertices = 4; |
||||
simple_mesh* sm = meInitMesh(num_vertices); |
||||
sm->num_vertices = num_vertices; |
||||
|
||||
sm->vertices[0] = glm::vec3(-1000, 0, 1000); |
||||
sm->vertices[1] = glm::vec3(-1000, 0, -1000); |
||||
sm->vertices[2] = glm::vec3(1000, 0, -1000); |
||||
sm->vertices[3] = glm::vec3(1000, 0, 1000); |
||||
sm->vert_colors[0] = glm::vec3(255, 0, 0); |
||||
sm->vert_colors[1] = glm::vec3(255, 0, 0); |
||||
sm->vert_colors[2] = glm::vec3(255, 0, 0); |
||||
sm->vert_colors[3] = glm::vec3(255, 0, 0); |
||||
|
||||
return sm; |
||||
} |
||||
|
||||
int |
||||
getRand(int lower = 0, int upper = RAND_MAX) |
||||
{ |
||||
// NOTE: only need to seed prng once
|
||||
static bool seeded = false; |
||||
|
||||
if (!seeded) { |
||||
srand(time(NULL)); |
||||
seeded = true; |
||||
} |
||||
|
||||
return (rand() % (upper - lower) + lower); |
||||
} |
||||
|
||||
bool |
||||
createModelEntities(render_group* rg, |
||||
const char* model_file, |
||||
uint item_count, |
||||
glm::vec3 min_pos, |
||||
glm::vec3 max_pos, |
||||
glm::vec3 scaling) |
||||
{ |
||||
#if 0 |
||||
for (uint i = 0; i < item_count; i++) { |
||||
entity& e = rg->entities[i]; |
||||
|
||||
if (!entInitModel(e, model_file)) { |
||||
LOG(Error) << "Error initializing entity, exiting\n"; |
||||
return false; |
||||
} |
||||
|
||||
entSetWorldPosition(e, |
||||
glm::vec3(getRand(min_pos.x, max_pos.x), |
||||
getRand(min_pos.y, max_pos.y), |
||||
getRand(min_pos.z, max_pos.z)) |
||||
); |
||||
entScale(e, glm::vec3(scaling.x, scaling.y, scaling.z)); |
||||
} |
||||
|
||||
return true; |
||||
#endif |
||||
return false; |
||||
} |
||||
|
||||
void |
||||
doFrameCallbackPre(render_state* rs) |
||||
{ |
||||
#if 0 |
||||
static input_state is = {}; |
||||
inputProcessEvents(&is); |
||||
|
||||
if (is.window_closed || is.escape) { |
||||
rs->running = false; |
||||
return; |
||||
} |
||||
|
||||
// NOTE: rotate meshes on z-axis every frame
|
||||
static float angle = 1.2 / 60; // NOTE: 60 FPS
|
||||
static glm::vec3 axis(0, 0, 1); |
||||
|
||||
for (uint i = 0; i < rs->render_groups->count; i++) { |
||||
render_group* rg = &rs->render_groups->groups[i]; |
||||
|
||||
for (uint j = 0; j < rg->count; j++) { |
||||
entity* e = &rg->entities[j]; |
||||
entRotate(e, angle, axis); |
||||
} |
||||
} |
||||
|
||||
point_light& l1 = rs->lights->lights[0]; |
||||
point_light& l2 = rs->lights->lights[1]; |
||||
entity& square = rs->render_groups[2]->entities[0]; |
||||
|
||||
l1.position = glm::vec3( |
||||
square.world_transform * glm::vec4(10000, 0, 1000, 1)); |
||||
l2.position = glm::vec3( |
||||
square.world_transform * glm::vec4(10000, 0, -1000, 1)); |
||||
rs->lights->needs_update = true; |
||||
#endif |
||||
static input_state is = {}; |
||||
inputProcessEvents(&is); |
||||
|
||||
if (is.window_closed || is.escape) { |
||||
rs->running = false; |
||||
return; |
||||
} |
||||
|
||||
static float angle = 1.2 / 60; // NOTE: 60 FPS
|
||||
static glm::vec3 axis(0, 1, 0); |
||||
entity* spaceship = &rs->render_groups->groups[0].entities[0]; |
||||
entRotate(spaceship, angle, axis); |
||||
} |
||||
|
||||
int |
||||
main() |
||||
{ |
||||
render_state* rs = renInit("render group example"); |
||||
|
||||
if (rs == nullptr) { |
||||
LOG(Error) << "Error Initialzing renderer\n"; |
||||
return 1; |
||||
} |
||||
|
||||
#if 0 |
||||
cameraInitPerspective( |
||||
rs->cam, |
||||
glm::vec3(0, -2000, 0), |
||||
glm::vec3(0, 0, 0), |
||||
glm::vec3(0,0,1) |
||||
); |
||||
|
||||
renAddLight(rs, glm::vec3()); |
||||
renAddLight(rs, glm::vec3()); |
||||
|
||||
// FIXME: this introduces a potential buffer overrun. Need to implement
|
||||
// a memory manager for render_groups, eg:
|
||||
// renPushGroup(rs, new_group)
|
||||
// rgPushEntity(rg, new_ent)
|
||||
rs->render_groups = UTIL_ALLOC(256, render_group*); |
||||
|
||||
// ship entities
|
||||
const uint item_count = 20; |
||||
// TODO: better usage would be: renPushEntity(rs->render_groups[0], e);
|
||||
// would need to allocate a reasonable block size by default (~64), and
|
||||
// double it if pushing to render_group would overflow
|
||||
shader_wrapper sw = { DEFAULT_SHADER, rs->default_shader, nullptr }; |
||||
rs->render_groups[0] = renAllocateGroup(item_count, sw); |
||||
rs->render_group_count++; |
||||
|
||||
bool ret = createModelEntities(rs->render_groups[0], |
||||
"../data/spaceship.glb", |
||||
item_count, |
||||
glm::vec3(-750, -750, -750), |
||||
glm::vec3(750, 750, 750), |
||||
glm::vec3(10, 10, 10) |
||||
); |
||||
assert(ret == true); |
||||
|
||||
// sphere entities
|
||||
// NOTE: can also re-use the default shader already defined above
|
||||
shader_wrapper sw2 = { DEFAULT_SHADER, rs->default_shader, nullptr }; |
||||
rs->render_groups[1] = renAllocateGroup(item_count, sw2); |
||||
rs->render_group_count++; |
||||
ret = createModelEntities(rs->render_groups[1], |
||||
"../data/icosphere.glb", |
||||
item_count, |
||||
glm::vec3(-750, -750, -750), |
||||
glm::vec3(750, 750, 750), |
||||
glm::vec3(100, 100, 100) |
||||
); |
||||
assert(ret == true); |
||||
|
||||
// simple_mesh/shader
|
||||
shader_wrapper sw3 = { SIMPLE_SHADER, nullptr, rs->simple_shader }; |
||||
rs->render_groups[2] = renAllocateGroup(1, sw3); |
||||
rs->render_group_count++; |
||||
|
||||
entity& square = rs->render_groups[2]->entities[0]; |
||||
simple_mesh* sm = makeSquareMesh(); |
||||
entInitMesh(square, sm, GL_LINE_LOOP); |
||||
|
||||
renDoRenderLoop(rs, 60, doFrameCallbackPre); |
||||
#endif |
||||
// NOTE: testing entity system with new asset structures
|
||||
|
||||
cameraInitPerspective( |
||||
rs->cam, |
||||
glm::vec3(0, 50, -100), |
||||
glm::vec3(0, 0, 0), |
||||
glm::vec3(0,1,0) |
||||
); |
||||
|
||||
shader_wrapper sw = { DEFAULT_SHADER, rs->default_shader, nullptr }; |
||||
render_group* rg = rgAlloc(rs->render_groups, 64, sw); |
||||
|
||||
#if 1 |
||||
entity* e = |
||||
rgAppend(rg, rs->assets, rs->textures, rs->arena, |
||||
"../data/blender/spaceship.gltf"); |
||||
uint scale = 4; |
||||
//entRotate(e, -1 * M_PI_2, glm::vec3(1, 0, 0));
|
||||
#else |
||||
entity* e = |
||||
rgAppend(rg, rs->assets, rs->textures, rs->arena, |
||||
"../data/blender/icosphere.gltf"); |
||||
uint scale = 20; |
||||
#endif |
||||
if (e != nullptr) { |
||||
entScale(*e, glm::vec3(scale, scale, scale)); |
||||
|
||||
//renDoRenderLoop(rs, 60);
|
||||
renDoRenderLoop(rs, 60, doFrameCallbackPre); |
||||
} else { |
||||
LOG(Error) << "failed to load entity\n"; |
||||
} |
||||
|
||||
renShutdown(rs); |
||||
return 0; |
||||
} |
||||
|
||||
@ -1,89 +0,0 @@
|
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "dumbLog.h" |
||||
#include "input.h" |
||||
#include "mesh.h" |
||||
#include "renderer.h" |
||||
|
||||
|
||||
void |
||||
doFrameCallbackPre(render_state* rs) |
||||
{ |
||||
static input_state is = {}; |
||||
inputProcessEvents(&is); |
||||
|
||||
if (is.window_closed || is.escape) { |
||||
rs->running = false; |
||||
return; |
||||
} |
||||
|
||||
int rotate_mod = 0; |
||||
|
||||
if (is.left) |
||||
rotate_mod = -1; |
||||
if (is.right) |
||||
rotate_mod = 1; |
||||
|
||||
// NOTE: rotate mesh on z-axis every frame
|
||||
entity& e = rs->render_groups[0]->entities[0]; |
||||
static float angle = (float) M_PI_2 / 33; |
||||
static glm::vec3 axis(0, 0, 1); |
||||
entRotate(e, angle * rotate_mod, axis); |
||||
} |
||||
|
||||
simple_mesh* |
||||
makeSquareMesh() |
||||
{ |
||||
uint num_vertices = 4; |
||||
simple_mesh* sm = meInitMesh(num_vertices); |
||||
sm->num_vertices = num_vertices; |
||||
|
||||
sm->vertices[0] = glm::vec3(-200, 0, 200); |
||||
sm->vertices[1] = glm::vec3(-200, 0, -200); |
||||
sm->vertices[2] = glm::vec3(200, 0, -200); |
||||
sm->vertices[3] = glm::vec3(200, 0, 200); |
||||
sm->vert_colors[0] = glm::vec3(255, 0, 0); |
||||
sm->vert_colors[1] = glm::vec3(255, 0, 0); |
||||
sm->vert_colors[2] = glm::vec3(255, 0, 0); |
||||
sm->vert_colors[3] = glm::vec3(255, 0, 0); |
||||
|
||||
return sm; |
||||
} |
||||
|
||||
int |
||||
main() |
||||
{ |
||||
render_state* rs = renInit("simple mesh"); |
||||
rs->render_groups = UTIL_ALLOC(256, render_group*); |
||||
|
||||
if (rs == nullptr) { |
||||
LOG(Error) << "Error Initialzing renderer\n"; |
||||
return 1; |
||||
} |
||||
|
||||
// TODO: this needs to be more convenient
|
||||
shader_wrapper sw = { SIMPLE_SHADER, nullptr, rs->simple_shader }; |
||||
rs->render_groups[0] = renAllocateGroup(1, sw); |
||||
rs->render_group_count = 1; |
||||
|
||||
entity& e = rs->render_groups[0]->entities[0]; |
||||
simple_mesh* sm = makeSquareMesh(); |
||||
|
||||
// TODO: better usage would be: renPushEntity(rs->render_groups[0], e);
|
||||
// would need to allocate a reasonable block size by default (~64), and
|
||||
// double it if pushing to render_group would overflow
|
||||
entInitMesh(e, sm, GL_LINE_LOOP); |
||||
|
||||
cameraInitPerspective( |
||||
rs->cam, |
||||
glm::vec3(0, -500, 0), |
||||
glm::vec3(0, 0, 0), |
||||
glm::vec3(0,0,1) |
||||
); |
||||
|
||||
renDoRenderLoop(rs, 60, doFrameCallbackPre); |
||||
|
||||
renShutdown(rs); |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,147 @@
|
||||
|
||||
// NOTE: get useful debug messages from opengl
|
||||
// https://www.khronos.org/opengl/wiki/Debug_Output
|
||||
|
||||
#ifndef GL_DEBUG_H |
||||
#define GL_DEBUG_H |
||||
|
||||
#include <GL/glew.h> |
||||
|
||||
#include "shader.h" |
||||
|
||||
|
||||
void dumpShader(GLuint prog_id); |
||||
|
||||
const char* glEnumToString(GLenum e); |
||||
|
||||
void openglDebugCallback(GLenum source, |
||||
GLenum type, |
||||
GLuint id, |
||||
GLenum severity, |
||||
GLsizei length, |
||||
const GLchar* message, |
||||
const void* userParam); |
||||
|
||||
#endif // GL_DEBUG_H
|
||||
|
||||
|
||||
#ifdef GL_DEBUG_IMPLEMENTATION |
||||
|
||||
const char* |
||||
glEnumToString(GLenum e) |
||||
{ |
||||
switch (e) { |
||||
case GL_FLOAT_MAT4: return "GL_FLOAT_MAT4"; |
||||
case GL_FLOAT_VEC3: return "GL_FLOAT_VEC3"; |
||||
case GL_FLOAT_VEC4: return "GL_FLOAT_VEC4"; |
||||
|
||||
case GL_BYTE: return "GL_BYTE"; |
||||
case GL_UNSIGNED_BYTE: return "GL_UNSIGNED_BYTE"; |
||||
case GL_SHORT: return "GL_SHORT"; |
||||
case GL_UNSIGNED_SHORT: return "GL_UNSIGNED_SHORT"; |
||||
case GL_INT: return "GL_INT"; |
||||
case GL_UNSIGNED_INT: return "GL_UNSIGNED_INT"; |
||||
case GL_FLOAT: return "GL_FLOAT"; |
||||
case GL_DOUBLE: return "GL_DOUBLE"; |
||||
|
||||
case GL_ARRAY_BUFFER: return "GL_ARRAY_BUFFER"; |
||||
case GL_ELEMENT_ARRAY_BUFFER: return "GL_ELEMENT_ARRAY_BUFFER"; |
||||
case GL_UNIFORM_BUFFER: return "GL_UNIFORM_BUFFER"; |
||||
case GL_TEXTURE_BUFFER: return "GL_TEXTURE_BUFFER"; |
||||
|
||||
case GL_DEBUG_TYPE_ERROR: return "GL_DEBUG_TYPE_ERROR"; |
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: |
||||
return "GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR"; |
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: |
||||
return "GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR"; |
||||
case GL_DEBUG_TYPE_PORTABILITY: return "GL_DEBUG_TYPE_PORTABILITY"; |
||||
case GL_DEBUG_TYPE_PERFORMANCE: return "GL_DEBUG_TYPE_PERFORMANCE"; |
||||
case GL_DEBUG_TYPE_MARKER: return "GL_DEBUG_TYPE_MARKER"; |
||||
case GL_DEBUG_TYPE_PUSH_GROUP: return "GL_DEBUG_TYPE_PUSH_GROUP"; |
||||
case GL_DEBUG_TYPE_POP_GROUP: return "GL_DEBUG_TYPE_POP_GROUP"; |
||||
case GL_DEBUG_TYPE_OTHER: return "GL_DEBUG_TYPE_OTHER"; |
||||
|
||||
case GL_DEBUG_SEVERITY_HIGH: return "GL_DEBUG_SEVERITY_HIGH"; |
||||
case GL_DEBUG_SEVERITY_MEDIUM: return "GL_DEBUG_SEVERITY_MEDIUM"; |
||||
case GL_DEBUG_SEVERITY_LOW: return "GL_DEBUG_SEVERITY_LOW"; |
||||
case GL_DEBUG_SEVERITY_NOTIFICATION: |
||||
return "GL_DEBUG_SEVERITY_NOTIFICATION"; |
||||
|
||||
case GL_NO_ERROR: return "GL_NO_ERROR"; |
||||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; |
||||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; |
||||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; |
||||
case GL_INVALID_FRAMEBUFFER_OPERATION: |
||||
return "GL_INVALID_FRAMEBUFFER_OPERATION"; |
||||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; |
||||
|
||||
default: return "???"; |
||||
} |
||||
} |
||||
|
||||
void |
||||
openglDebugCallback(GLenum source, |
||||
GLenum type, |
||||
GLuint id, |
||||
GLenum severity, |
||||
GLsizei length, |
||||
const GLchar* message, |
||||
const void* userParam) |
||||
{ |
||||
// NOTE: filter out notification about using video memory for buffer object
|
||||
if (id == 131185) |
||||
return; |
||||
|
||||
std::cout << "message id: " << id << ", " |
||||
<< ((type == GL_DEBUG_TYPE_ERROR) ? "Error" : "Debug") |
||||
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "") |
||||
<< ", type: " << glEnumToString(type) |
||||
<< ", severity: " << glEnumToString(severity) |
||||
<< ", message: " << message << "\n"; |
||||
} |
||||
|
||||
void |
||||
dumpShader(GLuint prog_id) |
||||
{ |
||||
LOGF(Debug, "------------------------\n"); |
||||
LOGF(Debug, "%s(), dumping shader info, program id: %d\n", |
||||
__FUNCTION__, prog_id); |
||||
|
||||
GLint active_uniforms; |
||||
GLint active_uniform_blocks; |
||||
GLint active_attribs; |
||||
|
||||
// NOTE: unused uniforms/attributes get optimized away
|
||||
// https://www.khronos.org/opengl/wiki/Program_Introspection#Attributes
|
||||
glGetProgramiv(prog_id, GL_ACTIVE_UNIFORMS, &active_uniforms); |
||||
glGetProgramiv(prog_id, GL_ACTIVE_UNIFORM_BLOCKS, &active_uniform_blocks); |
||||
glGetProgramiv(prog_id, GL_ACTIVE_ATTRIBUTES, &active_attribs); |
||||
|
||||
LOGF(Debug, "active uniforms: %d\n", active_uniforms); |
||||
LOGF(Debug, "active uniform blocks: %d\n", active_uniform_blocks); |
||||
LOGF(Debug, "active attributes: %d\n", active_attribs); |
||||
|
||||
GLchar uni_name[256]; |
||||
GLsizei length; |
||||
GLint size; |
||||
GLenum type; |
||||
|
||||
for (int i = 0; i < active_uniforms; i++) { |
||||
glGetActiveUniform(prog_id, i, sizeof(uni_name), |
||||
&length, &size, &type, uni_name); |
||||
LOGF(Debug, "uniform idx: %d, type: %s, name: %s \n", |
||||
i, glEnumToString(type), uni_name); |
||||
} |
||||
|
||||
for (int i = 0; i < active_attribs; i++) { |
||||
glGetActiveAttrib(prog_id, i, sizeof(uni_name), |
||||
&length, &size, &type, uni_name); |
||||
LOGF(Debug, "attribute idx: %d, type: %s, name: %s \n", |
||||
i, glEnumToString(type), uni_name); |
||||
} |
||||
|
||||
LOGF(Debug, "------------------------\n"); |
||||
} |
||||
|
||||
#endif // ifdef GL_DEBUG_IMPLEMENTATION
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "util.h" |
||||
|
||||
|
||||
struct render_object; |
||||
|
||||
struct node_animation |
||||
{ |
||||
render_object* ro; |
||||
glm::mat4* xform_array; |
||||
uint key_count; |
||||
uint cur_frame; |
||||
}; |
||||
|
||||
@ -1,66 +0,0 @@
|
||||
|
||||
#pragma once |
||||
#include <glm/glm.hpp> |
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
#include "util.h" |
||||
|
||||
|
||||
struct camera |
||||
{ |
||||
float hAngle; |
||||
float vAngle; |
||||
|
||||
glm::vec3 position; |
||||
glm::vec3 forward; |
||||
glm::vec3 up; |
||||
glm::vec3 left; |
||||
glm::vec3 target; |
||||
glm::vec3 world_up; |
||||
|
||||
glm::mat4 model; |
||||
glm::mat4 view; |
||||
glm::mat4 projection; |
||||
glm::mat4 MVP; |
||||
}; |
||||
|
||||
enum projection_type |
||||
{ |
||||
PERSPECTIVE, |
||||
ORTHOGRAPHIC, |
||||
}; |
||||
|
||||
|
||||
v2f |
||||
cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height); |
||||
|
||||
v3f |
||||
cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims); |
||||
|
||||
bool |
||||
cameraIntersectPlane(camera& cam, |
||||
v3f ray, |
||||
v3f plane_origin, |
||||
v3f plane_normal, |
||||
v3f& intersection); |
||||
|
||||
void |
||||
cameraInitPerspective(camera* cam, |
||||
glm::vec3 position, |
||||
glm::vec3 target, |
||||
glm::vec3 world_up, |
||||
float aspect_ratio = 16.f / 9.f); |
||||
|
||||
void |
||||
cameraMove(camera& cam, |
||||
bool up, |
||||
bool left, |
||||
bool down, |
||||
bool right, |
||||
bool forward, |
||||
bool backward); |
||||
|
||||
void |
||||
cameraRotate(camera& cam, int32 xrel, int32 yrel); |
||||
|
||||
void |
||||
cameraRoll(camera& cam, bool CW, bool CCW); |
||||
@ -0,0 +1,30 @@
|
||||
|
||||
#pragma once |
||||
|
||||
|
||||
const char* DUMMY_VERTEX_SHADER = R"VS( |
||||
#version 330 core |
||||
layout (location = 0) in vec3 position; |
||||
|
||||
uniform mat4 world_transform; |
||||
uniform mat4 MVP; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
gl_Position = MVP * world_transform * vec4(position, 1); |
||||
} |
||||
)VS"; |
||||
|
||||
const char* DUMMY_FRAGMENT_SHADER = R"FS( |
||||
#version 330 core |
||||
|
||||
out vec4 color; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
color = vec4(1, 0, 0, 1); |
||||
} |
||||
)FS"; |
||||
|
||||
@ -1,30 +1,32 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "asset.h" |
||||
#include "render_object.h" |
||||
#include "shader.h" |
||||
#include "types.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
struct entity |
||||
struct Entity |
||||
{ |
||||
glm::mat4 world_transform; |
||||
uint64_t model_id; // NOTE: filepath hash
|
||||
render_objects* render_objs; |
||||
u32 num_meshes; |
||||
GLMesh* meshes; |
||||
GLTexture* diffuse_texture; // NOTE: pointer into gl_ctx->textures array
|
||||
glm::mat4* model_xform; |
||||
char* name; |
||||
}; |
||||
|
||||
bool entInitModel(entity* e, model* mdl); |
||||
|
||||
// FIXME: might as well stay consistent and make all these pointers
|
||||
void entFree(entity* e); |
||||
|
||||
void entSetWorldPosition(entity& e, glm::vec3 v); |
||||
|
||||
void entTranslate(entity& e, glm::vec3 v); |
||||
bool initEntity(Entity* e, |
||||
GLContext* gl_ctx, |
||||
MemoryArena* arena, |
||||
Model* mdl, |
||||
u32 num_attrib_mappings, |
||||
GLBufferToAttribMapping* attrib_mappings, |
||||
const char* name = ""); |
||||
|
||||
void entScale(entity& e, glm::vec3 v); |
||||
void setEntityPosition(Entity* e, glm::vec3 pos); |
||||
|
||||
void entRotate(entity* e, float angle, glm::vec3 axis); |
||||
void rotateEntity(Entity* e, glm::vec3 axis, float radians); |
||||
|
||||
void scaleEntity(Entity* e, float scale); |
||||
|
||||
@ -1,23 +0,0 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <SDL2/SDL.h> |
||||
|
||||
|
||||
struct input_state |
||||
{ |
||||
bool window_closed; |
||||
bool escape; |
||||
bool left; |
||||
bool right; |
||||
bool up; |
||||
bool down; |
||||
}; |
||||
|
||||
void |
||||
inputProcessEvent(input_state* is, SDL_Event& e); |
||||
|
||||
// NOTE: convenience function that provides a while(SDL_PollEvents()) loop
|
||||
void |
||||
inputProcessEvents(input_state* is); |
||||
|
||||
@ -1,34 +0,0 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "shader_program.h" |
||||
#include "util.h" |
||||
|
||||
struct point_light |
||||
{ |
||||
uint light_ID; |
||||
glm::vec3 position; |
||||
glm::vec3 color; |
||||
float intensity; |
||||
}; |
||||
|
||||
struct light_group |
||||
{ |
||||
point_light* lights; |
||||
uint num_lights; |
||||
uint max_lights; |
||||
bool needs_update; |
||||
}; |
||||
|
||||
// NOTE: max_lights should match the max_lights variable in the fragment shader
|
||||
light_group* lightsInit(uint max_lights = 1000); |
||||
|
||||
void lightsOut(light_group* lights); |
||||
|
||||
bool |
||||
lightsAdd(light_group* lights, glm::vec3 pos, glm::vec3 color, float intensity); |
||||
|
||||
void lightsUpdate(light_group* lights, default_shader_program* shader); |
||||
|
||||
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* mesh.h |
||||
* - wrapper for assimp http://www.assimp.org
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "animation.h" |
||||
#include "util.h" |
||||
#include "util_image.h" |
||||
|
||||
|
||||
struct mesh_info |
||||
{ |
||||
glm::mat4 model_transform; |
||||
// NOTE: vertices, normals, and tex_coords have the same count
|
||||
uint num_vertices; |
||||
glm::vec3* vertices; |
||||
glm::vec3* normals; |
||||
glm::vec3* texture_coords; // NOTE: stay aligned with other buffers
|
||||
uint num_indices; |
||||
uint* indices; |
||||
// FIXME: WTF? why are we storing a copy of the texture on each mesh_info
|
||||
// instead of once per mesh_group?
|
||||
util_image diffuse_texture; |
||||
node_animation* node_anim; |
||||
}; |
||||
|
||||
struct mesh_group |
||||
{ |
||||
// TODO: this should be one big allocation since the mesh doesn't change
|
||||
// while running
|
||||
mesh_info** meshes; |
||||
uint num_meshes; |
||||
}; |
||||
|
||||
struct simple_mesh |
||||
{ |
||||
glm::mat4 model_transform; |
||||
uint num_vertices; |
||||
glm::vec3* vertices; |
||||
glm::vec3* vert_colors; |
||||
}; |
||||
|
||||
|
||||
// NOTE: meshes loaded from assimp require texture coordinates, and a diffuse
|
||||
// texture, which can be a seperate file, or embedded in the input file
|
||||
bool |
||||
meLoadFromFile(mesh_group& mesh_group, const char* filepath); |
||||
|
||||
simple_mesh* |
||||
meInitMesh(uint num_vertices); |
||||
|
||||
void |
||||
meFreeMeshGroup(mesh_group& mesh_group); |
||||
|
||||
void |
||||
meFreeSimpleMesh(simple_mesh* mesh); |
||||
|
||||
void |
||||
meShutdownAssimp(); |
||||
|
||||
@ -1,102 +0,0 @@
|
||||
|
||||
// attempt to fix vsync with opengl on windows
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=467617
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmthk/nf-d3dkmthk-d3dkmtwaitforverticalblankevent
|
||||
// NOTE: requires installing windows driver kit addon for msvc
|
||||
// TODO: not working with hdc provided by SDL, try enumerating display devices as described here:
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmthk/nf-d3dkmthk-d3dkmtopenadapterfromhdc
|
||||
// NOTE: the above seems to make some difference in lowering cpu usage, but cpu stays at max frequency even when relatively idle.
|
||||
|
||||
// TODO: if d3dkmt... doensn't work, try DWMFlush() https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmflush
|
||||
// glfw uses this https://github.com/glfw/glfw/blob/master/src/wgl_context.c
|
||||
//
|
||||
// if that doesn't work... https://docs.microsoft.com/en-us/windows/desktop/api/timeapi/nf-timeapi-timebeginperiod
|
||||
// to increase schedular granularity. can then use sleep(1) for 1ms resolution
|
||||
|
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include "SDL_syswm.h" |
||||
|
||||
#if defined(_WIN32) |
||||
#include "windows.h" |
||||
#include "d3dkmthk.h" |
||||
|
||||
typedef uint32_t D3DKMT_HANDLE; |
||||
typedef struct { |
||||
D3DKMT_HANDLE hAdapter = NULL; |
||||
UINT vidID = 0; |
||||
} platform_win_device_handles; |
||||
platform_win_device_handles g_platform_win_device_handles; |
||||
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
inline bool |
||||
platform_init(SDL_Window* window) |
||||
{ |
||||
#if defined(_WIN32) |
||||
D3DKMT_HANDLE hAdapter = NULL; |
||||
UINT vidID = 0; |
||||
D3DKMT_OPENADAPTERFROMHDC OpenAdapterData; |
||||
DISPLAY_DEVICE dd; |
||||
HDC hdc; |
||||
|
||||
memset(&dd, 0, sizeof(dd)); |
||||
dd.cb = sizeof dd; |
||||
|
||||
for (int i = 0; EnumDisplayDevicesA(NULL, i, &dd, 0); ++i) { |
||||
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) |
||||
break; |
||||
} |
||||
|
||||
hdc = CreateDC(NULL, dd.DeviceName, NULL, NULL); |
||||
if (hdc == NULL) |
||||
return false; |
||||
|
||||
OpenAdapterData.hDc = hdc; |
||||
if ((D3DKMTOpenAdapterFromHdc(&OpenAdapterData)) >= 0) { |
||||
DeleteDC(hdc); |
||||
g_platform_win_device_handles.hAdapter = OpenAdapterData.hAdapter; |
||||
g_platform_win_device_handles.vidID = OpenAdapterData.VidPnSourceId; |
||||
return true; |
||||
} |
||||
|
||||
DeleteDC(hdc); |
||||
return false; |
||||
#endif // _WIN32
|
||||
|
||||
return true; |
||||
} |
||||
|
||||
inline void |
||||
platform_wait_for_vblank(bool wait=true) |
||||
{ |
||||
if (!wait) return; |
||||
|
||||
#if defined(_WIN32) |
||||
_D3DKMT_WAITFORVERTICALBLANKEVENT waitForVBlankData; |
||||
memset(&waitForVBlankData, 0, sizeof(waitForVBlankData)); |
||||
waitForVBlankData.hAdapter = g_platform_win_device_handles.hAdapter; |
||||
waitForVBlankData.VidPnSourceId = g_platform_win_device_handles.vidID; |
||||
NTSTATUS ret = D3DKMTWaitForVerticalBlankEvent(&waitForVBlankData); |
||||
|
||||
switch (ret) { |
||||
case 0x00000000: // STATUS_SUCCESS
|
||||
//OutputDebugString("Success\r\n");
|
||||
break; |
||||
//case STATUS_DEVICE_REMOVED:
|
||||
// OutputDebugString("STATUS_DEVICE_REMOVED\r\n");
|
||||
// break;
|
||||
case STATUS_INVALID_PARAMETER: |
||||
OutputDebugString("STATUS_INVALID_PARAMETER\r\n"); |
||||
break; |
||||
default: |
||||
OutputDebugString("????"); |
||||
} |
||||
#endif // _WIN32
|
||||
|
||||
// TODO: implement/test for linux
|
||||
} |
||||
|
||||
@ -1,27 +0,0 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <GL/glew.h> |
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "asset.h" |
||||
#include "camera.h" |
||||
#include "lights.h" |
||||
#include "shader_program.h" |
||||
|
||||
|
||||
struct render_objects; |
||||
|
||||
render_objects* |
||||
roInitModel(model* mdl); |
||||
|
||||
void |
||||
roFree(render_objects* r_objs); |
||||
|
||||
void |
||||
roDraw(render_objects* r_ojbs, |
||||
glm::mat4 world_transform, |
||||
camera* cam, |
||||
shader_wrapper sw, |
||||
light_group* lights); |
||||
|
||||
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* libTangerine, a small, modern openGL renderer specializing in flat shaded, |
||||
* solid color models using pallete textures. See README for build instructions |
||||
* and look in the examples folder for... examples |
||||
* |
||||
* fixmes) |
||||
* FIXME: load textures into texture asset array, and map in model structure |
||||
* FIXME: better shader abstraction so we can store a list of shaders on |
||||
* render_state |
||||
* FIXME: clean up examples with new asset system |
||||
* FIXME: remove mesh.h, mesh.cpp |
||||
* FIXME: merge render_group_fix branch back into master |
||||
* FIXME: remove platform_wait_for_vblank |
||||
* FIXME: make a test case for overflowing default array sizes, rg->assets |
||||
* rg_info->groups, render_group->enitities, rg->textures |
||||
* FIXME: look for glm::mat4 in structs, and replace with pointers |
||||
* |
||||
* todos) |
||||
* TODO: make assetInit* functions generic |
||||
* TODO: make initModel/initTexture functions generic |
||||
* TODO: resizable arrays for entities and asset system |
||||
* TODO: defaults include for various #defines |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#if defined (_WIN32) |
||||
#include <SDL.h> |
||||
#else |
||||
#include <SDL2/SDL.h> |
||||
#endif |
||||
#include <GL/glew.h> |
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "asset.h" |
||||
#include "camera.h" |
||||
#include "entity.h" |
||||
#include "lights.h" |
||||
#include "util.h" |
||||
#include "shader_program.h" |
||||
|
||||
|
||||
// NOTE: array of entities rendered with the same shader program
|
||||
struct render_group |
||||
{ |
||||
// TODO: also needs to be resizable
|
||||
entity* entities; |
||||
uint count; |
||||
uint max_size; |
||||
shader_wrapper shader; |
||||
}; |
||||
|
||||
struct rg_info |
||||
{ |
||||
render_group* groups; |
||||
uint count; |
||||
uint max_size; |
||||
}; |
||||
|
||||
render_group* |
||||
rgAlloc(rg_info* rgi, uint num_entites, shader_wrapper shader); |
||||
|
||||
entity* |
||||
rgAppend(render_group* rg, |
||||
model_assets* assets, |
||||
texture_assets* textures, |
||||
memory_arena* arena, |
||||
const char* model_path); |
||||
|
||||
struct SDL_Handles |
||||
{ |
||||
SDL_Window *window; |
||||
SDL_GLContext glContext; |
||||
SDL_DisplayMode currentDisplayMode; |
||||
}; |
||||
|
||||
#define DEFAULT_RG_COUNT 256 |
||||
struct render_state |
||||
{ |
||||
glm::vec2 viewport_dims; |
||||
camera* cam; |
||||
util_RGBA clear_col; |
||||
SDL_Handles* handles; |
||||
bool running; |
||||
|
||||
rg_info* render_groups; |
||||
|
||||
memory_arena* arena; |
||||
model_assets* assets; |
||||
texture_assets* textures; |
||||
|
||||
// TODO: hide shaders behind a better abstraction than 'shader_wrapper'
|
||||
default_shader_program* default_shader; |
||||
simple_shader_program* simple_shader; |
||||
|
||||
light_group* lights; |
||||
}; |
||||
|
||||
#define DEFAULT_ASSET_SIZE 256 |
||||
render_state* |
||||
renInit(const char* title = "Tangerine", |
||||
glm::vec2 viewport_dims = glm::vec2(1280, 720), |
||||
Uint32 SDL_init_flags = 0, |
||||
size_t arena_size = DEFAULT_ARENA_SIZE, |
||||
uint asset_size = DEFAULT_ASSET_SIZE); |
||||
|
||||
void renShutdown(render_state* rs); |
||||
|
||||
// NOTE: callback function signature to use with renDoRenderLoop()
|
||||
typedef void (*frame_callback_fn) (render_state*); |
||||
|
||||
// NOTE: There are 2 callbacks to use here, cb_func_pre is called before
|
||||
// the call to renRenderFrame(), cb_fun_post is called after
|
||||
// NOTE: if you use cb_func_pre, you will have to use SDL_PollEvent() manually
|
||||
// and at minimum set rs->running = false on SDL_QUIT event
|
||||
void |
||||
renDoRenderLoop(render_state* rs, |
||||
uint framerate = 60, |
||||
frame_callback_fn cb_func_pre = nullptr, |
||||
frame_callback_fn cb_func_post = nullptr); |
||||
|
||||
void renRenderFrame(render_state* rs); |
||||
|
||||
bool |
||||
renAddLight(render_state* rs, |
||||
glm::vec3 pos = glm::vec3(0, 0, 0), |
||||
glm::vec3 color = glm::vec3(1, 1, 1), |
||||
float intensity = 1.0); |
||||
|
||||
glm::vec2 |
||||
renGetWindowDims(render_state* rs); |
||||
|
||||
@ -0,0 +1,222 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <SDL2/SDL.h> |
||||
#include <GL/glew.h> |
||||
#include <glm/glm.hpp> |
||||
|
||||
#include "types.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
enum UniformType |
||||
{ |
||||
UNIFORM_SAMPLER, |
||||
UNIFORM_NODE_XFORM, |
||||
UNIFORM_VIEW_XFORM, |
||||
UNIFORM_PROJECTION_XFORM, |
||||
UNIFORM_NORMAL_XFORM, |
||||
UNIFORM_BLOCK_XFORMS, |
||||
UNIFORM_BLOCK_LIGHTS, |
||||
UNIFORM_UNKNOWN, |
||||
UNIFORM_TYPE_COUNT |
||||
}; |
||||
|
||||
struct GLUniform |
||||
{ |
||||
// NOTE: would be nice to use idx as the location parameter because it seems
|
||||
// to match when the uniform isn't part of a uniform block, at least with
|
||||
// intel mesa driver, but apparently that's not guarantied
|
||||
GLuint idx; |
||||
GLint location; |
||||
GLint block_idx; |
||||
UniformType uniform_type; |
||||
GLenum gl_type; // NOTE: GL_UNSIGNED_INT, GL_FLOAT_VEC4
|
||||
GLint num_elements; // NOTE: 1 unless uniform is an array of base types
|
||||
GLint array_stride; // NOTE: bytes between array elements
|
||||
GLint uniform_offset; // NOTE: byte offset from beginning of uniform
|
||||
char* name; |
||||
}; |
||||
|
||||
struct GLUniformBlock |
||||
{ |
||||
GLuint block_id; |
||||
GLint binding_idx; |
||||
UniformType uniform_type; |
||||
GLuint num_uniforms; |
||||
GLUniform* uniforms; |
||||
char* name; |
||||
}; |
||||
|
||||
enum MeshBufferType |
||||
{ |
||||
VERTEX, |
||||
NORMAL, |
||||
UV, |
||||
COLOR, |
||||
MESH_BUFFER_TYPE_COUNT |
||||
}; |
||||
|
||||
// NOTE: we need another struct for vertex attributes that mirrors GLBuffer
|
||||
// because an attribute is associated with a ShaderProgram while a buffer is
|
||||
// associated with the vertex data passed to glBufferData()
|
||||
struct GLVertexAttrib |
||||
{ |
||||
MeshBufferType buf_type; |
||||
GLenum data_type; |
||||
GLenum component_type; |
||||
u32 num_components; |
||||
GLuint location; |
||||
char* name; |
||||
}; |
||||
|
||||
// TODO: add a note explaining usage when we implement complex entities
|
||||
struct GLBufferToAttribMapping |
||||
{ |
||||
GLVertexAttrib* attrib; |
||||
MeshBufferType buf_type; |
||||
}; |
||||
|
||||
struct ShaderProgram |
||||
{ |
||||
GLuint prog_id; |
||||
|
||||
u32 num_blocks; |
||||
GLUniformBlock* uniform_blocks; |
||||
|
||||
u32 num_uniforms; |
||||
GLUniform* uniforms; |
||||
|
||||
u32 num_vertex_attribs; |
||||
GLVertexAttrib* vertex_attribs; |
||||
GLBufferToAttribMapping* attrib_mappings; |
||||
|
||||
char* name; |
||||
u64 hash; // NOTE: hash of vs filpath + fs filepath concat
|
||||
}; |
||||
|
||||
struct GLBuffer |
||||
{ |
||||
GLuint id; |
||||
GLenum target; |
||||
GLenum data_type; |
||||
GLuint data_size; // NOTE: size of buffer in bytes
|
||||
GLint location; // NOTE: if used as backing for vertex attribute
|
||||
GLint binding_idx; // NOTE: if used as backing from uniform buffer object
|
||||
char* name; |
||||
}; |
||||
|
||||
struct GLTexture |
||||
{ |
||||
GLuint id; |
||||
GLenum pixel_format; // NOTE: GL_RGB or GL_RGBA
|
||||
u32 width; |
||||
u32 height; |
||||
u64 filepath_hash; |
||||
}; |
||||
|
||||
struct GLContext |
||||
{ |
||||
GLuint binding_count; |
||||
GLint max_binding_points; |
||||
GLint max_vertex_blocks; |
||||
GLint max_fragment_blocks; |
||||
GLint max_ublock_size; |
||||
GLint max_vertex_attribs; |
||||
|
||||
u32 max_ubos; |
||||
u32 num_ubos; |
||||
GLBuffer* uniform_buffers; |
||||
|
||||
u32 max_shaders; |
||||
u32 num_shaders; |
||||
ShaderProgram* shaders; |
||||
|
||||
u32 max_textures; |
||||
u32 num_textures; |
||||
GLTexture* textures; |
||||
}; |
||||
|
||||
struct GLMesh |
||||
{ |
||||
u32 num_indices; |
||||
GLuint vao_id; |
||||
bool has_texture; |
||||
GLuint tex_id; |
||||
GLenum draw_mode; // NOTE: GL_LINES, GL_TRIANGLES
|
||||
GLenum usage; // NOTE: GL_STATIC_DRAW, GL_DYNAMIC_DRAW
|
||||
|
||||
glm::mat4* node_xform; |
||||
|
||||
u32 num_vertex_attrib_buffers; |
||||
GLBuffer* vertex_attrib_buffers; |
||||
GLBuffer* element_buf; |
||||
}; |
||||
|
||||
struct Animation; |
||||
|
||||
struct Transforms |
||||
{ |
||||
glm::mat4 view_xform; |
||||
glm::mat4 proj_xform; |
||||
glm::mat4 normal_xform; |
||||
}; |
||||
|
||||
|
||||
const float DEFAULT_FOV = 60.f; |
||||
const float NEAR_CLIP_PLANE = 5.f; |
||||
const float DEFAULT_ASPECT_RATIO = 16.f / 9.f; |
||||
|
||||
|
||||
GLContext* initGLContext(MemoryArena* arena, |
||||
u32 max_shaders, |
||||
u32 max_textures, |
||||
u32 max_ubos); |
||||
|
||||
// NOTE: every shader program is assumed to have one uniform block named
|
||||
// "matrices" that contains the projection and view matrices, and one uniform
|
||||
// named "node_xform" for the node matrix
|
||||
bool addShaderProgram(MemoryArena* arena, |
||||
GLContext* gl_ctx, |
||||
const char* vs, |
||||
const char* fs, |
||||
const char* name); |
||||
|
||||
ShaderProgram* getShaderByName(const char* name, GLContext* gl_ctx); |
||||
|
||||
ShaderProgram* getShaderByID(GLContext* gl_ctx, GLuint prog_id); |
||||
|
||||
ShaderProgram* getShaderByHash(GLContext* gl_ctx, u64 hash); |
||||
|
||||
ShaderProgram* getFreeShader(GLContext* gl_ctx); |
||||
|
||||
GLBuffer* getFreeUBO(GLContext* gl_ctx); |
||||
|
||||
GLBuffer* getUBOByName(GLContext* gl_ctx, const char* name); |
||||
|
||||
GLTexture* getGLTexture(GLContext* gl_ctx, Texture* diffuse_img); |
||||
|
||||
void updateGLBuffer(GLBuffer* gl_buf, void* data); |
||||
|
||||
void renderVAO(GLMesh* glmesh, |
||||
glm::mat4* node_xform, |
||||
ShaderProgram* shader, |
||||
GLTexture* gl_tex); |
||||
|
||||
GLVertexAttrib* getVertexAttribByName(ShaderProgram* shader, const char* name); |
||||
|
||||
GLMesh loadGLMesh(MemoryArena* arena, |
||||
const Mesh& m, |
||||
GLenum draw_mode, |
||||
GLTexture* diffuse_texture, |
||||
u32 num_mappings, |
||||
GLBufferToAttribMapping mappings[]); |
||||
|
||||
void initTransforms(MemoryArena* arena, |
||||
Transforms* xforms, |
||||
GLBuffer* xform_ubo, |
||||
GLContext* gl_ctx, |
||||
float fov = DEFAULT_FOV, |
||||
float near_clip_plane = NEAR_CLIP_PLANE, |
||||
float aspect_ratio = DEFAULT_ASPECT_RATIO); |
||||
|
||||
@ -1,57 +0,0 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <GL/glew.h> |
||||
|
||||
#include "types.h" |
||||
|
||||
|
||||
// TODO: implement a 'cavity' effect for the default shader
|
||||
// https://blender.community/c/rightclickselect/J9bbbc/
|
||||
// https://developer.blender.org/rBf1fd5ed74fb0afd602f53860d0b2db46189c218a
|
||||
// https://developer.blender.org/diffusion/B/browse/master/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
|
||||
// https://www.casual-effects.com/research/McGuire2011AlchemyAO/VV11AlchemyAO.pdf
|
||||
// https://github.com/evanw/madebyevan.com/blob/master/src/templates/shaders-curvature.jade
|
||||
struct default_shader_program |
||||
{ |
||||
GLuint program_id; |
||||
|
||||
GLuint model_matrix_id; |
||||
GLuint world_transform_id; |
||||
GLuint view_matrix_id; |
||||
GLuint projection_matrix_id; |
||||
GLuint normal_matrix_id; |
||||
|
||||
GLuint vertex_array_id; |
||||
GLuint sampler_id; |
||||
GLuint num_lights_id; |
||||
}; |
||||
|
||||
struct simple_shader_program |
||||
{ |
||||
GLuint program_id; |
||||
GLuint world_transform_id; |
||||
GLuint MVP_id; |
||||
GLuint vertex_array_id; |
||||
}; |
||||
|
||||
struct shader_wrapper |
||||
{ |
||||
shader_t shader_type; |
||||
default_shader_program* default_shader; |
||||
simple_shader_program* simple_shader; |
||||
}; |
||||
|
||||
|
||||
// TODO: find a way to initialize different shaders with different
|
||||
// uniform layouts with a single function
|
||||
// look at using uniform blocks and retrieving locations with
|
||||
// glGetUniformBlockIndex() and their size with glGetActiveUniformBlockiv()
|
||||
// see chapter 2 in the red book
|
||||
simple_shader_program* |
||||
shaderInitSimple(const char* vertex_code, const char* frag_code); |
||||
|
||||
default_shader_program* |
||||
shaderInitDefault(const char* vertex_code, const char* frag_code); |
||||
|
||||
void shaderFree(uint program_id); |
||||
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* libTangerine, a small, modern openGL renderer specializing in flat shaded, |
||||
* solid color models using pallete textures. See README for build instructions |
||||
* and look in the examples folder for... examples |
||||
*/ |
||||
|
||||
/*
|
||||
* === TODO: === |
||||
* - add scene abstrastion for RenderState |
||||
* - add an example of dynamically switching shaders for an entity |
||||
* - fix debug load times (either by using cgltf, or hiding tinygltf.h) |
||||
* - RenderGroups and Entities need to come into and out of existence during |
||||
* gameplay. So, we need to extend MemoryArena to be a pool allocator |
||||
* instead of an allocate only linear allocator |
||||
* - add libTangerine namespace? |
||||
* - merge back into libTangerine |
||||
* - make separate shaders for per vertex, and per pixel/fragment lighting |
||||
* see red book chapter 7 |
||||
* - add a bloom/blur render to texture shader for light sources? |
||||
* https://learnopengl.com/Advanced-Lighting/Bloom
|
||||
* - clean up examples with new asset system |
||||
* - merge render_group_fix branch back into master |
||||
* - make a test case for overflowing default array sizes, rg->assets |
||||
* rg_info->groups, render_group->enitities, rg->textures |
||||
* - look for glm::mat4 in structs, and replace with pointers (easier debugging) |
||||
* - resizable arrays for entities and asset system |
||||
* - defaults include for various #defines |
||||
* |
||||
* === TODONE: === |
||||
* - don't allocate Asset structure on asset arena |
||||
* - store on RenderState as reference |
||||
* - store asset arena to asset structure |
||||
* - condense get_X_ByPath functions with assetLoad functions |
||||
* - move to asset interface |
||||
* - load diffuse texture automatically when loading a model |
||||
* - move entity structure and functions to new file? |
||||
* - load diffuse texture into OpenGL, and store GLTexture structure onto |
||||
* GLContext |
||||
* - need a cache algorithm starting in initEntity() |
||||
* - check for GLTexture by path hash |
||||
* - load texture into gl if not cached |
||||
* - pass result GLTexture to loadGLMesh() |
||||
* - update loadScene function with textured models, and test texture loading |
||||
* - work on cleaner interface for initEntity and loadScene... |
||||
* - add a LOGF macro for printf style logging |
||||
* - replace instances of printf |
||||
* - rename data structures to be in the new format, eg) UpperCaseStyleNames |
||||
* - rename instances of 'model_xform' that refer to a mesh node to something |
||||
* like node_xform. model_xform should be reserved for the entity node |
||||
* - move default shaders out of git-lfs |
||||
* - full lighting model |
||||
* - add buffer backed storage for light arrays in GLSL |
||||
* - remove hard-coded values in full_lighting.frag |
||||
* - test buffer backed lights, probably need to pad any scalars/vec3s |
||||
* - add point light type with tweakable attenuation parameters |
||||
* - test complex entities |
||||
* - need a separate GLBufferToAttribMapping for each mesh on an entity |
||||
* - maybe fixed now with MeshBufferType enum and getMeshData() |
||||
* - allow entities to have an empty diffuse texture (see initEntity()) |
||||
* - use dynamic shader parsing to set 'sampler_id' for shaders with textures |
||||
* - add ambient light to LightsBuffer structure |
||||
* - remember to update offsets in initLights() |
||||
* - use rg_arena for store GLMeshes, see initGLMesh() |
||||
*/ |
||||
|
||||
|
||||
#pragma once |
||||
|
||||
#include <SDL2/SDL.h> |
||||
|
||||
#include "asset.h" |
||||
#include "entity.h" |
||||
#include "dumbLog.h" |
||||
#include "GLDebug.h" |
||||
#include "shader.h" |
||||
#include "types.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
struct SDLHandles |
||||
{ |
||||
SDL_Window* window; |
||||
SDL_GLContext sdl_gl_ctx; |
||||
SDL_DisplayMode display_mode; |
||||
}; |
||||
|
||||
// TODO: node/tree structure for entities
|
||||
struct Node; |
||||
|
||||
struct RenderGroup |
||||
{ |
||||
ShaderProgram* shader; |
||||
u32 num_entities; |
||||
u32 max_entities; |
||||
Entity* entities; |
||||
char* name; |
||||
}; |
||||
|
||||
#if 0 |
||||
struct Scene |
||||
{ |
||||
MemoryArena* rg_arena; |
||||
u32 num_render_groups; |
||||
u32 max_render_groups; |
||||
RenderGroup* render_groups; |
||||
|
||||
u32 num_point_lights; |
||||
u32 max_point_lights; |
||||
PointLight* p_lights; |
||||
|
||||
u32 num_d_lights; |
||||
u32 max_d_lights; |
||||
DirectionalLight* d_lights; |
||||
|
||||
u32 num_spot_lights; |
||||
u32 max_spot_lights; |
||||
SpotLight* s_lights; |
||||
|
||||
Node root_node; |
||||
|
||||
Camera cam; |
||||
}; |
||||
#endif |
||||
|
||||
struct GLClearColor |
||||
{ |
||||
GLfloat R; |
||||
GLfloat G; |
||||
GLfloat B; |
||||
GLfloat A; |
||||
}; |
||||
|
||||
// NOTE: structure to match the layout of the 'lights' uniform in a shader
|
||||
// all the pointers are pointers into the address space of 'buffer'. the vec4
|
||||
// pointers are required to start on 16 byte boundaries as per layout std140,
|
||||
// so there may be some padding added between the 'header', and the start of
|
||||
// the arrays
|
||||
// NOTE: this is also very fragile. Since c/c++ doesn't support reflection, we
|
||||
// need to manually update 'initLights()' if we ever update this structure.
|
||||
// And remember to update the 'padding' if the number of 'header' attributes
|
||||
// change
|
||||
struct LightsBuffer |
||||
{ |
||||
u32 buf_size; |
||||
u32* max_p_lights; |
||||
u32* active_p_lights; |
||||
u32* max_d_lights; |
||||
u32* active_d_lights; |
||||
|
||||
glm::vec4* ambient_color; |
||||
|
||||
glm::vec4* pl_positions; |
||||
glm::vec4* pl_colors; |
||||
glm::uvec4* pl_intensities; |
||||
|
||||
glm::vec4* dl_directions; |
||||
glm::vec4* dl_colors; |
||||
glm::uvec4* dl_intensities; |
||||
|
||||
void* buffer; |
||||
}; |
||||
|
||||
// FIXME: re-implement Camera interface
|
||||
struct Camera; |
||||
|
||||
struct RenderState |
||||
{ |
||||
bool running; |
||||
GLClearColor clear_col; |
||||
Transforms* xforms; // NOTE: would be part of camera in libTangerine
|
||||
SDLHandles handles; |
||||
GLContext* gl_ctx; |
||||
Assets assets; |
||||
|
||||
MemoryArena* rg_arena; |
||||
u32 num_render_groups; |
||||
u32 max_render_groups; |
||||
RenderGroup* render_groups; |
||||
|
||||
// TODO: should we have a 'scene' abstraction here?
|
||||
// could have render groups, lights, camera
|
||||
// could match up with gltf scene graph where everyting is part of a node
|
||||
LightsBuffer* lights_buf; |
||||
|
||||
// FIXME: re-integrating missing libTangerine render_state properties
|
||||
Camera* camera; |
||||
glm::vec2 viewport_dims; |
||||
///
|
||||
}; |
||||
|
||||
|
||||
#define DEFAULT_MODEL_COUNT 256 |
||||
#define DEFAULT_TEXTURE_COUNT 64 |
||||
#define DEFAULT_SHADER_COUNT 64 |
||||
#define DEFAULT_UBO_COUNT 32 |
||||
#define DEFAULT_RENDER_GROUP_COUNT 256 |
||||
#define DEFAULT_CLEAR_COLOR { 0.2, 0.2, 0.2, 1 } |
||||
#define DEFAULT_AMBIENT_COLOR { 0.1, 0.1, 0.1, 1 } |
||||
#define DEFAULT_MAX_LIGHTS 32 // NOTE: needs to match NUM_LIGHTS in shaders
|
||||
RenderState* initRenderState(GLClearColor clear_col = DEFAULT_CLEAR_COLOR, |
||||
glm::vec4 ambient_color = DEFAULT_AMBIENT_COLOR, |
||||
u32 max_models = DEFAULT_MODEL_COUNT, |
||||
u32 max_textures = DEFAULT_TEXTURE_COUNT, |
||||
u32 max_shaders = DEFAULT_SHADER_COUNT, |
||||
u32 max_render_groups = DEFAULT_RENDER_GROUP_COUNT, |
||||
u32 max_ubos = DEFAULT_UBO_COUNT, |
||||
u32 max_lights = DEFAULT_MAX_LIGHTS); |
||||
|
||||
void freeRenderState(RenderState*& rs); |
||||
|
||||
#define DEFAULT_ENTITY_COUNT 256 |
||||
void initRenderGroup(RenderGroup* rg, |
||||
MemoryArena* arena, |
||||
ShaderProgram* shader, |
||||
u32 num_entities = DEFAULT_ENTITY_COUNT, |
||||
const char* name = ""); |
||||
|
||||
void freeRenderGroup(RenderGroup* rg, MemoryArena* arena); |
||||
|
||||
RenderGroup* getFreeRenderGroup(RenderState* rs); |
||||
|
||||
RenderGroup* getRenderGroupByName(RenderState* rs, const char* name); |
||||
|
||||
Entity* getFreeEntity(RenderGroup* rg); |
||||
|
||||
// NOTE: callback function signature to use with renDoRenderLoop()
|
||||
typedef void (*frame_callback_fn) (RenderState*); |
||||
|
||||
// NOTE: There are 2 callbacks to use here, cb_func_pre is called before
|
||||
// the call to renRenderFrame(), cb_fun_post is called after
|
||||
// NOTE: if you use cb_func_pre, you will have to use SDL_PollEvent() manually
|
||||
// and at minimum set rs->running = false on SDL_QUIT event
|
||||
void doRenderLoop(RenderState* rs, |
||||
uint framerate = 60, |
||||
frame_callback_fn cb_func_pre = nullptr, |
||||
frame_callback_fn cb_func_post = nullptr); |
||||
|
||||
void renderFrame(RenderState* rs, const GLClearColor& clear_col); |
||||
|
||||
|
||||
// NOTE: automatic loading of default shaders
|
||||
|
||||
struct ShaderInit |
||||
{ |
||||
const char* name; |
||||
const char* vert_path; |
||||
const char* frag_path; |
||||
}; |
||||
|
||||
#define SHADER_INIT_COUNT 4 |
||||
#define TEXTURE_ONLY_SHADER_INIT { "texture_only", \ |
||||
"../data/texture_only.vert", \
|
||||
"../data/texture_only.frag" } |
||||
#define FULL_LIGHTING_SHADER_INIT { "full_lighting", \ |
||||
"../data/full_lighting.vert", \
|
||||
"../data/full_lighting.frag" } |
||||
#define DEBUG_SHADER_INIT { "debug", \ |
||||
"../data/debug.vert", \
|
||||
"../data/debug.frag", } |
||||
#define COLORED_VERT_SHADER_INIT { "colored_vertices", \ |
||||
"../data/colored_vertices.vert", \
|
||||
"../data/colored_vertices.frag", } |
||||
const ShaderInit SHADER_INIT_LIST[SHADER_INIT_COUNT] |
||||
{ |
||||
TEXTURE_ONLY_SHADER_INIT, |
||||
FULL_LIGHTING_SHADER_INIT, |
||||
DEBUG_SHADER_INIT, |
||||
COLORED_VERT_SHADER_INIT |
||||
}; |
||||
|
||||
bool loadDefaultShaders(RenderState* rs, |
||||
u32 num_shaders = SHADER_INIT_COUNT, |
||||
const ShaderInit shaders[] = SHADER_INIT_LIST); |
||||
|
||||
// NOTE: only useful if the shader attribute names match the names in the
|
||||
// default shaders. If loading a custom shader, this may not work as expected,
|
||||
// and you should use getVertexAttribByName instead
|
||||
GLVertexAttrib* getVertexAttribByType(ShaderProgram* shader, |
||||
MeshBufferType buf_type); |
||||
@ -1,16 +1,13 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
|
||||
enum shader_t |
||||
{ |
||||
SIMPLE_SHADER, |
||||
DEFAULT_SHADER |
||||
}; |
||||
|
||||
enum mesh_t |
||||
{ |
||||
SIMPLE_MESH, |
||||
DEFAULT_MESHES |
||||
}; |
||||
typedef uint8_t u8; |
||||
typedef uint16_t u16; |
||||
typedef uint32_t u32; |
||||
typedef uint64_t u64; |
||||
|
||||
typedef int32_t i32; |
||||
typedef int64_t i64; |
||||
|
||||
@ -1,118 +1,193 @@
|
||||
|
||||
#pragma once |
||||
#ifndef UTIL_H |
||||
#define UTIL_H |
||||
|
||||
#include <cstdint> |
||||
#include <cassert> |
||||
#include <cstring> |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
|
||||
#include "dumbLog.h" |
||||
#include "types.h" |
||||
|
||||
typedef float real32; |
||||
typedef float GLfloat; |
||||
typedef double real64; |
||||
typedef int32_t bool32; |
||||
typedef int32_t int32; |
||||
typedef int64_t int64; |
||||
typedef uint8_t uint8; |
||||
typedef uint32_t uint32; |
||||
typedef uint32_t uint; |
||||
|
||||
//-----------------
|
||||
// Hashing
|
||||
|
||||
struct v2f |
||||
{ |
||||
v2f(): x(0), y(0) {} |
||||
v2f(real64 a, real64 b): x(a), y(b) {} |
||||
real64 x; |
||||
real64 y; |
||||
}; |
||||
// 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); |
||||
|
||||
struct v2i |
||||
{ |
||||
v2i(int a, int b): x(a), y(b) {} |
||||
v2i() : x(0), y(0) {} |
||||
int32 x; |
||||
int32 y; |
||||
}; |
||||
//-----------------
|
||||
// Memory allocation
|
||||
|
||||
struct v3f |
||||
struct MemoryArena |
||||
{ |
||||
v3f(): x(0), y(0), z(0) {} |
||||
v3f(real64 a, real64 b, real64 c): x(a), y(b), z(c) {} |
||||
real64 x; |
||||
real64 y; |
||||
real64 z; |
||||
size_t max_size; |
||||
size_t free_size; |
||||
void* head; |
||||
void* next_free; |
||||
}; |
||||
|
||||
inline int32 |
||||
SafeTruncateToInt32(int64 val) |
||||
{ |
||||
assert(val <= INT32_MAX && val >= INT32_MIN); |
||||
return (int32) val; |
||||
} |
||||
#define DEFAULT_ARENA_SIZE 10 * 1024 * 1024 // 10MB
|
||||
MemoryArena* arenaInit(size_t initial_size = DEFAULT_ARENA_SIZE); |
||||
|
||||
inline void |
||||
utilConvertColor(GLfloat buf[3], uint32 color) |
||||
{ |
||||
// NOTE: not using the alpha values
|
||||
buf[0] = (GLfloat) ((color >> 24) & 0xFF) / (GLfloat) 255; |
||||
buf[1] = (GLfloat) ((color >> 16) & 0xFF) / (GLfloat) 255; |
||||
buf[2] = (GLfloat) ((color >> 8) & 0xFF) / (GLfloat) 255; |
||||
} |
||||
void arenaFree(MemoryArena*& arena); |
||||
|
||||
//-----------------
|
||||
// C Strings
|
||||
u32 arenaGetFreeSize(MemoryArena* arena); |
||||
|
||||
#define ARENA_ALLOC(arena, type, count) \ |
||||
(type*) arenaAllocateBlock(arena, sizeof(type) * count) |
||||
void* arenaAllocateBlock(MemoryArena* arena, size_t block_size); |
||||
|
||||
void* arenaGetAddressOffset(void* address, u32 offset); |
||||
|
||||
#define MAX_NAME_LENGTH 256 |
||||
char* arenaCopyCStr(MemoryArena* arena, |
||||
const char* input, |
||||
u32 max_len = MAX_NAME_LENGTH); |
||||
|
||||
#define UTIL_ALLOC(count, type) (type*) utilAllocate(count, sizeof(type)) |
||||
void* utilAllocate(u32 count, u32 type_size); |
||||
|
||||
char* utilAllocateCStr(const char* str, u32 max_len = 256); |
||||
|
||||
// NOTE: max_len should be the allocated size of dest
|
||||
bool utilCopyCStr(char* dest, const char* src, uint max_len); |
||||
void utilSafeFree(void* p); |
||||
|
||||
// NOTE: returns new string with '/' between
|
||||
// NOTE: max_len should be the size of return buffer.
|
||||
char* utilConcatPath(char* out, const char* base_dir, const char* file_name, uint max_len); |
||||
//---------------
|
||||
// C string utils
|
||||
|
||||
const char* utilBaseName(const char* path_str); |
||||
bool utilCStrMatch(const char* str1, const char* str2); |
||||
|
||||
// NOTE: returns true if the first 'sz' characters from each string match
|
||||
bool utilMatchPrefix(const char* lhs, const char* rhs, int sz); |
||||
|
||||
#endif // UTIL_H
|
||||
|
||||
|
||||
#ifdef UTIL_IMPLEMENTATION |
||||
|
||||
//-----------------
|
||||
// Hashing
|
||||
|
||||
// NOTE: FNV1a hashing algorithm http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
#define FNV1_64_INIT ((uint64_t) 0xcbf29ce484222325ULL) |
||||
#define FNV_64_PRIME ((uint64_t) 0x100000001b3ULL) |
||||
uint64_t |
||||
utilFNV64a_str(const char *str, uint64_t hval = FNV1_64_INIT); |
||||
u64 |
||||
utilFNV64a_str(const char* str, u64 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
|
||||
|
||||
// NOTE: Wrapper for calloc that will send error message on out of memory
|
||||
#define UTIL_ALLOC(len, type) (type *) utilLogAlloc((len), sizeof(type), __FILE__, __LINE__) |
||||
void* utilLogAlloc(uint item_count, uint type_size, const char* file_name, const int line); |
||||
MemoryArena* |
||||
arenaInit(size_t initial_size) |
||||
{ |
||||
u32 sz = sizeof(MemoryArena); |
||||
MemoryArena* arena = |
||||
(MemoryArena*) std::calloc(initial_size + sz, sizeof(u8)); |
||||
arena->head = arena->next_free = (uint8_t*) arena + sz; |
||||
arena->max_size = initial_size; |
||||
return arena; |
||||
} |
||||
|
||||
// TODO: replace instances of 'utilSafeFree()' with macro that casts to void
|
||||
// pointer reference. Can then set the pointer to nullptr in free function
|
||||
#define UTIL_FREE(mem_ptr) utilSafeFree((void*&) mem_ptr) |
||||
void utilSafeFree(const void* mem); |
||||
void |
||||
arenaFree(MemoryArena*& arena) |
||||
{ |
||||
if (arena != nullptr) { |
||||
std::free(arena); |
||||
arena = nullptr; |
||||
} |
||||
} |
||||
|
||||
struct memory_arena |
||||
uint |
||||
arenaGetFreeSize(MemoryArena* arena) |
||||
{ |
||||
size_t max_size; |
||||
void* head; |
||||
void* next_free; |
||||
}; |
||||
return (uint8_t*) arena->head |
||||
+ arena->max_size |
||||
- (uint8_t*) arena->next_free; |
||||
} |
||||
|
||||
#define DEFAULT_ARENA_SIZE 10 * 1024 * 1024 // 10MB
|
||||
memory_arena* arenaInit(size_t initial_size = DEFAULT_ARENA_SIZE); |
||||
void* |
||||
arenaAllocateBlock(MemoryArena* arena, size_t block_size) |
||||
{ |
||||
// TODO: resizable memory arena
|
||||
assert(arenaGetFreeSize(arena) >= block_size); |
||||
assert(block_size > 0); |
||||
void* ret = arena->next_free; |
||||
arena->next_free = (uint8_t*) arena->next_free + block_size; |
||||
arena->free_size = arenaGetFreeSize(arena); |
||||
return ret; |
||||
} |
||||
|
||||
void* |
||||
arenaGetAddressOffset(void* address, u32 offset) |
||||
{ |
||||
return (void*) ((u8*) address + offset); |
||||
} |
||||
|
||||
void arenaFree(memory_arena*& arena); |
||||
char* |
||||
arenaCopyCStr(MemoryArena* arena, const char* input, u32 max_len) |
||||
{ |
||||
u32 name_len = std::strlen(input) + 1; |
||||
assert(name_len > 1 && name_len < max_len); |
||||
char* out = ARENA_ALLOC(arena, char, name_len); |
||||
std::strncpy(out, input, name_len); |
||||
return out; |
||||
} |
||||
|
||||
uint arenaGetFreeSize(memory_arena* arena); |
||||
void* |
||||
utilAllocate(u32 count, u32 type_size) |
||||
{ |
||||
void* out = std::calloc(count, type_size); |
||||
assert(out != nullptr); |
||||
return out; |
||||
} |
||||
|
||||
void* arenaAllocateBlock(memory_arena* arena, size_t block_size); |
||||
char* |
||||
utilAllocateCStr(const char* str, u32 max_len) |
||||
{ |
||||
u32 len = strlen(str) + 1; |
||||
|
||||
//-----------------
|
||||
// File I/O
|
||||
if (len > max_len) { |
||||
LOGF(Error, "%s , longer than %i\n", str, max_len); |
||||
return nullptr; |
||||
} |
||||
|
||||
char* utilDumpTextFile(const char* filename); |
||||
char* out = (char*) std::calloc(len, sizeof(u8)); |
||||
strncpy(out, str, len - 1); |
||||
return out; |
||||
} |
||||
|
||||
bool utilWriteTextFile(const char* filename, const char* text); |
||||
void |
||||
utilSafeFree(void* p) |
||||
{ |
||||
if (p) |
||||
free(p); |
||||
else |
||||
LOGF(Error, "free called on nullptr\n"); |
||||
} |
||||
|
||||
//---------------
|
||||
// C string utils
|
||||
|
||||
bool |
||||
utilCStrMatch(const char* str1, const char* str2) |
||||
{ |
||||
assert(str1 != nullptr && str2 != nullptr); |
||||
u32 l1 = strlen(str1); |
||||
u32 l2 = strlen(str2); |
||||
|
||||
return (l1 == l2) |
||||
&& (strncmp(str1, str2, l1) == 0); |
||||
} |
||||
|
||||
#endif |
||||
|
||||
@ -1,35 +0,0 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include "util.h" |
||||
|
||||
|
||||
// NOTE: wrapper for stb_image
|
||||
struct util_image |
||||
{ |
||||
int32 w; |
||||
int32 h; |
||||
int32 bits_per_channel; |
||||
int32 num_channels; |
||||
uint data_len; |
||||
uint8* pixels; |
||||
uint64_t 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]; |
||||
}; |
||||
|
||||
struct util_RGBA |
||||
{ |
||||
real32 R; |
||||
real32 G; |
||||
real32 B; |
||||
real32 A; |
||||
}; |
||||
|
||||
util_image utilLoadImagePath(const char* full_path); |
||||
|
||||
util_image utilLoadImageBytes(const unsigned char* bytes, uint length); |
||||
|
||||
void utilFreeImage(util_image image); |
||||
|
||||
@ -1,204 +0,0 @@
|
||||
|
||||
#include <cassert> |
||||
|
||||
#include <GL/glew.h> |
||||
|
||||
#include "camera.h" |
||||
|
||||
// TODO: add these props to scene json
|
||||
#define MOVE_SPEED 5.f |
||||
#define ROTATE_SPEED 0.005f |
||||
#define CAMERA_Z_CLAMP_ANGLE 85.f |
||||
#define FOV 60.f |
||||
#define NEAR_CLIP_PLANE 20.f |
||||
|
||||
|
||||
// forward declarations
|
||||
inline glm::vec3 convertv3f(v3f v); |
||||
|
||||
|
||||
// interface
|
||||
|
||||
void |
||||
cameraInitPerspective(camera* cam, |
||||
glm::vec3 position, |
||||
glm::vec3 target, |
||||
glm::vec3 world_up, |
||||
float aspect_ratio) |
||||
{ |
||||
assert(aspect_ratio > 0); |
||||
|
||||
cam->position = position; |
||||
cam->target = target; |
||||
cam->world_up = world_up; |
||||
cam->projection = glm::infinitePerspective(glm::radians(FOV), |
||||
aspect_ratio, |
||||
NEAR_CLIP_PLANE); |
||||
|
||||
cam->forward = glm::normalize(target - position); |
||||
cam->left = glm::normalize(glm::cross(cam->world_up, cam->forward)); |
||||
cam->up = glm::normalize(glm::cross(cam->forward, cam->left)); |
||||
|
||||
cam->hAngle = glm::atan(cam->forward.x, cam->forward.y); |
||||
// NOTE: get absolute value of relative axis for vAngle component
|
||||
real32 len = glm::sqrt(glm::pow(cam->forward.y, 2) + |
||||
glm::pow(cam->forward.x, 2)); |
||||
cam->vAngle = glm::atan(cam->forward.z, len); |
||||
|
||||
cam->view = |
||||
glm::lookAt(cam->position, cam->position + cam->forward, cam->up); |
||||
cam->model = glm::mat4(1.0f); |
||||
cam->MVP = cam->projection * cam->view * cam->model; |
||||
} |
||||
|
||||
void |
||||
cameraInitOrthographic(/*camera& cam, */) |
||||
{ |
||||
#if 0 |
||||
// left, right, bottom, top, zNear, zFar
|
||||
cam.projection = glm::ortho(0.f, 1280.0f, 0.f, 720.0f, 0.1f, 100.0f); |
||||
cam.view = glm::lookAt( |
||||
glm::vec3(0.0f, 0.0f, 1.0f), // camera position
|
||||
glm::vec3(0.0f, 0.0f, 0.0f), // look at position
|
||||
glm::vec3(0,1,0) // "up" vector
|
||||
); |
||||
|
||||
cam.model = glm::mat4(1.0f); |
||||
cam.MVP = cam.projection * cam.view * cam.model; |
||||
#endif |
||||
} |
||||
|
||||
v2f |
||||
cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height) |
||||
{ |
||||
// NOTE: using depth buffer may not be as accurate as doing ray-cast
|
||||
GLfloat depth; |
||||
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); |
||||
glm::vec4 viewport = glm::vec4(0, 0, vp_width, vp_height); |
||||
glm::vec3 wincoord = glm::vec3(x, y, depth); |
||||
glm::vec3 vU = glm::unProject(wincoord, cam.view, cam.projection, viewport); |
||||
v2f v(vU.x, vU.y); |
||||
|
||||
return v; |
||||
} |
||||
|
||||
v3f |
||||
cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims) |
||||
{ |
||||
// NOTE: http://antongerdelan.net/opengl/raycasting.html
|
||||
float x = 2.f * vp_coords.x / vp_dims.x - 1.f; |
||||
float y = 2.f * vp_coords.y / vp_dims.y - 1.f; |
||||
glm::vec4 ray_clip = glm::vec4(x, y, -1.f, 1.f); |
||||
glm::vec4 ray_eye = glm::inverse(cam.projection) * ray_clip; |
||||
ray_eye = glm::vec4(ray_eye.x, ray_eye.y, -1.f, 0); // NOTE: reset as ray
|
||||
glm::vec4 ray_world = glm::normalize(glm::inverse(cam.view) * ray_eye); |
||||
|
||||
return v3f(ray_world.x, ray_world.y, ray_world.z); |
||||
} |
||||
|
||||
bool |
||||
cameraIntersectPlane(camera& cam, v3f ray, v3f plane_origin, v3f plane_normal, v3f& intersection) |
||||
{ |
||||
// NOTE: https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection
|
||||
glm::vec3 c_o = cam.position; |
||||
glm::vec3 r = convertv3f(ray); |
||||
glm::vec3 p_o = convertv3f(plane_origin); |
||||
glm::vec3 p_n = convertv3f(plane_normal); |
||||
float divisor = glm::dot(r, p_n); |
||||
|
||||
if (divisor <= 0.000001f && divisor >= -0.000001f) // NOTE: ray and plane are co-planar
|
||||
return false; |
||||
|
||||
float distance = glm::dot((p_o - c_o), p_n) / divisor; |
||||
glm::vec3 xsect = c_o + (r * distance); |
||||
intersection = v3f(xsect.x, xsect.y, xsect.z); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void |
||||
cameraMove(camera& cam, bool up, bool left, bool down, bool right, bool forward, bool backward) |
||||
{ |
||||
if (!up && !left && !down && !right && !forward && !backward) |
||||
return; |
||||
|
||||
glm::vec3 f = cam.forward; |
||||
glm::vec3 u = cam.up; |
||||
glm::vec3 old = cam.position; |
||||
glm::vec3 &p = cam.position; |
||||
glm::vec3 v(0.f); // normalized direction
|
||||
|
||||
// TODO: still seems like we're adding magnitude when moving in 2 directions
|
||||
#if 0 |
||||
if (forward) v = glm::normalize(v + f); |
||||
if (backward) v = glm::normalize(v - f); |
||||
if (up) v = glm::normalize(v + u); |
||||
if (down) v = glm::normalize(v - u); |
||||
if (left) v -= glm::normalize(glm::cross(f, u)); |
||||
if (right) v -= glm::normalize(glm::cross(u, f)); |
||||
#else |
||||
if (forward) v += f; |
||||
if (backward) v -= f; |
||||
if (up) v += u; |
||||
if (down) v -= u; |
||||
if (left) v -= glm::cross(f, u); |
||||
if (right) v -= glm::cross(u, f); |
||||
#endif |
||||
|
||||
p += (v * MOVE_SPEED); |
||||
glm::vec3 diff = old - p; |
||||
cam.view = glm::translate(cam.view, diff); |
||||
cam.MVP = cam.projection * cam.view * cam.model; |
||||
} |
||||
|
||||
void |
||||
cameraRotate(camera& cam, int32 xrel, int32 yrel) |
||||
{ |
||||
float &h = cam.hAngle; |
||||
float &v = cam.vAngle; |
||||
h += ROTATE_SPEED * xrel; |
||||
v -= ROTATE_SPEED * yrel; |
||||
|
||||
// clamp vAngle to prevent gimbal lock
|
||||
float a = glm::radians(CAMERA_Z_CLAMP_ANGLE); |
||||
if (v < (-1 * a)) v = (-1 * a); |
||||
if (v > a) v = a; |
||||
|
||||
cam.forward = glm::vec3( |
||||
glm::cos(v) * glm::sin(h), |
||||
glm::cos(v) * glm::cos(h), |
||||
glm::sin(v) |
||||
); |
||||
|
||||
glm::normalize(cam.forward); |
||||
cam.left = glm::normalize(glm::cross(cam.forward, cam.world_up)); |
||||
cam.up = glm::normalize(glm::cross(cam.left, cam.forward)); |
||||
|
||||
cam.view = glm::lookAt(cam.position, cam.position + cam.forward, cam.up); |
||||
cam.MVP = cam.projection * cam.view * cam.model; |
||||
} |
||||
|
||||
void |
||||
cameraRoll(camera& cam, bool CW, bool CCW) |
||||
{ |
||||
if ((!CW && !CCW) || (CW && CCW)) |
||||
return; |
||||
|
||||
float a = 0.005f; |
||||
if (CW) a *= 1; |
||||
if (CCW) a *= -1; |
||||
glm::mat4 m = glm::rotate(glm::mat4(1.f), a, cam.forward); |
||||
glm::vec4 v(cam.up.x, cam.up.y, cam.up.z, 0); |
||||
v = v * m; |
||||
cam.up = glm::vec3(v.x, v.y, v.z); |
||||
cam.view *= m; |
||||
cam.MVP = cam.projection * cam.view * cam.model; |
||||
} |
||||
|
||||
// internal
|
||||
|
||||
inline glm::vec3 |
||||
convertv3f(v3f v) |
||||
{ |
||||
return glm::vec3(v.x, v.y, v.z); |
||||
} |
||||
@ -1,162 +0,0 @@
|
||||
|
||||
|
||||
// NOTE: default shader
|
||||
|
||||
const char* DEFAULT_VERTEX_SHADER = R"VS( |
||||
#version 330 core |
||||
|
||||
layout (location = 0) in vec3 vertexPosition_modelspace; |
||||
layout (location = 1) in vec3 normal; |
||||
layout (location = 2) in vec3 texCoord; |
||||
|
||||
out vec3 fragVertex; |
||||
out vec3 fragNormal; |
||||
out vec2 fragUV; |
||||
|
||||
uniform mat4 world_transform; |
||||
uniform mat4 model; |
||||
uniform mat4 view; |
||||
uniform mat4 projection; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
fragNormal = vec4(world_transform * vec4(normal, 1)).xyz; |
||||
fragVertex = vertexPosition_modelspace; |
||||
fragUV = texCoord.st; |
||||
gl_Position = projection * view * |
||||
world_transform * model * vec4(vertexPosition_modelspace, 1); |
||||
} |
||||
)VS"; |
||||
|
||||
// TODO: there's a bug here with the array size of 'lights'
|
||||
// with intel opengl, size can be >1000
|
||||
// but with nvidea opengl size of >200 gives the following error:
|
||||
// error C6020: Constant register limit exceeded at sampler;
|
||||
// more than 1024 registers needed to compile program
|
||||
const char* DEFAULT_FRAGMENT_SHADER = R"FS( |
||||
#version 330 core |
||||
|
||||
in vec3 fragVertex; |
||||
in vec3 fragNormal; |
||||
in vec2 fragUV; |
||||
|
||||
out vec4 color; |
||||
|
||||
uniform mat4 model; |
||||
uniform mat3 normal_matrix; |
||||
uniform sampler2D sampler; |
||||
uniform uint num_lights = 0u; |
||||
|
||||
struct point_light { |
||||
uint light_ID; |
||||
vec3 position; |
||||
vec3 color; |
||||
float intensity; |
||||
}; |
||||
|
||||
uniform point_light lights[200]; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
vec3 normal = normalize(normal_matrix * fragNormal); |
||||
vec3 fragPosition = vec3(model * vec4(fragVertex, 1)); |
||||
float totalBrightness = 0; |
||||
|
||||
for (uint i = 0u; i < num_lights; i++) { |
||||
vec3 surfaceToLight = lights[i].position - fragPosition; |
||||
//float brightness = dot(normal, surfaceToLight) / (length(surfaceToLight) * length(normal));
|
||||
float brightness = dot(normal, surfaceToLight) / length(surfaceToLight); |
||||
totalBrightness += brightness; |
||||
} |
||||
|
||||
color = clamp(totalBrightness, 0, 1) * texture(sampler, fragUV.st); |
||||
} |
||||
)FS"; |
||||
|
||||
|
||||
// NOTE: debug shader
|
||||
|
||||
const char* DEBUG_VERTEX_SHADER = R"DVS( |
||||
#version 330 core |
||||
|
||||
layout (location = 0) in vec3 vertexPosition_modelspace; |
||||
layout (location = 1) in vec3 normal; |
||||
layout (location = 2) in vec3 texCoord; |
||||
|
||||
out vec3 fragVertex; |
||||
out vec3 fragNormal; |
||||
|
||||
uniform mat4 world_transform; |
||||
uniform mat4 model; |
||||
uniform mat4 view; |
||||
uniform mat4 projection; |
||||
uniform mat3 normal_matrix; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
fragNormal = vec4(world_transform * vec4(normal, 1)).xyz; |
||||
fragVertex = vertexPosition_modelspace; |
||||
gl_Position = projection * view * |
||||
world_transform * model * vec4(vertexPosition_modelspace, 1); |
||||
} |
||||
)DVS"; |
||||
|
||||
const char* DEBUG_FRAGMENT_SHADER = R"DFS( |
||||
#version 330 core |
||||
|
||||
in vec3 fragVertex; |
||||
in vec3 fragNormal; |
||||
|
||||
out vec4 color; |
||||
|
||||
uniform mat4 model; |
||||
uniform mat3 normal_matrix; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
color = vec4(normalize( |
||||
vec3(fragNormal.x, fragNormal.y, -1 * fragNormal.z) |
||||
), 1.0); |
||||
} |
||||
)DFS"; |
||||
|
||||
|
||||
// NOTE: simple shader
|
||||
|
||||
const char* SIMPLE_VERTEX_SHADER = R"SVS( |
||||
#version 330 core |
||||
|
||||
layout (location = 0) in vec3 position; |
||||
layout (location = 1) in vec3 color; |
||||
|
||||
out vec3 frag_color; |
||||
|
||||
uniform mat4 world_transform; |
||||
uniform mat4 MVP; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
frag_color = color; |
||||
gl_Position = MVP * world_transform * vec4(position, 1); |
||||
} |
||||
)SVS"; |
||||
|
||||
const char* SIMPLE_FRAGMENT_SHADER = R"SFS( |
||||
#version 330 core |
||||
|
||||
in vec3 frag_color; |
||||
|
||||
out vec4 color; |
||||
|
||||
|
||||
void main() |
||||
{ |
||||
color = vec4(frag_color.rgb, 1); |
||||
} |
||||
)SFS"; |
||||
|
||||
@ -1,76 +1,68 @@
|
||||
|
||||
#include <cassert> |
||||
|
||||
#include <glm/ext/matrix_transform.hpp> |
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#include "dumbLog.h" |
||||
#include "entity.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
// forward declarations
|
||||
|
||||
void initDefaults(entity& e); |
||||
|
||||
|
||||
// interface
|
||||
|
||||
bool |
||||
entInitModel(entity* e, model* mdl) |
||||
initEntity(Entity* e, |
||||
GLContext* gl_ctx, |
||||
MemoryArena* arena, |
||||
Model* mdl, |
||||
u32 num_attrib_mappings, |
||||
GLBufferToAttribMapping* attrib_mappings, |
||||
const char* name) |
||||
{ |
||||
e->render_objs = roInitModel(mdl); |
||||
e->world_transform = glm::mat4(1); |
||||
e->model_id = mdl->filepath_hash; |
||||
e->num_meshes = mdl->num_meshes; |
||||
e->meshes = ARENA_ALLOC(arena, GLMesh, e->num_meshes); |
||||
e->model_xform = ARENA_ALLOC(arena, glm::mat4, 1); |
||||
*e->model_xform = glm::mat4(1.f); |
||||
e->name = arenaCopyCStr(arena, name); |
||||
|
||||
if (e->render_objs == nullptr) { |
||||
entFree(e); |
||||
return false; |
||||
} |
||||
if (mdl->diffuse_texture) { |
||||
e->diffuse_texture = getGLTexture(gl_ctx, mdl->diffuse_texture); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void |
||||
entFree(entity* e) |
||||
{ |
||||
roFree(e->render_objs); |
||||
e->render_objs = nullptr; |
||||
} |
||||
if (!e->diffuse_texture) |
||||
return false; |
||||
} |
||||
|
||||
void |
||||
entSetWorldPosition(entity& e, glm::vec3 v) |
||||
{ |
||||
e.world_transform[3][0] = v.x; |
||||
e.world_transform[3][1] = v.y; |
||||
e.world_transform[3][2] = v.z; |
||||
} |
||||
for (u32 i = 0; i< e->num_meshes; i++) { |
||||
GLMesh* glm = &e->meshes[i]; |
||||
*glm = loadGLMesh(arena, |
||||
mdl->meshes[i], |
||||
GL_TRIANGLES, |
||||
e->diffuse_texture, |
||||
num_attrib_mappings, |
||||
attrib_mappings); |
||||
|
||||
if (glm->vao_id == 0) { |
||||
LOGF(Error, "error initializing entity\n"); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
void |
||||
entTranslate(entity& e, glm::vec3 v) |
||||
{ |
||||
e.world_transform = glm::translate(e.world_transform, v); |
||||
return true; |
||||
} |
||||
|
||||
void |
||||
entScale(entity& e, glm::vec3 v) |
||||
setEntityPosition(Entity* e, glm::vec3 pos) |
||||
{ |
||||
e.world_transform = glm::scale(e.world_transform, v); |
||||
(*e->model_xform)[3][0] = pos.x; |
||||
(*e->model_xform)[3][1] = pos.y; |
||||
(*e->model_xform)[3][2] = pos.z; |
||||
} |
||||
|
||||
void |
||||
entRotate(entity* e, float angle, glm::vec3 axis) |
||||
rotateEntity(Entity* e, glm::vec3 axis, float radians) |
||||
{ |
||||
e->world_transform = glm::rotate(e->world_transform, angle, axis); |
||||
*e->model_xform = glm::rotate(*e->model_xform, radians, axis); |
||||
} |
||||
|
||||
|
||||
// internal
|
||||
|
||||
void |
||||
initDefaults(entity& e) |
||||
scaleEntity(Entity* e, float scale) |
||||
{ |
||||
e.world_transform = glm::mat4(1.0); |
||||
entScale(e, glm::vec3(1.0)); |
||||
entSetWorldPosition(e, glm::vec3(0, 0, 0)); |
||||
*e->model_xform = |
||||
glm::scale(*e->model_xform, glm::vec3(scale, scale, scale)); |
||||
} |
||||
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
|
||||
#include "input.h" |
||||
|
||||
|
||||
void |
||||
inputProcessEvent(input_state* is, SDL_Event& e) |
||||
{ |
||||
switch (e.type) { |
||||
case SDL_QUIT: |
||||
is->window_closed = true; |
||||
break; |
||||
case SDL_KEYDOWN: |
||||
switch (e.key.keysym.sym) { |
||||
case SDLK_ESCAPE: is->escape = true; break; |
||||
case SDLK_LEFT: is->left = true; break; |
||||
case SDLK_RIGHT: is->right = true; break; |
||||
case SDLK_UP: is->up = true; break; |
||||
case SDLK_DOWN: is->down = true; break; |
||||
} |
||||
break; |
||||
case SDL_KEYUP: |
||||
switch (e.key.keysym.sym) { |
||||
case SDLK_ESCAPE: is->escape = false; break; |
||||
case SDLK_LEFT: is->left = false; break; |
||||
case SDLK_RIGHT: is->right = false; break; |
||||
case SDLK_UP: is->up = false; break; |
||||
case SDLK_DOWN: is->down = false; break; |
||||
} |
||||
break; |
||||
default: break; |
||||
} |
||||
} |
||||
|
||||
void |
||||
inputProcessEvents(input_state* is) |
||||
{ |
||||
SDL_Event e; |
||||
|
||||
while (SDL_PollEvent(&e)) |
||||
inputProcessEvent(is, e); |
||||
} |
||||
|
||||
@ -1,12 +0,0 @@
|
||||
|
||||
// NOTE: put all the header-only libs in a separate compilation unit to save on
|
||||
// compile times
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION |
||||
#include "tiny_gltf.h" |
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION |
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION |
||||
#include "stb_image.h" |
||||
#include "stb_image_write.h" |
||||
|
||||
@ -1,55 +0,0 @@
|
||||
|
||||
#include <sstream> // stringstream |
||||
|
||||
#include "lights.h" |
||||
|
||||
|
||||
light_group* |
||||
lightsInit(uint max_lights) |
||||
{ |
||||
light_group* lg = UTIL_ALLOC(1, light_group); |
||||
lg->lights = UTIL_ALLOC(max_lights, point_light); |
||||
lg->max_lights = max_lights; |
||||
return lg; |
||||
} |
||||
|
||||
void |
||||
lightsOut(light_group* lights) |
||||
{ |
||||
utilSafeFree(lights->lights); |
||||
utilSafeFree(lights); |
||||
} |
||||
|
||||
bool |
||||
lightsAdd(light_group* lights, glm::vec3 pos, glm::vec3 color, float intensity) |
||||
{ |
||||
if (lights->num_lights == lights->max_lights) |
||||
return false; |
||||
|
||||
point_light& pl = lights->lights[lights->num_lights]; |
||||
pl.position = pos; |
||||
pl.color = color; |
||||
pl.intensity = intensity; |
||||
lights->needs_update = true; |
||||
lights->num_lights++; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void |
||||
lightsUpdate(light_group* lights, default_shader_program* shader) |
||||
{ |
||||
glUniform1ui(shader->num_lights_id, lights->num_lights); |
||||
|
||||
for (uint i = 0; i < lights->num_lights; i++) { |
||||
std::stringstream ss; |
||||
ss << "lights[" << i << "].position"; |
||||
int light_pos_loc = |
||||
glGetUniformLocation(shader->program_id, ss.str().c_str()); |
||||
|
||||
glUniform3fv(light_pos_loc, 1, &lights->lights[i].position[0]); |
||||
} |
||||
|
||||
lights->needs_update = false; |
||||
} |
||||
|
||||
@ -1,262 +0,0 @@
|
||||
#include <cassert> |
||||
|
||||
#if 0 |
||||
#include <assimp/cimport.h> |
||||
#include <assimp/scene.h> |
||||
#include <assimp/postprocess.h> |
||||
#endif |
||||
|
||||
#include <glm/glm.hpp> |
||||
#include <glm/geometric.hpp> |
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#include "dumbLog.h" |
||||
|
||||
#include "mesh.h" |
||||
|
||||
|
||||
// WIP
|
||||
bool meInitAssimp() { return false; } |
||||
|
||||
bool meLoadFromFile(mesh_group& mesh_group, const char* filepath) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
simple_mesh* |
||||
meInitMesh(uint num_vertices) { return nullptr; } |
||||
|
||||
void |
||||
meFreeMeshGroup(mesh_group& mesh_group) {} |
||||
|
||||
void |
||||
meFreeSimpleMesh(simple_mesh* mesh) {} |
||||
|
||||
void |
||||
meShutdownAssimp() {} |
||||
|
||||
// WIP
|
||||
|
||||
|
||||
#if 0 |
||||
// forward declarations
|
||||
|
||||
mesh_info* allocateMeshInfo(uint num_vertices, uint num_indices); |
||||
void assimpLogCB(const char* message, char* user); |
||||
mesh_info* copyMeshInfo(const aiScene* scene, aiMesh* mesh); |
||||
inline glm::vec3 copyVector(aiVector3D v_in, glm::vec3& v_out); |
||||
void freeMesh(mesh_info* mesh); |
||||
bool loadDiffuseTexture(const aiScene* scene, aiMesh* mesh, mesh_info* mi); |
||||
bool validateScene(const aiScene* scene, const char* filepath); |
||||
|
||||
// interface
|
||||
|
||||
bool |
||||
meInitAssimp() |
||||
{ |
||||
LOG(Info) << "Initializing Assimp\n"; |
||||
aiLogStream ls; |
||||
ls.callback = assimpLogCB; |
||||
aiAttachLogStream(&ls); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
meLoadFromFile(mesh_group& mesh_group, const char* filepath) |
||||
{ |
||||
LOG(Info) << "Loading file: " << filepath << "\n"; |
||||
const aiScene* scene = aiImportFile(filepath, aiProcessPreset_TargetRealtime_MaxQuality); |
||||
|
||||
if (!validateScene(scene, filepath)) |
||||
return false; |
||||
|
||||
mesh_group.num_meshes = scene->mNumMeshes; |
||||
mesh_group.meshes = UTIL_ALLOC(mesh_group.num_meshes, mesh_info*); |
||||
|
||||
for (uint i = 0; i < scene->mNumMeshes; i++) { |
||||
aiMesh* mesh = scene->mMeshes[i]; |
||||
mesh_info* mi = copyMeshInfo(scene, mesh); |
||||
|
||||
if (!mesh->HasTextureCoords(0) || |
||||
!loadDiffuseTexture(scene, mesh, mi)) |
||||
{ |
||||
LOG(Error) << "Error loading texture, cleaning up import\n"; |
||||
freeMesh(mi); |
||||
aiReleaseImport(scene); |
||||
return false; |
||||
} |
||||
|
||||
mesh_group.meshes[i] = mi; |
||||
} |
||||
|
||||
aiReleaseImport(scene); |
||||
return true; |
||||
} |
||||
|
||||
simple_mesh* |
||||
meInitMesh(uint num_vertices) |
||||
{ |
||||
assert(num_vertices > 0); |
||||
simple_mesh* sm = UTIL_ALLOC(1, simple_mesh); |
||||
sm->model_transform = glm::mat4(1.0); |
||||
sm->num_vertices = num_vertices; |
||||
sm->vertices = UTIL_ALLOC(num_vertices, glm::vec3); |
||||
sm->vert_colors = UTIL_ALLOC(num_vertices, glm::vec3); |
||||
return sm; |
||||
} |
||||
|
||||
void |
||||
meFreeMeshGroup(mesh_group& mesh_group) |
||||
{ |
||||
for (uint i = 0; i < mesh_group.num_meshes; i++) |
||||
freeMesh(mesh_group.meshes[i]); |
||||
|
||||
utilSafeFree(mesh_group.meshes); |
||||
mesh_group.num_meshes = 0; |
||||
mesh_group.meshes = nullptr; |
||||
} |
||||
|
||||
void |
||||
meFreeSimpleMesh(simple_mesh* mesh) |
||||
{ |
||||
assert(mesh != nullptr); |
||||
utilSafeFree(mesh->vertices); |
||||
mesh->vertices = nullptr; |
||||
utilSafeFree(mesh->vert_colors); |
||||
mesh->vert_colors = nullptr; |
||||
mesh->num_vertices = 0; |
||||
} |
||||
|
||||
void |
||||
meShutdownAssimp() |
||||
{ |
||||
aiDetachAllLogStreams(); |
||||
} |
||||
|
||||
|
||||
// internal
|
||||
|
||||
mesh_info* |
||||
allocateMeshInfo(uint num_vertices, uint num_indices) |
||||
{ |
||||
mesh_info* mi = UTIL_ALLOC(1, mesh_info); |
||||
mi->model_transform = glm::mat4(1); |
||||
|
||||
// allocate buffers for vertex and index data from mesh
|
||||
mi->num_vertices = num_vertices; |
||||
mi->vertices = UTIL_ALLOC(mi->num_vertices, glm::vec3); |
||||
mi->num_indices = num_indices; |
||||
mi->indices = UTIL_ALLOC(num_indices, uint); |
||||
mi->normals = UTIL_ALLOC(mi->num_vertices, glm::vec3); |
||||
mi->texture_coords = UTIL_ALLOC(mi->num_vertices, glm::vec3); |
||||
|
||||
return mi; |
||||
} |
||||
|
||||
void |
||||
assimpLogCB(const char* message, char* user) |
||||
{ |
||||
// NOTE: filter 'info' messages from assimp
|
||||
if (!utilMatchPrefix(message, "Info,", 5)) |
||||
LOG(Info) << message << "\n"; |
||||
} |
||||
|
||||
mesh_info* |
||||
copyMeshInfo(const aiScene* scene, aiMesh* mesh) |
||||
{ |
||||
mesh_info* mi = allocateMeshInfo(mesh->mNumVertices, mesh->mNumFaces * 3); |
||||
|
||||
// copy vertices, normals, and texture coords
|
||||
for (uint i = 0; i < mi->num_vertices; i++) { |
||||
copyVector(mesh->mVertices[i], mi->vertices[i]); |
||||
copyVector(mesh->mNormals[i], mi->normals[i]); |
||||
mi->texture_coords[i].x = mesh->mTextureCoords[0][i].x; |
||||
mi->texture_coords[i].y = mesh->mTextureCoords[0][i].y; |
||||
mi->texture_coords[i].z = 0; |
||||
} |
||||
|
||||
// copy indices
|
||||
for (uint i = 0; i < mesh->mNumFaces; i++) |
||||
for (uint j = 0; j < 3; j++) |
||||
mi->indices[i * 3 + j] = mesh->mFaces[i].mIndices[j]; |
||||
|
||||
return mi; |
||||
} |
||||
|
||||
inline glm::vec3 |
||||
copyVector(aiVector3D v_in, glm::vec3& v_out) |
||||
{ |
||||
v_out.x = v_in.x; |
||||
v_out.y = v_in.y; |
||||
v_out.z = v_in.z; |
||||
return v_out; |
||||
} |
||||
|
||||
void |
||||
freeMesh(mesh_info* mesh) |
||||
{ |
||||
utilFreeImage(mesh->diffuse_texture); |
||||
utilSafeFree(mesh->vertices); |
||||
utilSafeFree(mesh->normals); |
||||
utilSafeFree(mesh->texture_coords); |
||||
utilSafeFree(mesh->indices); |
||||
utilSafeFree(mesh); |
||||
} |
||||
|
||||
bool |
||||
loadDiffuseTexture(const aiScene* scene, aiMesh* mesh, mesh_info* mi) |
||||
{ |
||||
aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex]; |
||||
aiString file_name; |
||||
|
||||
if (mat->GetTextureCount(aiTextureType_DIFFUSE) < 1) |
||||
return false; |
||||
|
||||
if (AI_SUCCESS != mat->GetTexture( |
||||
aiTextureType_DIFFUSE, 0, &file_name, NULL, NULL, NULL, NULL, NULL)) |
||||
{ |
||||
LOG(Error) << "No diffuse texture from assimp\n"; |
||||
return false; |
||||
} else { |
||||
const aiTexture* tex = scene->GetEmbeddedTexture(file_name.C_Str()); |
||||
|
||||
if (tex != nullptr) { |
||||
LOG(Info) << "has embedded texture\n"; |
||||
mi->diffuse_texture = utilLoadImageBytes((const uint8*) tex->pcData, tex->mWidth); |
||||
} else { |
||||
LOG(Info) << "Loading texture file: " << file_name.C_Str() << "\n"; |
||||
mi->diffuse_texture = utilLoadImagePath(file_name.C_Str()); |
||||
} |
||||
|
||||
if (mi->diffuse_texture.pixels == nullptr) { |
||||
LOG(Error) << "Error loading texture\n"; |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
validateScene(const aiScene* scene, const char* filepath) |
||||
{ |
||||
if (!scene) { |
||||
LOG(Error) << "Error loading file: " << filepath << "\n"; |
||||
return false; |
||||
} |
||||
|
||||
if (scene->mNumMeshes < 1) { |
||||
LOG(Error) << "Scene contains no meshes\n"; |
||||
return false; |
||||
} |
||||
|
||||
if (!scene->mMeshes[0]->HasNormals()) { |
||||
LOG(Error) << "Mesh doesn't have normals\n"; |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
#endif |
||||
|
||||
@ -1,246 +0,0 @@
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#include "dumbLog.h" |
||||
#include "render_object.h" |
||||
|
||||
|
||||
struct default_render_object |
||||
{ |
||||
glm::mat4 node_xform; |
||||
|
||||
GLuint tex_id; |
||||
GLuint vertex_buffer_id; |
||||
GLuint normal_buffer_id; |
||||
GLuint uv_buffer_id; |
||||
GLuint index_buffer_id; |
||||
uint index_buffer_count; |
||||
}; |
||||
|
||||
struct render_objects |
||||
{ |
||||
default_render_object* objects; |
||||
uint count; |
||||
mesh_t mesh_type; |
||||
}; |
||||
|
||||
|
||||
// forward declarations
|
||||
|
||||
void drawDefault(render_objects* r_objs, |
||||
glm::mat4 world_transform, |
||||
camera* cam, |
||||
shader_wrapper sw, |
||||
light_group* lights); |
||||
bool loadMeshIntoGL(default_render_object* ro_out, mesh* me_in); |
||||
|
||||
|
||||
// interface
|
||||
|
||||
render_objects* |
||||
roInitModel(model* mdl) |
||||
{ |
||||
uint count = mdl->num_meshes; |
||||
assert(count > 0); |
||||
|
||||
render_objects* r_objs = UTIL_ALLOC(1, render_objects); |
||||
r_objs->objects = UTIL_ALLOC(count, default_render_object); |
||||
r_objs->count = count; |
||||
r_objs->mesh_type = DEFAULT_MESHES; |
||||
|
||||
default_render_object* objects = (default_render_object*) r_objs->objects; |
||||
|
||||
for (uint i = 0; i < count; i++) { |
||||
if (!loadMeshIntoGL(&objects[i], &mdl->meshes[i])) { |
||||
roFree(r_objs); |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
return r_objs; |
||||
} |
||||
|
||||
void |
||||
roFree(render_objects* r_objs) |
||||
{ |
||||
if (r_objs->mesh_type == SIMPLE_MESH) { |
||||
//
|
||||
} else if (r_objs->mesh_type == DEFAULT_MESHES) { |
||||
default_render_object* objects = r_objs->objects; |
||||
|
||||
for (uint i = 0; i < r_objs->count; i++) { |
||||
glDeleteBuffers(1, &objects[i].vertex_buffer_id); |
||||
glDeleteBuffers(1, &objects[i].normal_buffer_id); |
||||
glDeleteBuffers(1, &objects[i].uv_buffer_id); |
||||
glDeleteBuffers(1, &objects[i].index_buffer_id); |
||||
glDeleteTextures(1, &objects[i].tex_id); |
||||
} |
||||
|
||||
utilSafeFree(r_objs->objects); |
||||
utilSafeFree(r_objs); |
||||
} |
||||
} |
||||
|
||||
// TODO: update projection * view matrices once per frame here
|
||||
void |
||||
roDraw(render_objects* r_objs, |
||||
glm::mat4 world_transform, |
||||
camera* cam, |
||||
shader_wrapper sw, |
||||
light_group* lights) |
||||
{ |
||||
assert(r_objs->mesh_type == DEFAULT_MESHES); |
||||
// FIXME: might as well move this function now that we only have one path
|
||||
drawDefault(r_objs, world_transform, cam, sw, lights); |
||||
} |
||||
|
||||
|
||||
// internal
|
||||
|
||||
bool |
||||
initGLBuffer(void* buffer, |
||||
uint count, |
||||
GLuint* buffer_id, |
||||
uint el_count = 3, |
||||
uint el_size = sizeof(float), |
||||
GLenum target = GL_ARRAY_BUFFER, |
||||
GLenum usage = GL_STATIC_DRAW) |
||||
{ |
||||
|
||||
if ((el_count == 3 && el_size == sizeof(float)) |
||||
|| (el_count == 2 && el_size == sizeof(float)) |
||||
|| (el_count == 1 && el_size == sizeof(unsigned short))) |
||||
{ |
||||
glGenBuffers(1, buffer_id); |
||||
glBindBuffer(target, *buffer_id); |
||||
glBufferData(target, count * el_count * el_size, buffer, usage); |
||||
|
||||
return (glGetError() == GL_NO_ERROR); |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
inline void |
||||
enableGLFloatBuffer(uint buffer_id, uint location) |
||||
{ |
||||
glEnableVertexAttribArray(location); |
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer_id); |
||||
glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); |
||||
} |
||||
|
||||
bool |
||||
initGLTexture(const util_image image, GLuint& tex_id) |
||||
{ |
||||
glGenTextures(1, &tex_id); |
||||
glBindTexture(GL_TEXTURE_2D, tex_id); |
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
||||
GLenum pixel_format = (image.num_channels == 3) ? GL_RGB : GL_RGBA; |
||||
glTexImage2D(GL_TEXTURE_2D, 0, pixel_format, image.w, image.h, 0, |
||||
pixel_format, GL_UNSIGNED_BYTE, image.pixels); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||||
|
||||
return (glGetError() == GL_NO_ERROR); |
||||
} |
||||
|
||||
bool |
||||
loadMeshIntoGL(default_render_object* ro_out, mesh* mesh_in) |
||||
{ |
||||
assert(mesh_in != nullptr && ro_out != nullptr); |
||||
|
||||
if (initGLBuffer(mesh_in->vertices, mesh_in->num_vertices, |
||||
&ro_out->vertex_buffer_id) |
||||
&& initGLBuffer(mesh_in->normals, mesh_in->num_vertices, |
||||
&ro_out->normal_buffer_id) |
||||
&& initGLBuffer(mesh_in->texture_coords, mesh_in->num_vertices, |
||||
&ro_out->uv_buffer_id, 2) |
||||
&& initGLBuffer(mesh_in->indices, mesh_in->num_indices, |
||||
&ro_out->index_buffer_id, 1, sizeof(unsigned short), |
||||
GL_ELEMENT_ARRAY_BUFFER) |
||||
// FIXME: re-implement diffuse texture, but with index into an array
|
||||
// on render_state
|
||||
#if 0 |
||||
&& initGLTexture(mesh_in->diffuse_texture, ro_out->tex_id)) |
||||
#endif |
||||
) |
||||
{ |
||||
ro_out->node_xform = *mesh_in->xform; |
||||
ro_out->index_buffer_count = mesh_in->num_indices; |
||||
return true; |
||||
} |
||||
|
||||
LOG(Error) << "Failed to initialize render_object\n"; |
||||
return false; |
||||
} |
||||
|
||||
// TODO: really only need to update the view and projection matrices once per
|
||||
// frame, maybe add another interface function in render_object to call from
|
||||
// renRenderFrame
|
||||
inline void |
||||
updateMatrices(default_shader_program* shader, |
||||
camera* cam, |
||||
glm::mat4 world_xform, |
||||
glm::mat4 node_xform) |
||||
{ |
||||
glUniformMatrix4fv( |
||||
shader->world_transform_id, 1, GL_FALSE, &world_xform[0][0]); |
||||
glUniformMatrix4fv(shader->model_matrix_id, 1, GL_FALSE, &node_xform[0][0]); |
||||
glUniformMatrix4fv(shader->view_matrix_id, 1, GL_FALSE, &cam->view[0][0]); |
||||
glUniformMatrix4fv(shader->projection_matrix_id, 1, GL_FALSE, |
||||
&cam->projection[0][0]); |
||||
glm::mat3 normal_matrix = glm::transpose( |
||||
glm::inverse(glm::mat3(cam->model))); |
||||
glUniformMatrix3fv(shader->normal_matrix_id, 1, GL_FALSE, |
||||
&normal_matrix[0][0]); |
||||
} |
||||
|
||||
void |
||||
drawDefault(render_objects* r_objs, |
||||
glm::mat4 world_transform, |
||||
camera* cam, |
||||
shader_wrapper sw, |
||||
light_group* lights) |
||||
{ |
||||
default_shader_program* shader = sw.default_shader; |
||||
default_render_object* objects = r_objs->objects; |
||||
glUseProgram(shader->program_id); |
||||
updateMatrices(shader, cam, world_transform, objects->node_xform); |
||||
// FIXME: re-enable lights
|
||||
#if 0 |
||||
if (lights->needs_update) lightsUpdate(lights, shader); |
||||
#endif |
||||
|
||||
for (uint i = 0; i < r_objs->count; i++) { |
||||
enableGLFloatBuffer(objects[i].vertex_buffer_id, 0); |
||||
enableGLFloatBuffer(objects[i].normal_buffer_id, 1); |
||||
// TODO: could pass in a stride parameter here to enableGLFloatBuffer()
|
||||
// could then use a 2d buffer for uv coords
|
||||
enableGLFloatBuffer(objects[i].uv_buffer_id, 2); |
||||
// FIXME: re-enable textures
|
||||
#if 0 |
||||
glBindTexture(GL_TEXTURE_2D, objects[i].tex_id); |
||||
glUniform1i(shader->sampler_id, 0); |
||||
#endif |
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, objects[i].index_buffer_id); |
||||
// FIXME: tinygltf uses unsigned short as index type
|
||||
#if 0 |
||||
glDrawElements(GL_TRIANGLES, |
||||
objects[i].index_buffer_count, |
||||
GL_UNSIGNED_INT, |
||||
0); |
||||
#endif |
||||
glDrawElements(GL_TRIANGLES, |
||||
objects[i].index_buffer_count, |
||||
GL_UNSIGNED_SHORT, |
||||
0); |
||||
|
||||
glDisableVertexAttribArray(0); |
||||
glDisableVertexAttribArray(1); |
||||
glDisableVertexAttribArray(2); |
||||
} |
||||
|
||||
glUseProgram(0); |
||||
} |
||||
|
||||
@ -1,369 +0,0 @@
|
||||
|
||||
#if defined (_WIN32) |
||||
#include <SDL.h> |
||||
#else |
||||
#include <SDL2/SDL.h> |
||||
#endif |
||||
#include <glm/glm.hpp> |
||||
#include <glm/geometric.hpp> |
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#include "default_shaders.cpp" |
||||
#include "dumbLog.h" |
||||
#include "input.h" |
||||
#include "render_object.h" |
||||
#include "renderer.h" |
||||
|
||||
#define CLEAR_COL_R 55.f / 255.f |
||||
#define CLEAR_COL_G 55.f / 255.f |
||||
#define CLEAR_COL_B 55.f / 255.f |
||||
#define CLEAR_COL_A 1.f |
||||
#define USE_SECOND_MONITOR 0 |
||||
|
||||
|
||||
// forward declarations
|
||||
|
||||
bool createWindow(const char* title, |
||||
SDL_Handles* handles, |
||||
glm::vec2& viewport_dims); |
||||
bool initContext(SDL_Handles* handles); |
||||
bool initGlOptions(); |
||||
bool initSDL(SDL_Handles* handles, Uint32 SDL_init_flags); |
||||
bool initShaders(render_state* rs); |
||||
void openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
||||
GLsizei length, const GLchar* message, const void* userParam); |
||||
void setDefaults(render_state* rs, glm::vec2 viewport_dims); |
||||
|
||||
|
||||
// interface
|
||||
|
||||
render_group* |
||||
rgAlloc(rg_info* rgi, uint num_entites, shader_wrapper shader) |
||||
{ |
||||
if (rgi->count < rgi->max_size) { |
||||
render_group* rg = &rgi->groups[rgi->count]; |
||||
rgi->count++; |
||||
rg->entities = UTIL_ALLOC(num_entites, entity); |
||||
rg->max_size = num_entites; |
||||
rg->shader = shader; |
||||
return rg; |
||||
} |
||||
|
||||
LOG(Error) << "no free render_group\n"; |
||||
return nullptr; |
||||
} |
||||
|
||||
entity* |
||||
rgAppend(render_group* rg, |
||||
model_assets* assets, |
||||
texture_assets* textures, |
||||
memory_arena* arena, |
||||
const char* model_path) |
||||
{ |
||||
if (rg->count < rg->max_size) { |
||||
entity* e = &rg->entities[rg->count]; |
||||
model* mdl = assetGetCached(assets, utilFNV64a_str(model_path)); |
||||
|
||||
// NOTE: not cached
|
||||
if (mdl == nullptr) { |
||||
mdl = assetLoadFromFile(assets, textures, arena, model_path); |
||||
|
||||
if (mdl == nullptr) { |
||||
LOG(Error) << "Error loading model: " << model_path << "\n"; |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
if (entInitModel(e, mdl)) { |
||||
rg->count++; |
||||
return e; |
||||
} else { |
||||
LOG(Error) << "Error initializing GL buffers\n"; |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
LOG(Error) << "no free entity in render_group\n"; |
||||
return nullptr; |
||||
} |
||||
|
||||
render_state* |
||||
renInit(const char* title, |
||||
glm::vec2 viewport_dims, |
||||
Uint32 SDL_init_flags, |
||||
size_t arena_size, |
||||
uint asset_size) |
||||
{ |
||||
render_state* rs = UTIL_ALLOC(1, render_state); |
||||
rs->handles = UTIL_ALLOC(1, SDL_Handles); |
||||
rs->cam = UTIL_ALLOC(1, camera); |
||||
// TODO: add parameter for custom render_group count
|
||||
rs->render_groups = UTIL_ALLOC(1, rg_info); |
||||
rs->render_groups->groups = UTIL_ALLOC(DEFAULT_RG_COUNT, render_group); |
||||
rs->render_groups->max_size = DEFAULT_RG_COUNT; |
||||
rs->arena = arenaInit(arena_size); |
||||
rs->assets = assetInitModelBlock(rs->arena, asset_size); |
||||
rs->textures = assetInitTextureBlock(rs->arena, asset_size); |
||||
rs->lights = lightsInit(); |
||||
setDefaults(rs, viewport_dims); |
||||
|
||||
if (rs->assets != nullptr && |
||||
initSDL(rs->handles, SDL_init_flags) && |
||||
createWindow(title, rs->handles, rs->viewport_dims) && |
||||
initContext(rs->handles) && |
||||
initGlOptions() && |
||||
initShaders(rs)) |
||||
{ |
||||
return rs; |
||||
} |
||||
|
||||
LOG(Error) << "renderer initialization failed, aborting\n"; |
||||
return nullptr; |
||||
} |
||||
|
||||
void |
||||
renShutdown(render_state* rs) |
||||
{ |
||||
for (uint i = 0; i < rs->render_groups->count; i++) { |
||||
render_group* rg = &rs->render_groups->groups[i]; |
||||
|
||||
for (uint j = 0; j < rg->count; j++) |
||||
entFree(&rg->entities[j]); |
||||
} |
||||
|
||||
shaderFree(rs->default_shader->program_id); |
||||
utilSafeFree(rs->default_shader); |
||||
rs->default_shader = nullptr; |
||||
shaderFree(rs->simple_shader->program_id); |
||||
utilSafeFree(rs->simple_shader); |
||||
rs->simple_shader = nullptr; |
||||
|
||||
lightsOut(rs->lights); |
||||
utilSafeFree(rs->render_groups); |
||||
rs->render_groups = nullptr; |
||||
arenaFree(rs->arena); |
||||
SDL_GL_DeleteContext(rs->handles->glContext); |
||||
SDL_DestroyWindow(rs->handles->window); |
||||
SDL_Quit(); |
||||
utilSafeFree(rs->handles); |
||||
} |
||||
|
||||
void |
||||
renDoRenderLoop(render_state* rs, |
||||
uint framerate, |
||||
frame_callback_fn cb_func_pre, |
||||
frame_callback_fn cb_func_post) |
||||
{ |
||||
uint delay = (framerate > 0) ? 1 / framerate : 0; |
||||
uint frameStart, frameTime; |
||||
static input_state is = {}; |
||||
|
||||
while (rs->running) { |
||||
frameStart = SDL_GetTicks(); |
||||
|
||||
if (cb_func_pre != nullptr) { |
||||
cb_func_pre(rs); |
||||
} else { |
||||
inputProcessEvents(&is); |
||||
|
||||
if (is.window_closed || is.escape) { |
||||
rs->running = false; |
||||
return; |
||||
} |
||||
} |
||||
|
||||
renRenderFrame(rs); |
||||
if (cb_func_post != nullptr) cb_func_post(rs); |
||||
|
||||
SDL_GL_SwapWindow(rs->handles->window); |
||||
frameTime = SDL_GetTicks() - frameStart; |
||||
|
||||
if (delay > frameTime) |
||||
SDL_Delay(delay - frameTime); |
||||
} |
||||
} |
||||
|
||||
void |
||||
renRenderFrame(render_state* rs) |
||||
{ |
||||
glClearColor(rs->clear_col.R, |
||||
rs->clear_col.G, |
||||
rs->clear_col.B, |
||||
rs->clear_col.A); |
||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
||||
|
||||
for (uint i = 0; i < rs->render_groups->count; i++) { |
||||
render_group* rg = &rs->render_groups->groups[i]; |
||||
|
||||
for (uint j = 0; j < rg->count; j++) { |
||||
entity* e = &rg->entities[j]; |
||||
roDraw(e->render_objs, |
||||
e->world_transform, |
||||
rs->cam, |
||||
rg->shader, |
||||
rs->lights); |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool |
||||
renAddLight(render_state* rs, glm::vec3 pos, glm::vec3 color, float intensity) |
||||
{ |
||||
if (!lightsAdd(rs->lights, pos, color, intensity)) { |
||||
LOG(Error) << "Error adding light\n"; |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
glm::vec2 |
||||
renGetWindowDims(render_state* rs) |
||||
{ |
||||
int x = 0, y = 0; |
||||
SDL_GetWindowSize(rs->handles->window, &x, &y); |
||||
glm::vec2 dims(x, y); |
||||
return dims; |
||||
} |
||||
|
||||
|
||||
// internal
|
||||
|
||||
bool |
||||
createWindow(const char* title, SDL_Handles* handles, glm::vec2& viewport_dims) |
||||
{ |
||||
uint display_id = 0; |
||||
if (USE_SECOND_MONITOR && SDL_GetNumVideoDisplays() > 1) |
||||
display_id = 1; |
||||
|
||||
handles->window = SDL_CreateWindow( |
||||
title, |
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display_id), |
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display_id), |
||||
viewport_dims.x, |
||||
viewport_dims.y, |
||||
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE |
||||
); |
||||
|
||||
if (!handles->window) { |
||||
LOG(Error) << "Error creating window: " << SDL_GetError() << "\n"; |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
initContext(SDL_Handles* handles) |
||||
{ |
||||
handles->glContext = SDL_GL_CreateContext(handles->window); |
||||
|
||||
if (!handles->glContext) { |
||||
LOG(Error) << "Error creating glContext: " << SDL_GetError() << "\n"; |
||||
return false; |
||||
} |
||||
|
||||
// TODO: this doesn't work inside VM with QXL graphics
|
||||
if (SDL_GL_SetSwapInterval(1) != 0) // vsync
|
||||
LOG(Error) << "SDL Errors: " << SDL_GetError() << "\n"; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
initGlOptions() |
||||
{ |
||||
if (glewInit()) { |
||||
LOG(Error) << "failed to initialize OpenGL\n"; |
||||
return false; |
||||
} |
||||
|
||||
LOG(Info) << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; |
||||
LOG(Info)<< "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; |
||||
LOG(Info) << "opengl version: " << glGetString(GL_VERSION) << "\n"; |
||||
|
||||
glEnable(GL_DEPTH_TEST); |
||||
glEnable(GL_LINE_SMOOTH); |
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
||||
#if 0 |
||||
// TODO: blending messes up rendering with mesa on intel graphics 4000
|
||||
glEnable(GL_BLEND); |
||||
glBlendEquation(GL_FUNC_ADD); |
||||
glBlendFunc(GL_ONE, GL_SRC_ALPHA); |
||||
#endif |
||||
|
||||
// TODO: glDebugMessageCallback is only availabe from >v4.3
|
||||
// check and warn if context doesn't support this function here
|
||||
glEnable (GL_DEBUG_OUTPUT); |
||||
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); |
||||
// hide VRAM debug messages
|
||||
glDebugMessageControl(GL_DONT_CARE, 33361, GL_DONT_CARE, 0, 0, GL_FALSE); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
initSDL(SDL_Handles* handles, Uint32 SDL_init_flags) |
||||
{ |
||||
Uint32 flags = SDL_INIT_VIDEO | SDL_init_flags; |
||||
|
||||
if (SDL_Init(flags) != 0) { |
||||
LOG(Error) << "Error, SDL_Init: " << SDL_GetError() << "\n"; |
||||
return false; |
||||
} |
||||
|
||||
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, &handles->currentDisplayMode); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
initShaders(render_state* rs) |
||||
{ |
||||
rs->default_shader = |
||||
// FIXME: debug shader
|
||||
#if 0 |
||||
shaderInitDefault(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER); |
||||
#endif |
||||
shaderInitDefault(DEBUG_VERTEX_SHADER, DEBUG_FRAGMENT_SHADER); |
||||
rs->simple_shader = |
||||
shaderInitSimple(SIMPLE_VERTEX_SHADER, SIMPLE_FRAGMENT_SHADER); |
||||
|
||||
if (rs->default_shader == nullptr || |
||||
rs->simple_shader == nullptr) |
||||
{ |
||||
LOG(Error) << "shader loading failed\n"; |
||||
return false; |
||||
} else { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
void |
||||
openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
||||
GLsizei length, const GLchar* message, const void* userParam) |
||||
{ |
||||
LOG((type == GL_DEBUG_TYPE_ERROR) ? Error : Debug) |
||||
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "") |
||||
<< ", type: " << type |
||||
<< ", severity: " << severity |
||||
<< ", message: " << message << "\n"; |
||||
} |
||||
|
||||
void |
||||
setDefaults(render_state* rs, glm::vec2 viewport_dims) |
||||
{ |
||||
rs->running = true; |
||||
rs->viewport_dims = viewport_dims; |
||||
rs->clear_col.R = CLEAR_COL_R; |
||||
rs->clear_col.B = CLEAR_COL_B; |
||||
rs->clear_col.G = CLEAR_COL_G; |
||||
rs->clear_col.A = CLEAR_COL_A; |
||||
} |
||||
@ -0,0 +1,751 @@
|
||||
|
||||
#include <cassert> |
||||
#include <cstddef> |
||||
#include <cstdlib> |
||||
#include <cstdio> |
||||
#include <cstring> |
||||
#include <fstream> |
||||
|
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#include "asset.h" |
||||
#include "dumbLog.h" |
||||
#include "dummy_shader.h" |
||||
#define GL_DEBUG_IMPLEMENTATION |
||||
#include "GLDebug.h" |
||||
#include "shader.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
// NOTE: forward declarations
|
||||
|
||||
bool parseShader(MemoryArena* arena, GLContext* gl_ctx, ShaderProgram* s); |
||||
|
||||
void loadDummyShader(); |
||||
|
||||
void initCTXSizes(GLContext* gl_ctx); |
||||
|
||||
bool compileAndLinkShader(ShaderProgram* shader, |
||||
const char* vert_src, |
||||
const char* frag_src, |
||||
GLuint& vs_id, |
||||
GLuint& fs_id); |
||||
|
||||
u32 getGLTypeSize(GLenum e); |
||||
|
||||
GLTexture* getFreeGLTexture(GLContext* gl_ctx); |
||||
|
||||
bool loadGLTexture(Texture* image, GLuint& tex_id); |
||||
|
||||
void* getMeshData(const Mesh& m, const GLBufferToAttribMapping& mapping); |
||||
|
||||
|
||||
// NOTE: interface
|
||||
|
||||
GLContext* |
||||
initGLContext(MemoryArena* arena, |
||||
u32 max_shaders, |
||||
u32 max_textures, |
||||
u32 max_ubos) |
||||
{ |
||||
GLContext* gl_ctx = ARENA_ALLOC(arena, GLContext, 1); |
||||
gl_ctx->max_shaders = max_shaders; |
||||
gl_ctx->shaders = ARENA_ALLOC(arena, ShaderProgram, max_shaders); |
||||
gl_ctx->max_ubos = max_ubos; |
||||
gl_ctx->uniform_buffers = ARENA_ALLOC(arena, GLBuffer, max_ubos); |
||||
|
||||
// NOTE: initialize GLBuffer struct members to sane defaults
|
||||
for (u32 i = 0; i < max_ubos; i++) { |
||||
GLBuffer& buf = gl_ctx->uniform_buffers[i]; |
||||
buf.location = -1; |
||||
buf.binding_idx = -1; |
||||
} |
||||
|
||||
gl_ctx->max_textures = max_textures; |
||||
gl_ctx->textures = ARENA_ALLOC(arena, GLTexture, max_textures); |
||||
|
||||
// NOTE: load a dummy shader to avoid chicken and egg problem where we need
|
||||
// GLContext info before we can parse a shader, which needs GLContext info
|
||||
loadDummyShader(); |
||||
initCTXSizes(gl_ctx); |
||||
|
||||
return gl_ctx; |
||||
} |
||||
|
||||
bool |
||||
addShaderProgram(MemoryArena* arena, |
||||
GLContext* gl_ctx, |
||||
const char* vs, |
||||
const char* fs, |
||||
const char* name) |
||||
{ |
||||
LOGF(Info, "loading shader, %s\n", name); |
||||
const u32 max_len = 256; |
||||
char input_str[max_len]; |
||||
snprintf(input_str, max_len, "%s%s", vs, fs); |
||||
u64 hash = utilFNV64a_str(input_str); |
||||
|
||||
if (getShaderByHash(gl_ctx, hash)) { |
||||
LOGF(Error, "shader is already loaded\n"); |
||||
return false; |
||||
} |
||||
|
||||
ShaderProgram* s = getFreeShader(gl_ctx); |
||||
|
||||
if (s) { |
||||
s->name = arenaCopyCStr(arena, name); |
||||
s->hash = hash; |
||||
|
||||
size_t vs_size, fs_size; |
||||
const char* v_str = (const char*) SDL_LoadFile(vs, &vs_size); |
||||
const char* f_str = (const char*) SDL_LoadFile(fs, &fs_size); |
||||
GLuint vs_id, fs_id; |
||||
|
||||
if (compileAndLinkShader(s, v_str, f_str, vs_id, fs_id)) { |
||||
glDetachShader(s->prog_id, vs_id); |
||||
glDetachShader(s->prog_id, fs_id); |
||||
glDeleteShader(vs_id); |
||||
glDeleteShader(fs_id); |
||||
|
||||
return parseShader(arena, gl_ctx, s); |
||||
} |
||||
|
||||
LOGF(Error, "Error linking shader\n"); |
||||
return false; |
||||
} |
||||
|
||||
LOGF(Error, "error loading shader\n"); |
||||
return false; |
||||
} |
||||
|
||||
ShaderProgram* |
||||
getFreeShader(GLContext* gl_ctx) |
||||
{ |
||||
if (gl_ctx->num_shaders >= gl_ctx->max_shaders) { |
||||
LOGF(Error, "GLContext->shaders full\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
ShaderProgram* s = &gl_ctx->shaders[gl_ctx->num_shaders++]; |
||||
return s; |
||||
} |
||||
|
||||
ShaderProgram* |
||||
getShaderByHash(GLContext* gl_ctx, u64 hash) |
||||
{ |
||||
for (u32 i; i < gl_ctx->num_shaders; i++) { |
||||
if (gl_ctx->shaders[i].hash == hash) |
||||
return &gl_ctx->shaders[i]; |
||||
} |
||||
|
||||
return nullptr; |
||||
} |
||||
|
||||
ShaderProgram* |
||||
getShaderByName(const char* name, GLContext* gl_ctx) |
||||
{ |
||||
for (u32 i = 0; i < gl_ctx->num_shaders; i++) { |
||||
if (utilCStrMatch(name, gl_ctx->shaders[i].name)) |
||||
return &gl_ctx->shaders[i]; |
||||
} |
||||
|
||||
LOGF(Error, "shader not found, %s\n", name); |
||||
return nullptr; |
||||
} |
||||
|
||||
ShaderProgram* |
||||
getShaderByID(GLContext* gl_ctx, GLuint prog_id) |
||||
{ |
||||
for (u32 i = 0; i < gl_ctx->num_shaders; i++) { |
||||
if (gl_ctx->shaders[i].prog_id) |
||||
return &gl_ctx->shaders[i]; |
||||
} |
||||
|
||||
LOGF(Error, "shader not found, %d\n", prog_id); |
||||
return nullptr; |
||||
} |
||||
|
||||
GLBuffer* |
||||
getFreeUBO(GLContext* gl_ctx) |
||||
{ |
||||
if (gl_ctx->num_ubos < gl_ctx->max_ubos) |
||||
return &gl_ctx->uniform_buffers[gl_ctx->num_ubos++]; |
||||
|
||||
LOGF(Error, "no free Uniform Buffer Objects\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
GLBuffer* |
||||
getUBOByName(GLContext* gl_ctx, const char* name) |
||||
{ |
||||
GLBuffer* ubo_out = nullptr; |
||||
|
||||
for (u32 i = 0; i < gl_ctx->num_ubos; i++) { |
||||
GLBuffer* buf = &gl_ctx->uniform_buffers[i]; |
||||
|
||||
if (utilCStrMatch(name, buf->name)) |
||||
ubo_out = buf; |
||||
} |
||||
|
||||
if (ubo_out == nullptr) |
||||
LOGF(Error, "GLBuffer, \"%s\", not found\n", name); |
||||
|
||||
return ubo_out; |
||||
} |
||||
|
||||
GLTexture* |
||||
getGLTexture(GLContext* gl_ctx, Texture* diffuse_img) |
||||
{ |
||||
u64 fp_hash = utilFNV64a_str(diffuse_img->file_path); |
||||
|
||||
for (u32 i = 0; i < gl_ctx->num_textures; i++) { |
||||
GLTexture* glt = &gl_ctx->textures[i]; |
||||
|
||||
if (glt->filepath_hash == fp_hash) |
||||
return glt; |
||||
} |
||||
|
||||
GLTexture* glt = getFreeGLTexture(gl_ctx); |
||||
if (!glt) return nullptr; |
||||
|
||||
glt->pixel_format = (diffuse_img->num_channels == 3) ? GL_RGB : GL_RGBA; |
||||
glt->width = diffuse_img->w; |
||||
glt->height = diffuse_img->h; |
||||
glt->filepath_hash = diffuse_img->filepath_hash; |
||||
|
||||
if (loadGLTexture(diffuse_img, glt->id)) |
||||
return glt; |
||||
|
||||
LOGF(Error, "Error, unable to load texture\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
void |
||||
updateGLBuffer(GLBuffer* gl_buf, void* data) |
||||
{ |
||||
assert(gl_buf && data); |
||||
glBindBuffer(gl_buf->target, gl_buf->id); |
||||
glBufferSubData(gl_buf->target, 0, gl_buf->data_size, data); |
||||
} |
||||
|
||||
void |
||||
renderVAO(GLMesh* glmesh, |
||||
glm::mat4* node_xform, |
||||
ShaderProgram* shader, |
||||
GLTexture* gl_tex) |
||||
{ |
||||
glUseProgram(shader->prog_id); |
||||
glBindVertexArray(glmesh->vao_id); |
||||
|
||||
for (u32 i = 0; i < shader->num_uniforms; i++) { |
||||
const GLUniform& uniform = shader->uniforms[i]; |
||||
|
||||
if (uniform.uniform_type == UNIFORM_NODE_XFORM) { |
||||
glUniformMatrix4fv(uniform.location, 1, GL_FALSE, |
||||
(float*) node_xform); |
||||
} |
||||
|
||||
else if (glmesh->has_texture && |
||||
uniform.uniform_type == UNIFORM_SAMPLER) |
||||
{ |
||||
glBindTexture(GL_TEXTURE_2D, gl_tex->id); |
||||
glUniform1i(uniform.location, 0); |
||||
} |
||||
} |
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glmesh->element_buf->id); |
||||
glDrawElements( |
||||
glmesh->draw_mode, glmesh->num_indices, GL_UNSIGNED_SHORT, 0); |
||||
glBindVertexArray(0); |
||||
} |
||||
|
||||
void |
||||
initTransforms(MemoryArena* arena, |
||||
Transforms* xforms, |
||||
GLBuffer* xform_ubo, |
||||
GLContext* gl_ctx, |
||||
float fov, |
||||
float near_clip_plane, |
||||
float aspect_ratio) |
||||
{ |
||||
xforms->proj_xform = glm::infinitePerspective( |
||||
glm::radians(fov), aspect_ratio, near_clip_plane); |
||||
|
||||
glGenBuffers(1, &xform_ubo->id); |
||||
xform_ubo->target = GL_UNIFORM_BUFFER; |
||||
xform_ubo->data_type = GL_FLOAT; |
||||
xform_ubo->name = arenaCopyCStr(arena, "matrices"); |
||||
xform_ubo->data_size = sizeof(*xforms); |
||||
|
||||
glBindBuffer(xform_ubo->target, xform_ubo->id); |
||||
glBufferData(xform_ubo->target, xform_ubo->data_size, xforms, |
||||
GL_DYNAMIC_DRAW); |
||||
|
||||
// NOTE: bindbufferbase
|
||||
xform_ubo->binding_idx = gl_ctx->binding_count++; |
||||
glBindBufferBase(xform_ubo->target, xform_ubo->binding_idx, xform_ubo->id); |
||||
glBindBuffer(xform_ubo->target, 0); |
||||
} |
||||
|
||||
GLVertexAttrib* |
||||
getVertexAttribByName(ShaderProgram* shader, const char* name) |
||||
{ |
||||
for (u32 i = 0; i < shader->num_vertex_attribs; i++) { |
||||
if (strncmp(shader->vertex_attribs[i].name, name, 256) == 0) |
||||
return &shader->vertex_attribs[i]; |
||||
} |
||||
|
||||
LOGF(Debug, "attribute: %s, not found on shader: %s\n", name, shader->name); |
||||
return nullptr; |
||||
} |
||||
|
||||
GLMesh |
||||
initGLMesh(MemoryArena* arena, |
||||
const Mesh& m, |
||||
u32 num_mappings, |
||||
GLenum draw_mode) |
||||
{ |
||||
GLMesh glm = {0}; |
||||
glm.num_indices = m.num_indices; |
||||
glm.draw_mode = draw_mode; |
||||
glm.usage = GL_STATIC_DRAW; // TODO: logic for updating dynamic meshes
|
||||
glm.num_vertex_attrib_buffers = num_mappings; |
||||
glm .vertex_attrib_buffers = ARENA_ALLOC(arena, GLBuffer, num_mappings); |
||||
glm.element_buf = ARENA_ALLOC(arena, GLBuffer, 1); |
||||
glm.node_xform = ARENA_ALLOC(arena, glm::mat4, 1); |
||||
*glm.node_xform = glm::mat4(1); |
||||
|
||||
return glm; |
||||
} |
||||
|
||||
void |
||||
initGLAttribBuffer(GLBuffer* buf, GLenum target, GLVertexAttrib* attrib) |
||||
{ |
||||
glGenBuffers(1, &buf->id); |
||||
buf->target = target; |
||||
buf->data_type = attrib->data_type; |
||||
buf->location = attrib->location; |
||||
buf->name = utilAllocateCStr(attrib->name); |
||||
} |
||||
|
||||
// TODO: might as well pass in pointer to GLMesh, since that's how we're
|
||||
// going to use this, can void copying GLMesh twice that way
|
||||
GLMesh |
||||
loadGLMesh(MemoryArena* arena, |
||||
const Mesh& m, |
||||
GLenum draw_mode, |
||||
GLTexture* diffuse_texture, |
||||
u32 num_mappings, |
||||
GLBufferToAttribMapping mappings[]) |
||||
{ |
||||
GLMesh glm = initGLMesh(arena, m, num_mappings, draw_mode); |
||||
|
||||
if (diffuse_texture) { |
||||
glm.has_texture = true; |
||||
glm.tex_id = diffuse_texture->id; |
||||
} |
||||
|
||||
glGenVertexArrays(1, &glm.vao_id); |
||||
glBindVertexArray(glm.vao_id); |
||||
|
||||
for (u32 i = 0; i < num_mappings; i++) { |
||||
GLBuffer& buf = glm.vertex_attrib_buffers[i]; |
||||
GLVertexAttrib* attrib = mappings[i].attrib; |
||||
attrib->buf_type = mappings[i].buf_type; |
||||
u32 type_size = getGLTypeSize(attrib->data_type); |
||||
assert(type_size > 0); |
||||
buf.data_size = m.num_vertices * type_size; |
||||
|
||||
void* mesh_buf_data = getMeshData(m, mappings[i]); |
||||
assert(mesh_buf_data); |
||||
initGLAttribBuffer(&buf, GL_ARRAY_BUFFER, attrib); |
||||
glBindBuffer(buf.target, buf.id); |
||||
glBufferData(buf.target, |
||||
buf.data_size, |
||||
mesh_buf_data, |
||||
glm.usage); |
||||
glVertexAttribPointer(attrib->location, attrib->num_components, |
||||
attrib->component_type, GL_FALSE, 0, 0); |
||||
glEnableVertexAttribArray(attrib->location); |
||||
} |
||||
|
||||
glGenBuffers(1, &glm.element_buf->id); |
||||
glm.element_buf->target = GL_ELEMENT_ARRAY_BUFFER; |
||||
glm.element_buf->data_type = GL_UNSIGNED_SHORT; |
||||
glm.element_buf->data_size = m.num_indices * sizeof(u16); |
||||
glBindBuffer(glm.element_buf->target, glm.element_buf->id); |
||||
glBufferData(glm.element_buf->target, |
||||
glm.element_buf->data_size, |
||||
m.indices, |
||||
glm.usage); |
||||
|
||||
// TODO: many of these GL functions can set an error state
|
||||
// TODO: return error status
|
||||
glBindVertexArray(0); |
||||
|
||||
return glm; |
||||
} |
||||
|
||||
|
||||
// NOTE: internal
|
||||
|
||||
void* |
||||
getMeshData(const Mesh& m, const GLBufferToAttribMapping& mapping) |
||||
{ |
||||
switch (mapping.buf_type) { |
||||
case VERTEX: return m.vertices; |
||||
case NORMAL: return m.normals; |
||||
case UV: return m.uvs; |
||||
case COLOR: return m.colors; |
||||
default: return nullptr; |
||||
} |
||||
} |
||||
|
||||
GLTexture* |
||||
getFreeGLTexture(GLContext* gl_ctx) |
||||
{ |
||||
if (gl_ctx->num_textures < gl_ctx->max_textures) |
||||
return &gl_ctx->textures[gl_ctx->num_textures++]; |
||||
|
||||
LOGF(Error, "no free textures\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
bool |
||||
loadGLTexture(Texture* image, GLuint& tex_id) |
||||
{ |
||||
glGenTextures(1, &tex_id); |
||||
glBindTexture(GL_TEXTURE_2D, tex_id); |
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
||||
GLenum pixel_format = (image->num_channels == 3) ? GL_RGB : GL_RGBA; |
||||
glTexImage2D(GL_TEXTURE_2D, 0, pixel_format, image->w, image->h, 0, |
||||
pixel_format, GL_UNSIGNED_BYTE, image->pixels); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||||
|
||||
return (glGetError() == GL_NO_ERROR); |
||||
} |
||||
|
||||
bool |
||||
compileAndLinkShader(ShaderProgram* shader, |
||||
const char* vert_src, |
||||
const char* frag_src, |
||||
GLuint& vs_id, |
||||
GLuint& fs_id) |
||||
{ |
||||
if (vert_src && frag_src && strlen(vert_src) > 0 && strlen(frag_src) > 0) { |
||||
vs_id = glCreateShader(GL_VERTEX_SHADER); |
||||
fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
||||
glShaderSource(vs_id, 1, &vert_src, NULL); |
||||
glShaderSource(fs_id, 1, &frag_src, NULL); |
||||
|
||||
glCompileShader(vs_id); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
glCompileShader(fs_id); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
|
||||
shader->prog_id = glCreateProgram(); |
||||
glAttachShader(shader->prog_id, vs_id); |
||||
glAttachShader(shader->prog_id, fs_id); |
||||
|
||||
glLinkProgram(shader->prog_id); |
||||
GLint is_linked = 0; |
||||
glGetProgramiv(shader->prog_id, GL_LINK_STATUS, &is_linked); |
||||
|
||||
// NOTE: log shader linking errors
|
||||
if (is_linked != GL_TRUE) { |
||||
const u32 max_len = 512; |
||||
char err_str[max_len]; |
||||
i32 write_len; |
||||
glGetProgramInfoLog( |
||||
shader->prog_id, max_len, &write_len, &err_str[0]); |
||||
LOGF(Error, "Info Log: %s\n", err_str); |
||||
glDeleteProgram(shader->prog_id); |
||||
} |
||||
|
||||
return (is_linked == GL_TRUE); |
||||
} |
||||
|
||||
LOGF(Error, "empty shader source\n"); |
||||
return false; |
||||
} |
||||
|
||||
void |
||||
loadDummyShader() |
||||
{ |
||||
GLuint vs_id = 0, fs_id = 0; |
||||
ShaderProgram temp_shader = {0}; |
||||
bool ret = compileAndLinkShader(&temp_shader, |
||||
DUMMY_VERTEX_SHADER, |
||||
DUMMY_FRAGMENT_SHADER, |
||||
vs_id, |
||||
fs_id); |
||||
assert(ret); |
||||
glDeleteProgram(temp_shader.prog_id); |
||||
} |
||||
|
||||
u32 |
||||
getGLTypeSize(GLenum e) |
||||
{ |
||||
switch (e) { |
||||
case GL_FLOAT_VEC2: return 2 * sizeof(GLfloat); |
||||
case GL_FLOAT_VEC3: return 3 * sizeof(GLfloat); |
||||
case GL_FLOAT_VEC4: return 4 * sizeof(GLfloat); |
||||
case GL_FLOAT_MAT4: return 16 * sizeof(GLfloat); |
||||
default: |
||||
LOGF(Error, "unknown GLenum\n"); |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
// NOTE: returns sizes based on GLSL layout std140
|
||||
// https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
|
||||
u32 |
||||
getGLTypeSizeStd140(GLenum e) |
||||
{ |
||||
switch (e) { |
||||
case GL_FLOAT_VEC3: return 4 * sizeof(GLfloat); |
||||
case GL_FLOAT_VEC4: return 4 * sizeof(GLfloat); |
||||
case GL_FLOAT_MAT4: return 16 * sizeof(GLfloat); |
||||
default: |
||||
LOGF(Error, "unknown GLenum\n"); |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
UniformType |
||||
getUniformType(const char* name) |
||||
{ |
||||
if (utilCStrMatch(name, "sampler")) |
||||
return UNIFORM_SAMPLER; |
||||
else if (utilCStrMatch(name, "node_xform")) |
||||
return UNIFORM_NODE_XFORM; |
||||
else if (utilCStrMatch(name, "normal_xform")) |
||||
return UNIFORM_NORMAL_XFORM; |
||||
else if (utilCStrMatch(name, "view_xform")) |
||||
return UNIFORM_VIEW_XFORM; |
||||
else if (utilCStrMatch(name, "proj_xform")) |
||||
return UNIFORM_PROJECTION_XFORM; |
||||
else if (utilCStrMatch(name, "matrices")) |
||||
return UNIFORM_BLOCK_XFORMS; |
||||
else if (utilCStrMatch(name, "lights")) |
||||
return UNIFORM_BLOCK_LIGHTS; |
||||
else |
||||
return UNIFORM_UNKNOWN; |
||||
} |
||||
|
||||
const GLUniform |
||||
parseUniform(MemoryArena* arena, ShaderProgram* s, u32 uniform_idx) |
||||
{ |
||||
GLUniform unif = {0}; |
||||
GLchar unif_name[256] = {0}; |
||||
GLsizei name_len = 0; |
||||
unif.idx = uniform_idx; |
||||
|
||||
glGetActiveUniform(s->prog_id, |
||||
uniform_idx, |
||||
sizeof(unif_name), |
||||
&name_len, |
||||
&unif.num_elements, |
||||
&unif.gl_type, |
||||
unif_name); |
||||
|
||||
glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_BLOCK_INDEX, |
||||
&unif.block_idx); |
||||
glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_ARRAY_STRIDE, |
||||
&unif.array_stride); |
||||
glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_OFFSET, |
||||
&unif.uniform_offset); |
||||
|
||||
unif.name = arenaCopyCStr(arena, unif_name); |
||||
unif.uniform_type = getUniformType(unif.name); |
||||
unif.location = glGetUniformLocation(s->prog_id, unif.name); |
||||
|
||||
return unif; |
||||
} |
||||
|
||||
bool |
||||
parseShaderUniforms(MemoryArena* arena, ShaderProgram* s, GLContext* gl_ctx) |
||||
{ |
||||
// NOTE: only add uniforms in the default block to the base uniform array
|
||||
GLint num_uniforms_total = 0; |
||||
glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORMS, &num_uniforms_total); |
||||
GLint indices[num_uniforms_total]; |
||||
|
||||
for (u32 i = 0; i < (u32) num_uniforms_total; i++) { |
||||
GLint block_idx = 0; |
||||
glGetActiveUniformsiv(s->prog_id, 1, &i, |
||||
GL_UNIFORM_BLOCK_INDEX, &block_idx); |
||||
|
||||
if (block_idx == -1) { |
||||
indices[s->num_uniforms] = i; |
||||
s->num_uniforms++; |
||||
} |
||||
} |
||||
|
||||
s->uniforms = ARENA_ALLOC(arena, GLUniform, s->num_uniforms); |
||||
|
||||
for (u32 i = 0; i < s->num_uniforms; i++) { |
||||
const GLUniform unif = parseUniform(arena, s, indices[i]); |
||||
std::memcpy(&s->uniforms[i], &unif, sizeof(unif)); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void |
||||
initCTXSizes(GLContext* gl_ctx) |
||||
{ |
||||
// NOTE: see https://docs.gl/gl3/glGet for other useful context info
|
||||
if (gl_ctx->max_binding_points == 0) { |
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, |
||||
&gl_ctx->max_binding_points); |
||||
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &gl_ctx->max_vertex_blocks); |
||||
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, |
||||
&gl_ctx->max_fragment_blocks); |
||||
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gl_ctx->max_ublock_size); |
||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_ctx->max_vertex_attribs); |
||||
|
||||
#if 1 |
||||
LOGF(Debug, "context size info set\n"); |
||||
LOGF(Debug, "GL_MAX_UNIFORM_BUFFER_BINDINGS: %d\n", |
||||
gl_ctx->max_binding_points); |
||||
LOGF(Debug, "GL_MAX_VERTEX_UNIFORM_BLOCKS: %d\n", |
||||
gl_ctx->max_vertex_blocks); |
||||
LOGF(Debug, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS: %d\n", |
||||
gl_ctx->max_fragment_blocks); |
||||
LOGF(Debug, "GL_MAX_UNIFORM_BLOCK_SIZE: %d\n", |
||||
gl_ctx->max_ublock_size); |
||||
LOGF(Debug, "GL_MAX_VERTEX_ATTRIBS: %d\n", gl_ctx->max_vertex_attribs); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
i32 |
||||
ctxGetUniformBlockBinding(GLContext* gl_ctx, const char* name) |
||||
{ |
||||
for (u32 i = 0; i < gl_ctx->num_ubos; i++) { |
||||
GLBuffer& ubo = gl_ctx->uniform_buffers[i]; |
||||
|
||||
if (std::strstr(ubo.name, name)) |
||||
return ubo.binding_idx; |
||||
} |
||||
|
||||
LOGF(Error, "no buffer found with name: %s\n", name); |
||||
return -1; |
||||
} |
||||
|
||||
bool |
||||
parseUniformBlocks(MemoryArena* arena, ShaderProgram* s, GLContext* gl_ctx) |
||||
{ |
||||
glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORM_BLOCKS, |
||||
(GLint*) &s->num_blocks); |
||||
s->uniform_blocks = ARENA_ALLOC(arena, GLUniformBlock, s->num_blocks); |
||||
|
||||
for (u32 i = 0; i < s->num_blocks; i++) { |
||||
GLUniformBlock& ub = s->uniform_blocks[i]; |
||||
ub.block_id = i; |
||||
|
||||
GLchar block_name[256] = {0}; |
||||
GLsizei name_len = 0; |
||||
glGetActiveUniformBlockName( |
||||
s->prog_id, i, 256, &name_len, block_name); |
||||
ub.name = arenaCopyCStr(arena, block_name); |
||||
ub.uniform_type = getUniformType(ub.name); |
||||
|
||||
glGetActiveUniformBlockiv(s->prog_id, i, |
||||
GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, (GLint*) &ub.num_uniforms); |
||||
ub.uniforms = ARENA_ALLOC(arena, GLUniform, ub.num_uniforms); |
||||
GLint indices[ub.num_uniforms] = {0}; |
||||
glGetActiveUniformBlockiv(s->prog_id, i, |
||||
GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, (GLint*) &indices); |
||||
|
||||
for (u32 j = 0; j < ub.num_uniforms; j++) { |
||||
const GLUniform unif = parseUniform(arena, s, indices[j]); |
||||
std::memcpy(&ub.uniforms[j], &unif, sizeof(unif)); |
||||
} |
||||
|
||||
ub.binding_idx = ctxGetUniformBlockBinding(gl_ctx, ub.name); |
||||
|
||||
if (ub.binding_idx < 0) |
||||
return false; |
||||
|
||||
glUniformBlockBinding(s->prog_id, i, ub.binding_idx); |
||||
} |
||||
|
||||
// TODO: would be helpful for debugging if we sort the uniforms in a block
|
||||
// by their uniform_offset instead of their idx
|
||||
|
||||
return true; |
||||
} |
||||
|
||||
u32 |
||||
getNumAttribComponents(GLenum type) |
||||
{ |
||||
switch (type) { |
||||
case GL_FLOAT_VEC3: return 3; |
||||
case GL_FLOAT_VEC2: return 2; |
||||
default: |
||||
LOGF(Error, "unknown GLenum\n"); |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
GLenum |
||||
getAttribComponentType(GLenum type) |
||||
{ |
||||
switch (type) { |
||||
case GL_FLOAT_VEC3: return GL_FLOAT; |
||||
case GL_FLOAT_VEC2: return GL_FLOAT; |
||||
default: |
||||
LOGF(Error, "unknown GLenum\n"); |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
bool |
||||
parseAttributes(MemoryArena* arena, ShaderProgram* s, GLContext* gl_ctx) |
||||
{ |
||||
GLint num_attribs; |
||||
glGetProgramiv(s->prog_id, GL_ACTIVE_ATTRIBUTES, &num_attribs); |
||||
s->num_vertex_attribs = num_attribs; |
||||
s->vertex_attribs = ARENA_ALLOC(arena, GLVertexAttrib, num_attribs); |
||||
s->attrib_mappings = |
||||
ARENA_ALLOC(arena, GLBufferToAttribMapping, num_attribs); |
||||
|
||||
GLchar attrib_name[256] = {0}; |
||||
GLsizei length; |
||||
GLint size; |
||||
GLenum type; |
||||
|
||||
for (int i = 0; i < num_attribs; i++) { |
||||
glGetActiveAttrib(s->prog_id, i, sizeof(attrib_name), |
||||
&length, &size, &type, attrib_name); |
||||
GLint location = glGetAttribLocation(s->prog_id, attrib_name); |
||||
GLVertexAttrib* attrib = &s->vertex_attribs[i]; |
||||
attrib->data_type = type; |
||||
attrib->location = location; |
||||
attrib->num_components = getNumAttribComponents(type); |
||||
assert(attrib->num_components > 0); |
||||
attrib->component_type = getAttribComponentType(type); |
||||
assert(attrib->component_type > 0); |
||||
attrib->name = arenaCopyCStr(arena, attrib_name, sizeof(attrib_name)); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool |
||||
parseShader(MemoryArena* arena, GLContext* gl_ctx, ShaderProgram* s) |
||||
{ |
||||
if (parseShaderUniforms(arena, s, gl_ctx) |
||||
&& parseUniformBlocks(arena, s, gl_ctx) |
||||
&& parseAttributes(arena, s, gl_ctx)) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
LOGF(Error, "Error parsing shader\n"); |
||||
return false; |
||||
} |
||||
|
||||
@ -1,146 +0,0 @@
|
||||
|
||||
#include "dumbLog.h" |
||||
#include "shader_program.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
// forward declarations
|
||||
bool checkShaderErrors(GLuint program_id); |
||||
void cleanUpShader(GLuint program_id, GLuint vs_id, GLuint fs_id); |
||||
bool compileProgram(GLuint& program_id_out, |
||||
GLuint& vs_id_out, |
||||
GLuint& fs_id_out, |
||||
const char* vertex_code, |
||||
const char* frag_code); |
||||
|
||||
|
||||
//interface
|
||||
|
||||
default_shader_program* |
||||
shaderInitDefault(const char* vertex_code, const char* frag_code) |
||||
{ |
||||
LOG(Info) << "loading default shader\n"; |
||||
default_shader_program* sp = UTIL_ALLOC(1, default_shader_program); |
||||
GLuint vs_id = 0; |
||||
GLuint fs_id = 0; |
||||
|
||||
compileProgram(sp->program_id, vs_id, fs_id, vertex_code, frag_code); |
||||
glGenVertexArrays(1, &sp->vertex_array_id); |
||||
glBindVertexArray(sp->vertex_array_id); |
||||
|
||||
// assign uniforms
|
||||
sp->world_transform_id = |
||||
glGetUniformLocation(sp->program_id, "world_transform"); |
||||
sp->model_matrix_id = glGetUniformLocation(sp->program_id, "model"); |
||||
sp->view_matrix_id = glGetUniformLocation(sp->program_id, "view"); |
||||
sp->projection_matrix_id = |
||||
glGetUniformLocation(sp->program_id, "projection"); |
||||
sp->normal_matrix_id = |
||||
glGetUniformLocation(sp->program_id, "normal_matrix"); |
||||
// FIXME: re-enable textures
|
||||
#if 0 |
||||
sp->num_lights_id = glGetUniformLocation(sp->program_id, "num_lights"); |
||||
sp->sampler_id = glGetUniformLocation(sp->program_id, "sampler"); |
||||
#endif |
||||
|
||||
cleanUpShader(sp->program_id, vs_id, fs_id); |
||||
|
||||
if (!checkShaderErrors(sp->program_id)) { |
||||
glDeleteProgram(sp->program_id); |
||||
utilSafeFree(sp); |
||||
return nullptr; |
||||
} |
||||
|
||||
return sp; |
||||
} |
||||
|
||||
simple_shader_program* |
||||
shaderInitSimple(const char* vertex_code, const char* frag_code) |
||||
{ |
||||
LOG(Info) << "loading simple shader\n"; |
||||
simple_shader_program* sp = UTIL_ALLOC(1, simple_shader_program); |
||||
GLuint vs_id = 0; |
||||
GLuint fs_id = 0; |
||||
compileProgram(sp->program_id, vs_id, fs_id, vertex_code, frag_code); |
||||
glGenVertexArrays(1, &sp->vertex_array_id); |
||||
glBindVertexArray(sp->vertex_array_id); |
||||
|
||||
// assign uniforms
|
||||
sp->world_transform_id = |
||||
glGetUniformLocation(sp->program_id, "world_transform"); |
||||
sp->MVP_id = glGetUniformLocation(sp->program_id, "MVP"); |
||||
|
||||
if (!checkShaderErrors(sp->program_id)) { |
||||
glDeleteProgram(sp->program_id); |
||||
utilSafeFree(sp); |
||||
return nullptr; |
||||
} |
||||
|
||||
return sp; |
||||
} |
||||
|
||||
void |
||||
shaderFree(uint program_id) |
||||
{ |
||||
// TODO: can check for valid id here
|
||||
glDeleteProgram(program_id); |
||||
} |
||||
|
||||
|
||||
// internal
|
||||
|
||||
bool |
||||
checkShaderErrors(uint program_id) |
||||
{ |
||||
GLint isLinked = 0; |
||||
GLint info_len = 0; |
||||
glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked); |
||||
|
||||
if (isLinked == GL_FALSE) { |
||||
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_len); |
||||
char* infoLog = UTIL_ALLOC(info_len, char); |
||||
glGetProgramInfoLog(program_id, info_len, &info_len, &infoLog[0]); |
||||
LOG(Error) << infoLog << "\n"; |
||||
utilSafeFree(infoLog); |
||||
glDeleteProgram(program_id); |
||||
|
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void |
||||
cleanUpShader(GLuint program_id, GLuint vs_id, GLuint fs_id) |
||||
{ |
||||
glDetachShader(program_id, vs_id); |
||||
glDetachShader(program_id, fs_id); |
||||
glDeleteShader(vs_id); |
||||
glDeleteShader(fs_id); |
||||
} |
||||
|
||||
bool |
||||
compileProgram(GLuint& program_id_out, |
||||
GLuint& vs_id_out, |
||||
GLuint& fs_id_out, |
||||
const char* vertex_code, |
||||
const char* frag_code) |
||||
{ |
||||
vs_id_out = glCreateShader(GL_VERTEX_SHADER); |
||||
fs_id_out = glCreateShader(GL_FRAGMENT_SHADER); |
||||
|
||||
glShaderSource(vs_id_out, 1, &vertex_code, NULL); |
||||
glShaderSource(fs_id_out, 1, &frag_code, NULL); |
||||
glCompileShader(vs_id_out); |
||||
glCompileShader(fs_id_out); |
||||
// TODO: can check for error here
|
||||
|
||||
program_id_out = glCreateProgram(); |
||||
glAttachShader(program_id_out, vs_id_out); |
||||
glAttachShader(program_id_out, fs_id_out); |
||||
glLinkProgram(program_id_out); |
||||
// TODO: can check for error here
|
||||
|
||||
return true; |
||||
} |
||||
|
||||
@ -0,0 +1,386 @@
|
||||
|
||||
#include <cassert> |
||||
|
||||
#include "tangerine.h" |
||||
#define UTIL_IMPLEMENTATION |
||||
#include "util.h" |
||||
|
||||
|
||||
// forward declarations
|
||||
|
||||
bool initGraphics(SDLHandles* handles); |
||||
|
||||
LightsBuffer* initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights, |
||||
glm::vec4 ambient_color); |
||||
|
||||
|
||||
// interface
|
||||
|
||||
RenderState* |
||||
initRenderState(GLClearColor clear_col, |
||||
glm::vec4 ambient_color, |
||||
u32 max_models, |
||||
u32 max_textures, |
||||
u32 max_shaders, |
||||
u32 max_render_groups, |
||||
u32 max_ubos, |
||||
u32 max_lights) |
||||
{ |
||||
LOGF(Info, "Initializing Renderer\n"); |
||||
RenderState* rs = UTIL_ALLOC(1, RenderState); |
||||
|
||||
if (rs) { |
||||
rs->clear_col = clear_col; |
||||
rs->assets.arena = arenaInit(DEFAULT_ARENA_SIZE); |
||||
rs->assets.max_models = max_models; |
||||
rs->assets.models = ARENA_ALLOC(rs->assets.arena, Model, max_models); |
||||
rs->assets.max_textures = max_textures; |
||||
rs->assets.textures = |
||||
ARENA_ALLOC(rs->assets.arena, Texture, max_textures); |
||||
|
||||
rs->rg_arena = arenaInit(DEFAULT_ARENA_SIZE); |
||||
rs->max_render_groups = DEFAULT_RENDER_GROUP_COUNT; |
||||
rs->render_groups = ARENA_ALLOC(rs->rg_arena, RenderGroup, |
||||
DEFAULT_RENDER_GROUP_COUNT); |
||||
|
||||
if (!initGraphics(&rs->handles)) { |
||||
LOGF(Error, "error initializing renderer\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
rs->gl_ctx = initGLContext(rs->assets.arena, |
||||
max_shaders, |
||||
max_textures, |
||||
max_ubos); |
||||
|
||||
rs->xforms = ARENA_ALLOC(rs->assets.arena, Transforms, 1); |
||||
GLBuffer* xforms_ubo = getFreeUBO(rs->gl_ctx); |
||||
assert(xforms_ubo); |
||||
initTransforms(rs->assets.arena, rs->xforms, xforms_ubo, rs->gl_ctx); |
||||
|
||||
rs->lights_buf = initLights(rs->rg_arena, rs->gl_ctx, max_lights, |
||||
ambient_color); |
||||
|
||||
bool ret = loadDefaultShaders(rs); |
||||
assert(ret); |
||||
} |
||||
|
||||
return rs; |
||||
} |
||||
|
||||
void |
||||
freeRenderState(RenderState*& rs) |
||||
{ |
||||
if (rs) { |
||||
SDL_GL_DeleteContext(rs->handles.sdl_gl_ctx); |
||||
SDL_DestroyWindow(rs->handles.window); |
||||
arenaFree(rs->assets.arena); |
||||
arenaFree(rs->rg_arena); |
||||
utilSafeFree(rs); |
||||
rs = nullptr; |
||||
} |
||||
|
||||
SDL_Quit(); |
||||
} |
||||
|
||||
void |
||||
initRenderGroup(RenderGroup* rg, |
||||
MemoryArena* arena, |
||||
ShaderProgram* shader, |
||||
u32 num_entities, |
||||
const char* name) |
||||
{ |
||||
rg->max_entities = num_entities; |
||||
rg->shader = shader; |
||||
rg->name = arenaCopyCStr(arena, name); |
||||
rg->entities = ARENA_ALLOC(arena, Entity, num_entities); |
||||
} |
||||
|
||||
void |
||||
freeRenderGroup(RenderGroup* rg, MemoryArena* arena) |
||||
{ |
||||
LOGF(Info, "should probably look into freeing arena memory?\n"); |
||||
assert(0); |
||||
} |
||||
|
||||
RenderGroup* |
||||
getFreeRenderGroup(RenderState* rs) |
||||
{ |
||||
if (rs->num_render_groups < rs->max_render_groups) |
||||
return &rs->render_groups[rs->num_render_groups++]; |
||||
|
||||
LOGF(Error, "no free render group\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
RenderGroup* |
||||
getRenderGroupByName(RenderState* rs, const char* name) |
||||
{ |
||||
RenderGroup* rg_out = nullptr; |
||||
|
||||
for (u32 i = 0; i < rs->num_render_groups; i++) { |
||||
RenderGroup* rg = &rs->render_groups[i]; |
||||
|
||||
if (utilCStrMatch(name, rg->name)) |
||||
rg_out = rg; |
||||
} |
||||
|
||||
if (rg_out == nullptr) |
||||
LOGF(Error, "render group with name, \"%s\", not found\n", name); |
||||
|
||||
return rg_out; |
||||
} |
||||
|
||||
Entity* |
||||
getFreeEntity(RenderGroup* rg) |
||||
{ |
||||
if (rg->num_entities < rg->max_entities) |
||||
return &rg->entities[rg->num_entities++]; |
||||
|
||||
LOGF(Error, "render group full\n"); |
||||
return nullptr; |
||||
} |
||||
|
||||
void |
||||
doRenderLoop(RenderState* rs, |
||||
u32 framerate, |
||||
frame_callback_fn cb_func_pre, |
||||
frame_callback_fn cb_func_post) |
||||
{ |
||||
u32 delay = (framerate > 0) ? 1 / framerate : 0; |
||||
u32 frameStart, frameTime; |
||||
rs->running = true; |
||||
SDL_Event e; |
||||
|
||||
while (rs->running) { |
||||
frameStart = SDL_GetTicks(); |
||||
|
||||
if (cb_func_pre != nullptr) { |
||||
cb_func_pre(rs); |
||||
} else { |
||||
while (SDL_PollEvent(&e)) { |
||||
if (e.type == SDL_QUIT || |
||||
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) |
||||
{ |
||||
rs->running = false; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
renderFrame(rs, rs->clear_col); |
||||
|
||||
if (cb_func_post != nullptr) |
||||
cb_func_post(rs); |
||||
|
||||
SDL_GL_SwapWindow(rs->handles.window); |
||||
frameTime = SDL_GetTicks() - frameStart; |
||||
|
||||
if (delay > frameTime) |
||||
SDL_Delay(delay - frameTime); |
||||
} |
||||
} |
||||
|
||||
void |
||||
renderFrame(RenderState* rs, const GLClearColor& clear_col) |
||||
{ |
||||
glClearColor(clear_col.R, |
||||
clear_col.G, |
||||
clear_col.B, |
||||
clear_col.A); |
||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
||||
|
||||
for (u32 i = 0; i < rs->num_render_groups; i++) { |
||||
RenderGroup* rg = &rs->render_groups[i]; |
||||
|
||||
for (u32 j = 0; j < rg->num_entities; j++) { |
||||
Entity* e = &rg->entities[j]; |
||||
|
||||
for (u32 k = 0; k < e->num_meshes; k++) { |
||||
GLMesh& glm = e->meshes[k]; |
||||
renderVAO(&glm, e->model_xform, rg->shader, |
||||
e->diffuse_texture); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool |
||||
loadDefaultShaders(RenderState* rs, |
||||
u32 num_shaders, |
||||
const ShaderInit shaders[]) |
||||
{ |
||||
for (u32 i = 0; i < num_shaders; i++) { |
||||
const ShaderInit& si = shaders[i]; |
||||
|
||||
if (!addShaderProgram(rs->assets.arena, |
||||
rs->gl_ctx, |
||||
si.vert_path, |
||||
si.frag_path, |
||||
si.name)) |
||||
{ |
||||
LOG(Error) << "failed to load shader " << si.name << "\n"; |
||||
return false; |
||||
} |
||||
|
||||
ShaderProgram* shader = getShaderByName(si.name, rs->gl_ctx); |
||||
assert(shader); |
||||
|
||||
// NOTE: not every buffer will be available for every shader, so we
|
||||
// enumerate them all, and store the ones that are present
|
||||
u32 attrib_idx = 0; |
||||
|
||||
for (u32 i = 0; i < MESH_BUFFER_TYPE_COUNT; i++) { |
||||
MeshBufferType buf_type = (MeshBufferType) i; |
||||
GLVertexAttrib* attrib = getVertexAttribByType(shader, buf_type); |
||||
|
||||
if (attrib) |
||||
shader->attrib_mappings[attrib_idx++] = { attrib, buf_type }; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
GLVertexAttrib* |
||||
getVertexAttribByType(ShaderProgram* shader, MeshBufferType buf_type) |
||||
{ |
||||
switch (buf_type) { |
||||
case VERTEX: return getVertexAttribByName(shader, "position"); |
||||
case NORMAL: return getVertexAttribByName(shader, "normal"); |
||||
case UV: return getVertexAttribByName(shader, "uv"); |
||||
case COLOR: return getVertexAttribByName(shader, "color"); |
||||
default: return nullptr; |
||||
} |
||||
} |
||||
|
||||
|
||||
// internal
|
||||
|
||||
bool |
||||
initGraphics(SDLHandles* handles) |
||||
{ |
||||
handles->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, &handles->display_mode); |
||||
|
||||
handles->sdl_gl_ctx = SDL_GL_CreateContext(handles->window); |
||||
|
||||
if (!handles->sdl_gl_ctx) { |
||||
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 handles->window != nullptr; |
||||
} |
||||
|
||||
LightsBuffer* |
||||
initLights(MemoryArena* arena, |
||||
GLContext* gl_ctx, |
||||
u32 max_lights, |
||||
glm::vec4 ambient_color) |
||||
{ |
||||
// FIXME: revisit for 'Scene' abstraction
|
||||
|
||||
LightsBuffer* lb = ARENA_ALLOC(arena, LightsBuffer, 1); |
||||
lb->buf_size = 8 * sizeof(u32) // NOTE: 'header'
|
||||
+ sizeof(glm::vec4) // NOTE: ambient color
|
||||
+ 6 * max_lights * sizeof(glm::vec4); // NOTE: vector arrays
|
||||
LOGF(Debug, "buf_size: %d\n", lb->buf_size); |
||||
lb->buffer = ARENA_ALLOC(arena, u8, lb->buf_size); |
||||
|
||||
lb->max_p_lights = (u32*) lb->buffer; |
||||
lb->active_p_lights = |
||||
(u32*) arenaGetAddressOffset(lb->max_p_lights, sizeof(u32)); |
||||
lb->max_d_lights = |
||||
(u32*) arenaGetAddressOffset(lb->active_p_lights, sizeof(u32)); |
||||
lb->active_d_lights = |
||||
(u32*) arenaGetAddressOffset(lb->max_d_lights, sizeof(u32)); |
||||
|
||||
*lb->max_p_lights = max_lights; |
||||
*lb->max_d_lights = max_lights; |
||||
|
||||
// NOTE: add padding, we're not actually using this since 4 * u32 is on a
|
||||
// 16 byte boundary, but will be helpful if we need to add more 'headers'
|
||||
// in the future
|
||||
void* arr_start = arenaGetAddressOffset(lb->buffer, 8 * sizeof(u32)); |
||||
|
||||
// NOTE: ambient color
|
||||
lb->ambient_color = (glm::vec4*) arr_start; |
||||
*lb->ambient_color = ambient_color; |
||||
|
||||
// NOTE: set offsets for array pointers
|
||||
u32 arr_size = max_lights * sizeof(glm::vec4); |
||||
lb->pl_positions = //(glm::vec4*) arr_start;
|
||||
(glm::vec4*) arenaGetAddressOffset(arr_start, sizeof(glm::vec4)); |
||||
lb->pl_colors = |
||||
(glm::vec4*) arenaGetAddressOffset(lb->pl_positions, arr_size); |
||||
lb->pl_intensities = |
||||
(glm::uvec4*) arenaGetAddressOffset(lb->pl_colors, arr_size); |
||||
lb->dl_directions = |
||||
(glm::vec4*) arenaGetAddressOffset(lb->pl_intensities, arr_size); |
||||
lb->dl_colors = |
||||
(glm::vec4*) arenaGetAddressOffset(lb->dl_directions, arr_size); |
||||
lb->dl_intensities = |
||||
(glm::uvec4*) arenaGetAddressOffset(lb->dl_colors, arr_size); |
||||
|
||||
GLBuffer* lights_ubo = getFreeUBO(gl_ctx); |
||||
assert(lights_ubo); |
||||
|
||||
lights_ubo->target = GL_UNIFORM_BUFFER; |
||||
lights_ubo->data_type = GL_BYTE; // NOTE: mixed types in structure
|
||||
lights_ubo->data_size = lb->buf_size; |
||||
lights_ubo->name = arenaCopyCStr(arena, "lights"); |
||||
assert((GLint) gl_ctx->binding_count < gl_ctx->max_binding_points); |
||||
lights_ubo->binding_idx = gl_ctx->binding_count++; |
||||
|
||||
glGenBuffers(1, &lights_ubo->id); |
||||
glBindBuffer(lights_ubo->target, lights_ubo->id); |
||||
glBufferData(lights_ubo->target, |
||||
lb->buf_size, |
||||
lb->buffer, |
||||
GL_DYNAMIC_DRAW); |
||||
glBindBufferBase(lights_ubo->target, |
||||
lights_ubo->binding_idx, |
||||
lights_ubo->id); |
||||
|
||||
return lb; |
||||
} |
||||
|
||||
@ -0,0 +1,5 @@
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION |
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION |
||||
#define TINYGLTF_IMPLEMENTATION |
||||
#include "tiny_gltf.h" |
||||
@ -1,197 +0,0 @@
|
||||
|
||||
#include <cassert> |
||||
#include <cmath> |
||||
#include <cstdlib> |
||||
#include <cstdio> |
||||
#include <cstring> |
||||
|
||||
#include "dumbLog.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
const uint MAX_FILESIZE = 2 * 1024 * 1024; // 2MB
|
||||
const uint MAX_STRING_LENGTH = 1024; |
||||
|
||||
|
||||
//-----------------
|
||||
// C Strings
|
||||
|
||||
const char* |
||||
utilBaseName(const char* path_str) |
||||
{ |
||||
assert(std::strlen(path_str) < MAX_STRING_LENGTH); |
||||
const char* output = std::strrchr(path_str, '/'); |
||||
if (output) |
||||
return output; |
||||
else |
||||
return path_str; |
||||
} |
||||
|
||||
bool |
||||
utilCopyCStr(char* dest, const char* src, uint max_len) |
||||
{ |
||||
assert(std::strlen(src) < MAX_STRING_LENGTH && max_len <= MAX_STRING_LENGTH); |
||||
if (std::strlen(src) + 1 > max_len) |
||||
return false; |
||||
|
||||
std::memcpy(dest, src, std::strlen(src) + 1); |
||||
return true; |
||||
} |
||||
|
||||
char* |
||||
utilConcatPath(char* out, const char* base_dir, const char* file_name, uint max_len) |
||||
{ |
||||
size_t l1 = std::strlen(base_dir); |
||||
size_t l2 = std::strlen(file_name); |
||||
size_t padded = l1 + l2 + 2; // NOTE: null term + '/'
|
||||
|
||||
assert(padded <= MAX_STRING_LENGTH && padded <= max_len); |
||||
int c = std::snprintf(out, padded, "%s/%s", base_dir, file_name); |
||||
assert(c > 0); |
||||
return out; |
||||
} |
||||
|
||||
bool |
||||
utilMatchPrefix(const char* lhs, const char* rhs, int sz) |
||||
{ |
||||
int rc = strncmp(lhs, rhs, sz); |
||||
return (rc >= 0); |
||||
} |
||||
|
||||
//-----------------
|
||||
// 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
|
||||
|
||||
void * |
||||
utilLogAlloc(uint item_count, uint type_size, const char* file_name, const int line) |
||||
{ |
||||
assert(item_count > 0); // that was a fun bug
|
||||
|
||||
void* mem = std::calloc(item_count, type_size); |
||||
|
||||
if (mem == nullptr) { |
||||
LOG(Error) << "Memory allocation failed, called from " |
||||
<< file_name << ":" << line; |
||||
} |
||||
|
||||
assert(mem != nullptr); // might as well stop execution here
|
||||
|
||||
return mem; |
||||
} |
||||
|
||||
void |
||||
utilSafeFree(const void* mem) |
||||
{ |
||||
if (mem != nullptr) std::free((void *) mem); |
||||
} |
||||
|
||||
memory_arena* |
||||
arenaInit(size_t initial_size) |
||||
{ |
||||
uint sz = sizeof(memory_arena); |
||||
memory_arena* arena = |
||||
(memory_arena*) UTIL_ALLOC(initial_size + sz, uint8_t); |
||||
arena->head = arena->next_free = (uint8_t*) arena + sz; |
||||
arena->max_size = initial_size; |
||||
return arena; |
||||
} |
||||
|
||||
void |
||||
arenaFree(memory_arena*& arena) |
||||
{ |
||||
utilSafeFree(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; |
||||
} |
||||
|
||||
//-----------------
|
||||
// File I/O
|
||||
|
||||
// TODO: don't use ftell() to get filesize
|
||||
// https://wiki.sei.cmu.edu/confluence/display/c/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size+of+a+regular+file
|
||||
char * |
||||
utilDumpTextFile(const char* filename) |
||||
{ |
||||
LOG(Info) << "loading filename, " << filename << "\n"; |
||||
std::FILE* fp = std::fopen(filename, "rt"); |
||||
assert(fp); |
||||
|
||||
std::fseek(fp, 0, SEEK_END); |
||||
uint length = std::ftell(fp); |
||||
std::fseek(fp, 0, SEEK_SET); |
||||
assert(length < MAX_FILESIZE); |
||||
// TODO: check error codes for fseek and ftell
|
||||
|
||||
char* buf = UTIL_ALLOC(length, char); |
||||
assert(buf); |
||||
|
||||
std::fread(buf, sizeof(char), length, fp); |
||||
// TODO: check fp w/ ferror() here
|
||||
|
||||
return buf; |
||||
} |
||||
|
||||
// TODO: might want to do the base_dir concat in this function to prevent clobbering
|
||||
// user files on accident
|
||||
bool |
||||
utilWriteTextFile(const char* filename, const char* text) |
||||
{ |
||||
size_t text_len = std::strlen(text); |
||||
|
||||
if (text_len >= MAX_FILESIZE) { |
||||
LOG(Error) << "that string is too big\n"; |
||||
return false; |
||||
} |
||||
|
||||
std::FILE* fp = fopen(filename, "wt"); |
||||
if (fp) { |
||||
size_t written = fwrite(text, sizeof(char), text_len, fp); |
||||
fclose(fp); |
||||
if (written == text_len) { |
||||
LOG(Debug) << "successfuly wrote " << written << " bytes\n"; |
||||
return true; |
||||
} else { |
||||
LOG(Error) << "error writing to file: " << filename << "\n"; |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
LOG(Debug) << text << "\n"; |
||||
return false; |
||||
} |
||||
|
||||
@ -1,59 +0,0 @@
|
||||
|
||||
#include <cstring> |
||||
|
||||
#include "stb_image.h" |
||||
|
||||
#include "dumbLog.h" |
||||
#include "util_image.h" |
||||
|
||||
|
||||
util_image parseSTBResult(util_image img); |
||||
|
||||
|
||||
util_image |
||||
utilLoadImagePath(const char* full_path) |
||||
{ |
||||
LOG(Info) << "Loading Image: " << full_path << "\n"; |
||||
|
||||
util_image image; |
||||
stbi_set_flip_vertically_on_load(1); |
||||
return parseSTBResult(image); |
||||
} |
||||
|
||||
util_image |
||||
utilLoadImageBytes(const uint8* bytes, uint length) |
||||
{ |
||||
LOG(Info) << "Loading binary image data\n"; |
||||
util_image image; |
||||
stbi_set_flip_vertically_on_load(1); |
||||
image.pixels = stbi_load_from_memory(bytes, length, &image.w, &image.h, &image.num_channels, 0); |
||||
return parseSTBResult(image); |
||||
} |
||||
|
||||
void |
||||
utilFreeImage(util_image image) |
||||
{ |
||||
image.w = image.h = image.data_len = 0; |
||||
image.bits_per_channel = 8; |
||||
image.num_channels = 4; |
||||
std::memset(image.file_path, 0, std::strlen(image.file_path) + 1); |
||||
utilSafeFree(image.pixels); |
||||
} |
||||
|
||||
util_image |
||||
parseSTBResult(util_image image) |
||||
{ |
||||
image.bits_per_channel = 8; |
||||
image.data_len = image.w * image.h * image.num_channels; // NOTE: assumes 8 bits per channel
|
||||
|
||||
if (image.pixels == 0) { |
||||
LOG(Error) << stbi_failure_reason() << "\n"; |
||||
utilFreeImage(image); |
||||
return image; |
||||
} |
||||
|
||||
LOG(Info) << "Image properties, data_len: " << image.data_len |
||||
<< ", width: " << image.w << ", height: " << image.h << "\n"; |
||||
|
||||
return image; |
||||
} |
||||
Loading…
Reference in new issue