Compare commits
No commits in common. 'main' and 'render_group_fix' have entirely different histories.
main
...
render_gro
67 changed files with 3503 additions and 3074 deletions
@ -1,8 +1,4 @@ |
|||||||
*.png filter=lfs diff=lfs merge=lfs -text |
*.png filter=lfs diff=lfs merge=lfs -text |
||||||
*.glb filter=lfs diff=lfs merge=lfs -text |
*.glb filter=lfs diff=lfs merge=lfs -text |
||||||
*.blend filter=lfs diff=lfs merge=lfs -text |
*.blend filter=lfs diff=lfs merge=lfs -text |
||||||
examples/data/spaceship.bin filter=lfs diff=lfs merge=lfs -text |
examples/data/* filter=lfs diff=lfs merge=lfs -text |
||||||
examples/data/textured_cube.bin filter=lfs diff=lfs merge=lfs -text |
|
||||||
examples/data/spaceship.gltf filter=lfs diff=lfs merge=lfs -text |
|
||||||
examples/data/textured_cube.gltf filter=lfs diff=lfs merge=lfs -text |
|
||||||
examples/data/Color[[:space:]]Palette[[:space:]]140.png filter=lfs diff=lfs merge=lfs -text |
|
||||||
|
|||||||
@ -0,0 +1,72 @@ |
|||||||
|
|
||||||
|
#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"; |
||||||
|
} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
|
||||||
|
#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.
@ -0,0 +1,133 @@ |
|||||||
|
{ |
||||||
|
"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.
@ -0,0 +1,453 @@ |
|||||||
|
{ |
||||||
|
"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" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
@ -1,11 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
in vec3 frag_color; |
|
||||||
|
|
||||||
out vec4 color; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
color = vec4(frag_color, 1); |
|
||||||
} |
|
||||||
@ -1,22 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
layout (location = 0) in vec3 position; |
|
||||||
layout (location = 1) in vec3 color; |
|
||||||
|
|
||||||
out vec3 frag_color; |
|
||||||
|
|
||||||
layout (std140) uniform matrices |
|
||||||
{ |
|
||||||
mat4 view_xform; |
|
||||||
mat4 proj_xform; |
|
||||||
mat4 normal_xform; |
|
||||||
}; |
|
||||||
|
|
||||||
uniform mat4 node_xform; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
frag_color = color; |
|
||||||
gl_Position = proj_xform * view_xform * node_xform * vec4(position, 1); |
|
||||||
} |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
in vec3 frag_normal; |
|
||||||
|
|
||||||
out vec4 color; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
color = vec4(frag_normal, 1); |
|
||||||
} |
|
||||||
@ -1,26 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
layout (location = 0) in vec3 position; |
|
||||||
layout (location = 1) in vec3 normal; |
|
||||||
|
|
||||||
out vec3 frag_normal; |
|
||||||
|
|
||||||
layout (std140) uniform matrices |
|
||||||
{ |
|
||||||
mat4 view_xform; |
|
||||||
mat4 proj_xform; |
|
||||||
mat4 normal_xform; |
|
||||||
}; |
|
||||||
|
|
||||||
uniform mat4 node_xform; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
// TODO: probably better to do this once in a separate uniform than once |
|
||||||
// for every vertex |
|
||||||
mat4 xform = node_xform; |
|
||||||
xform[3] = vec4(0, 0, 0, 1); // NOTE: undo translation |
|
||||||
frag_normal = vec4(xform * vec4(normal, 1)).xyz; |
|
||||||
gl_Position = proj_xform * view_xform * node_xform * vec4(position, 1); |
|
||||||
} |
|
||||||
@ -1,67 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
in vec3 frag_pos; |
|
||||||
in vec3 frag_normal; |
|
||||||
in vec2 frag_uv; |
|
||||||
|
|
||||||
out vec4 color; |
|
||||||
|
|
||||||
uniform sampler2D sampler; |
|
||||||
|
|
||||||
const uint NUM_LIGHTS = 32u; |
|
||||||
|
|
||||||
layout (std140) uniform lights |
|
||||||
{ |
|
||||||
uint max_p_lights; |
|
||||||
uint active_p_lights; |
|
||||||
uint max_d_lights; |
|
||||||
uint active_d_lights; |
|
||||||
uint padding; |
|
||||||
|
|
||||||
vec4 ambient_color; |
|
||||||
|
|
||||||
vec4 pl_positions[NUM_LIGHTS]; |
|
||||||
vec4 pl_colors[NUM_LIGHTS]; |
|
||||||
uint pl_intensities[NUM_LIGHTS]; // NOTE: 16 bytes * NUM_LIGHTS |
|
||||||
|
|
||||||
vec4 dl_directions[NUM_LIGHTS]; |
|
||||||
vec4 dl_colors[NUM_LIGHTS]; |
|
||||||
uint dl_intensities[NUM_LIGHTS]; // NOTE: 16 bytes * NUM_LIGHTS |
|
||||||
}; |
|
||||||
|
|
||||||
const float CONSTANT_ATTENUATION = 0.1; |
|
||||||
const float LINEAR_ATTENUATION = 0.2; |
|
||||||
const float QUADRATIC_ATTENUATION = 0.02; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
vec4 diffuse_color = vec4(0); |
|
||||||
|
|
||||||
// NOTE: directional lights |
|
||||||
for (uint i = 0u; i < active_d_lights; i++) { |
|
||||||
vec4 light_direction = normalize(dl_directions[i]); |
|
||||||
float diffuse_factor = |
|
||||||
clamp(dot(vec4(frag_normal, 1), light_direction), 0, 1); |
|
||||||
diffuse_color = diffuse_color + |
|
||||||
dl_intensities[i] * diffuse_factor * dl_colors[i]; |
|
||||||
} |
|
||||||
|
|
||||||
// NOTE: point lights |
|
||||||
for (uint i = 0u; i < active_p_lights; i ++) { |
|
||||||
vec3 direction = vec3(pl_positions[i]).xyz - frag_pos; |
|
||||||
float distance = length(direction); |
|
||||||
direction = direction / distance; |
|
||||||
|
|
||||||
float attenuation = CONSTANT_ATTENUATION + |
|
||||||
LINEAR_ATTENUATION * distance + |
|
||||||
QUADRATIC_ATTENUATION * pow(distance, 2); |
|
||||||
float diffuse_factor = clamp(dot(frag_normal, direction), 0, 1); |
|
||||||
vec4 added_color = pl_colors[i] * pl_intensities[i] |
|
||||||
* diffuse_factor / attenuation; |
|
||||||
|
|
||||||
diffuse_color = diffuse_color + added_color; |
|
||||||
} |
|
||||||
|
|
||||||
color = (ambient_color + diffuse_color) * texture(sampler, frag_uv.st); |
|
||||||
} |
|
||||||
@ -1,27 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
layout (location = 0) in vec3 position; |
|
||||||
layout (location = 1) in vec3 normal; |
|
||||||
layout (location = 2) in vec2 uv; |
|
||||||
|
|
||||||
out vec3 frag_pos; |
|
||||||
out vec3 frag_normal; |
|
||||||
out vec2 frag_uv; |
|
||||||
|
|
||||||
layout (std140) uniform matrices |
|
||||||
{ |
|
||||||
mat4 view_xform; |
|
||||||
mat4 proj_xform; |
|
||||||
mat4 normal_xform; |
|
||||||
}; |
|
||||||
|
|
||||||
uniform mat4 node_xform; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
frag_pos = (node_xform * vec4(position, 1)).xyz; |
|
||||||
frag_uv = uv; |
|
||||||
frag_normal = normalize(mat3(node_xform) * normal); |
|
||||||
gl_Position = proj_xform * view_xform * node_xform * vec4(position, 1); |
|
||||||
} |
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,21 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
in vec2 frag_uv; |
|
||||||
|
|
||||||
out vec4 color; |
|
||||||
|
|
||||||
layout (std140) uniform matrices |
|
||||||
{ |
|
||||||
mat4 view_xform; |
|
||||||
mat4 proj_xform; |
|
||||||
mat4 normal_xform; |
|
||||||
}; |
|
||||||
|
|
||||||
uniform mat4 node_xform; |
|
||||||
uniform sampler2D sampler; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
color = texture(sampler, frag_uv.st); |
|
||||||
} |
|
||||||
@ -1,22 +0,0 @@ |
|||||||
#version 330 core |
|
||||||
|
|
||||||
layout (location = 0) in vec3 position; |
|
||||||
layout (location = 1) in vec2 uv; |
|
||||||
|
|
||||||
out vec2 frag_uv; |
|
||||||
|
|
||||||
layout (std140) uniform matrices |
|
||||||
{ |
|
||||||
mat4 view_xform; |
|
||||||
mat4 proj_xform; |
|
||||||
mat4 normal_xform; |
|
||||||
}; |
|
||||||
|
|
||||||
uniform mat4 node_xform; |
|
||||||
|
|
||||||
|
|
||||||
void main() |
|
||||||
{ |
|
||||||
frag_uv = uv; |
|
||||||
gl_Position = proj_xform * view_xform * node_xform * vec4(position, 1); |
|
||||||
} |
|
||||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,16 @@ |
|||||||
|
|
||||||
|
#include "renderer.h" |
||||||
|
|
||||||
|
|
||||||
|
int |
||||||
|
main() |
||||||
|
{ |
||||||
|
render_state* rs = renInit("Hello World"); |
||||||
|
|
||||||
|
if (rs == nullptr) |
||||||
|
return 1; |
||||||
|
|
||||||
|
renDoRenderLoop(rs); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
@ -1,344 +0,0 @@ |
|||||||
|
|
||||||
#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] = vec4(-2, 1, 3, 1); |
|
||||||
lb->dl_colors[idx] = vec4(0.5, 0.5, 0.3, 1); |
|
||||||
lb->dl_intensities[idx] = uvec4(1, 0, 0, 0); |
|
||||||
#endif |
|
||||||
// NOTE: add a point light
|
|
||||||
idx = (*lb->active_p_lights)++; |
|
||||||
lb->pl_positions[idx] = vec4(-10, 0, -10, 1); |
|
||||||
lb->pl_colors[idx] = vec4(1, 1, 0.8, 1); |
|
||||||
lb->pl_intensities[idx] = 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, 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; |
|
||||||
vec3 cube_locs[NUM_CUBES] = { |
|
||||||
vec3( 0, 0, 0), |
|
||||||
vec3(-10, 10, 0), |
|
||||||
vec3(-10, -10, 0), |
|
||||||
vec3( 10, 10, 0), |
|
||||||
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(rg); |
|
||||||
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, vec3(0, -10, -15)); |
|
||||||
} else { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
testColoredVertices(RenderState* rs) |
|
||||||
{ |
|
||||||
Mesh m = {0}; |
|
||||||
m.num_vertices = 5; |
|
||||||
m.num_indices = 12; |
|
||||||
|
|
||||||
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 |
|
||||||
}; |
|
||||||
|
|
||||||
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; |
|
||||||
mat4 xform = 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, vec3(0, -10, 15)); |
|
||||||
} else { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
loadCamera(RenderState* rs) |
|
||||||
{ |
|
||||||
Camera* cam = rs->camera; |
|
||||||
vec3 cam_pos = { 0, 15, 40 }; |
|
||||||
vec3 look_pos = { 0, 0, 0 }; |
|
||||||
vec3 up = { 0, 1, 0 }; |
|
||||||
cameraInitPerspective(cam, cam_pos, look_pos, up); |
|
||||||
GLBuffer* xforms_ubo = getUBOByName(rs->gl_ctx, "matrices"); |
|
||||||
|
|
||||||
if (!xforms_ubo) |
|
||||||
return false; |
|
||||||
|
|
||||||
updateGLBuffer(xforms_ubo, &cam->xforms); |
|
||||||
|
|
||||||
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 (!loadSpaceShip(rs)) { |
|
||||||
LOGF(Error, "Error loading ship modeln"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (!loadCamera(rs)) { |
|
||||||
LOGF(Error, "Error loading camera\n"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return loadLights(rs); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
orbitPositionZ0(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, void* user_data = nullptr) |
|
||||||
{ |
|
||||||
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, 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 vec4 cam_pos; |
|
||||||
static vec3 look_pos; |
|
||||||
static 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->camera->xforms.view = |
|
||||||
glm::lookAt(vec3(cam_pos), vec3(0, 0, 0), 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->camera->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, vec3(0, 1, 0), direction * (float) M_PI / (3 * 60)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int |
|
||||||
main() |
|
||||||
{ |
|
||||||
RenderState* rs = initRenderState("tangerine example", uvec2(1600, 900)); |
|
||||||
|
|
||||||
if (rs) { |
|
||||||
if (!loadScene(rs)) { |
|
||||||
LOGF(Error, "error loading scene\n"); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
doRenderLoop(rs, 60, render_cb_pre, nullptr, nullptr); |
|
||||||
|
|
||||||
freeRenderState(rs); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
LOGF(Error, "error loading scene\n"); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
@ -0,0 +1,233 @@ |
|||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,89 @@ |
|||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
@ -1,152 +0,0 @@ |
|||||||
|
|
||||||
// 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; |
|
||||||
|
|
||||||
#ifdef TANGERINE_GL_DEBUG_QUIET |
|
||||||
if (type != GL_DEBUG_TYPE_ERROR) |
|
||||||
return; |
|
||||||
#endif |
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@ -0,0 +1,18 @@ |
|||||||
|
|
||||||
|
#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,30 +0,0 @@ |
|||||||
|
|
||||||
#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"; |
|
||||||
|
|
||||||
@ -0,0 +1,34 @@ |
|||||||
|
|
||||||
|
#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); |
||||||
|
|
||||||
@ -0,0 +1,64 @@ |
|||||||
|
/*
|
||||||
|
* 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(); |
||||||
|
|
||||||
@ -0,0 +1,102 @@ |
|||||||
|
|
||||||
|
// 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
|
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,27 @@ |
|||||||
|
|
||||||
|
#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); |
||||||
|
|
||||||
@ -0,0 +1,132 @@ |
|||||||
|
/*
|
||||||
|
* 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); |
||||||
|
|
||||||
@ -1,214 +0,0 @@ |
|||||||
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include <SDL2/SDL.h> |
|
||||||
#include <GL/glew.h> |
|
||||||
#define GLM_FORCE_XYZW_ONLY |
|
||||||
#include <glm/glm.hpp> |
|
||||||
|
|
||||||
#include "asset.h" |
|
||||||
#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; |
|
||||||
|
|
||||||
mat4* node_xform; |
|
||||||
|
|
||||||
u32 num_vertex_attrib_buffers; |
|
||||||
GLBuffer* vertex_attrib_buffers; |
|
||||||
GLBuffer* element_buf; |
|
||||||
}; |
|
||||||
|
|
||||||
struct Animation; |
|
||||||
|
|
||||||
|
|
||||||
GLContext* initGLContext(MemoryArena* arena, |
|
||||||
u32 max_shaders, |
|
||||||
u32 max_textures, |
|
||||||
u32 max_ubos); |
|
||||||
|
|
||||||
// TODO: add more notes here describing the internals? This is the entry point
|
|
||||||
// to a complex process of programatically assigning properties to our notion
|
|
||||||
// of what a shader needs ie) uniforms, buffer block bindings, etc
|
|
||||||
// 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); |
|
||||||
|
|
||||||
GLBuffer* initGLBackingBuffer(GLContext* gl_ctx, |
|
||||||
MemoryArena* arena, |
|
||||||
const char* name, |
|
||||||
GLenum data_type, |
|
||||||
u32 buf_size, |
|
||||||
void* data); |
|
||||||
|
|
||||||
void updateGLBuffer(GLBuffer* gl_buf, void* data); |
|
||||||
|
|
||||||
void renderVAO(GLMesh* glmesh, |
|
||||||
mat4* node_xform, |
|
||||||
ShaderProgram* shader, |
|
||||||
GLTexture* gl_tex, |
|
||||||
GLenum draw_mode); |
|
||||||
|
|
||||||
GLVertexAttrib* getVertexAttribByName(ShaderProgram* shader, const char* name); |
|
||||||
|
|
||||||
GLMesh loadGLMesh(MemoryArena* arena, |
|
||||||
const Mesh& m, |
|
||||||
GLenum draw_mode, |
|
||||||
GLenum usage, |
|
||||||
GLTexture* diffuse_texture, |
|
||||||
u32 num_mappings, |
|
||||||
GLBufferToAttribMapping mappings[]); |
|
||||||
|
|
||||||
@ -0,0 +1,57 @@ |
|||||||
|
|
||||||
|
#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); |
||||||
@ -1,296 +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 |
|
||||||
*/ |
|
||||||
|
|
||||||
/*
|
|
||||||
* === TODO: === |
|
||||||
* - try to improve the setup interface for examples |
|
||||||
* see if we can remove some boilerplate with more helpers, or |
|
||||||
* re-organizing |
|
||||||
* - add orbit function to camera, see orbitPositionZ0 in example |
|
||||||
* maybe implement with quaternion, so we can set arbitrary orbit axis |
|
||||||
* - update camera interal state and rotations to use quaternions |
|
||||||
* - 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 |
|
||||||
* - resizable arrays for entities and asset system (^ see above) |
|
||||||
* - add libTangerine namespace? |
|
||||||
* - make a test case for overflowing default array sizes, rg->assets |
|
||||||
* rg_info->groups, render_group->enitities, rg->textures |
|
||||||
* - test for video memory leaking since we're not actually deleting GLBuffers |
|
||||||
* anywhere |
|
||||||
* - look for glm::mat4 in structs, and replace with pointers (easier debugging) |
|
||||||
* - defaults include for various #defines |
|
||||||
* - 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
|
|
||||||
* |
|
||||||
* === 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() |
|
||||||
* - merge back into libTangerine |
|
||||||
* - merge render_group_fix branch back into master |
|
||||||
*/ |
|
||||||
|
|
||||||
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include <SDL2/SDL.h> |
|
||||||
|
|
||||||
|
|
||||||
// NOTE: optional compile-time defines:
|
|
||||||
// TANGERINE_GL_DEBUG_QUIET disable extra debug messages from openGL
|
|
||||||
|
|
||||||
|
|
||||||
#include "asset.h" |
|
||||||
#include "camera.h" |
|
||||||
#include "entity.h" |
|
||||||
#include "dumbLog.h" |
|
||||||
#include "GLDebug.h" |
|
||||||
#include "input.h" |
|
||||||
#include "shader.h" |
|
||||||
#include "types.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
|
|
||||||
struct SDLHandles |
|
||||||
{ |
|
||||||
SDL_Window* window; |
|
||||||
SDL_GLContext sdl_gl_ctx; |
|
||||||
SDL_DisplayMode display_mode; |
|
||||||
u32 SDL_flags; |
|
||||||
}; |
|
||||||
|
|
||||||
struct RenderGroup |
|
||||||
{ |
|
||||||
ShaderProgram* shader; |
|
||||||
u32 num_entities; |
|
||||||
u32 max_entities; |
|
||||||
Entity* entities; |
|
||||||
char* name; |
|
||||||
}; |
|
||||||
|
|
||||||
// TODO: node/tree structure for entities
|
|
||||||
#if 0 |
|
||||||
struct Node; |
|
||||||
|
|
||||||
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; |
|
||||||
|
|
||||||
vec4* ambient_color; |
|
||||||
|
|
||||||
vec4* pl_positions; |
|
||||||
vec4* pl_colors; |
|
||||||
uvec4* pl_intensities; |
|
||||||
|
|
||||||
vec4* dl_directions; |
|
||||||
vec4* dl_colors; |
|
||||||
uvec4* dl_intensities; |
|
||||||
|
|
||||||
void* buffer; |
|
||||||
}; |
|
||||||
|
|
||||||
struct RenderState |
|
||||||
{ |
|
||||||
bool running; |
|
||||||
GLClearColor clear_col; |
|
||||||
SDLHandles handles; |
|
||||||
GLContext* gl_ctx; |
|
||||||
Assets assets; |
|
||||||
uvec2 window_dims; |
|
||||||
|
|
||||||
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; |
|
||||||
Camera* camera; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
#define DEFAULT_WIDTH 1280 |
|
||||||
#define DEFAULT_HEIGHT 720 |
|
||||||
#define DEFAULT_SDL_FLAGS SDL_INIT_VIDEO |
|
||||||
#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(const char* window_title = "tangerine", |
|
||||||
uvec2 window_dims = uvec2(DEFAULT_WIDTH, DEFAULT_HEIGHT), |
|
||||||
u32 SDL_flags = DEFAULT_SDL_FLAGS, |
|
||||||
GLClearColor clear_col = DEFAULT_CLEAR_COLOR, |
|
||||||
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); |
|
||||||
|
|
||||||
Entity* getEntityByName(RenderGroup* rg, const char* name); |
|
||||||
|
|
||||||
// NOTE: callback function signature to use with renDoRenderLoop()
|
|
||||||
typedef void (*frame_callback_fn) (RenderState*, void*); |
|
||||||
|
|
||||||
// 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* user_data = 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,24 +1,16 @@ |
|||||||
|
|
||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
#include <cstdint> |
|
||||||
#define GLM_FORCE_XYZW_ONLY |
|
||||||
#include <glm/glm.hpp> |
|
||||||
|
|
||||||
|
enum shader_t |
||||||
|
{ |
||||||
|
SIMPLE_SHADER, |
||||||
|
DEFAULT_SHADER |
||||||
|
}; |
||||||
|
|
||||||
typedef uint8_t u8; |
enum mesh_t |
||||||
typedef uint16_t u16; |
{ |
||||||
typedef uint32_t u32; |
SIMPLE_MESH, |
||||||
typedef uint64_t u64; |
DEFAULT_MESHES |
||||||
|
}; |
||||||
typedef int32_t i32; |
|
||||||
typedef int64_t i64; |
|
||||||
|
|
||||||
using glm::ivec2; |
|
||||||
using glm::uvec2; |
|
||||||
using glm::uvec4; |
|
||||||
using glm::vec2; |
|
||||||
using glm::vec3; |
|
||||||
using glm::vec4; |
|
||||||
using glm::mat4; |
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,35 @@ |
|||||||
|
|
||||||
|
#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); |
||||||
|
|
||||||
@ -0,0 +1,162 @@ |
|||||||
|
|
||||||
|
|
||||||
|
// 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,74 +1,76 @@ |
|||||||
|
|
||||||
#define GLM_FORCE_XYZW_ONLY |
#include <cassert> |
||||||
#include <glm/gtc/matrix_transform.hpp> |
|
||||||
|
#include <glm/ext/matrix_transform.hpp> |
||||||
|
|
||||||
#include "dumbLog.h" |
#include "dumbLog.h" |
||||||
#include "entity.h" |
#include "entity.h" |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
|
|
||||||
|
void initDefaults(entity& e); |
||||||
|
|
||||||
|
|
||||||
|
// interface
|
||||||
|
|
||||||
bool |
bool |
||||||
initEntity(Entity* e, |
entInitModel(entity* e, model* mdl) |
||||||
GLContext* gl_ctx, |
|
||||||
MemoryArena* arena, |
|
||||||
Model* mdl, |
|
||||||
u32 num_attrib_mappings, |
|
||||||
GLBufferToAttribMapping* attrib_mappings, |
|
||||||
const char* name, |
|
||||||
GLenum draw_mode, |
|
||||||
GLenum usage) |
|
||||||
{ |
{ |
||||||
e->num_meshes = mdl->num_meshes; |
e->render_objs = roInitModel(mdl); |
||||||
e->meshes = ARENA_ALLOC(arena, GLMesh, e->num_meshes); |
e->world_transform = glm::mat4(1); |
||||||
e->model_xform = ARENA_ALLOC(arena, mat4, 1); |
e->model_id = mdl->filepath_hash; |
||||||
*e->model_xform = mat4(1.f); |
|
||||||
e->name = arenaCopyCStr(arena, name); |
|
||||||
e->draw_mode = draw_mode; |
|
||||||
e->usage = usage; |
|
||||||
|
|
||||||
if (mdl->diffuse_texture) { |
|
||||||
e->diffuse_texture = getGLTexture(gl_ctx, mdl->diffuse_texture); |
|
||||||
|
|
||||||
if (!e->diffuse_texture) |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
for (u32 i = 0; i< e->num_meshes; i++) { |
if (e->render_objs == nullptr) { |
||||||
GLMesh* glm = &e->meshes[i]; |
entFree(e); |
||||||
*glm = loadGLMesh(arena, |
return false; |
||||||
mdl->meshes[i], |
|
||||||
draw_mode, |
|
||||||
usage, |
|
||||||
e->diffuse_texture, |
|
||||||
num_attrib_mappings, |
|
||||||
attrib_mappings); |
|
||||||
|
|
||||||
if (glm->vao_id == 0) { |
|
||||||
LOGF(Error, "error initializing entity\n"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
} |
||||||
|
|
||||||
return true; |
return true; |
||||||
} |
} |
||||||
|
|
||||||
void |
void |
||||||
setEntityPosition(Entity* e, vec3 pos) |
entFree(entity* e) |
||||||
|
{ |
||||||
|
roFree(e->render_objs); |
||||||
|
e->render_objs = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
entSetWorldPosition(entity& e, glm::vec3 v) |
||||||
{ |
{ |
||||||
(*e->model_xform)[3][0] = pos.x; |
e.world_transform[3][0] = v.x; |
||||||
(*e->model_xform)[3][1] = pos.y; |
e.world_transform[3][1] = v.y; |
||||||
(*e->model_xform)[3][2] = pos.z; |
e.world_transform[3][2] = v.z; |
||||||
} |
} |
||||||
|
|
||||||
void |
void |
||||||
rotateEntity(Entity* e, vec3 axis, float radians) |
entTranslate(entity& e, glm::vec3 v) |
||||||
{ |
{ |
||||||
*e->model_xform = glm::rotate(*e->model_xform, radians, axis); |
e.world_transform = glm::translate(e.world_transform, v); |
||||||
} |
} |
||||||
|
|
||||||
void |
void |
||||||
scaleEntity(Entity* e, float scale) |
entScale(entity& e, glm::vec3 v) |
||||||
|
{ |
||||||
|
e.world_transform = glm::scale(e.world_transform, v); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
entRotate(entity* e, float angle, glm::vec3 axis) |
||||||
|
{ |
||||||
|
e->world_transform = glm::rotate(e->world_transform, angle, axis); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// internal
|
||||||
|
|
||||||
|
void |
||||||
|
initDefaults(entity& e) |
||||||
{ |
{ |
||||||
*e->model_xform = |
e.world_transform = glm::mat4(1.0); |
||||||
glm::scale(*e->model_xform, vec3(scale, scale, scale)); |
entScale(e, glm::vec3(1.0)); |
||||||
|
entSetWorldPosition(e, glm::vec3(0, 0, 0)); |
||||||
} |
} |
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,12 @@ |
|||||||
|
|
||||||
|
// 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" |
||||||
|
|
||||||
@ -0,0 +1,55 @@ |
|||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,262 @@ |
|||||||
|
#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 |
||||||
|
|
||||||
@ -0,0 +1,246 @@ |
|||||||
|
|
||||||
|
#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); |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,369 @@ |
|||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
@ -1,748 +0,0 @@ |
|||||||
|
|
||||||
#include <cassert> |
|
||||||
#include <cstddef> |
|
||||||
#include <cstdlib> |
|
||||||
#include <cstdio> |
|
||||||
#include <cstring> |
|
||||||
#include <fstream> |
|
||||||
|
|
||||||
#define GLM_FORCE_XYZW_ONLY |
|
||||||
#include <glm/gtc/matrix_transform.hpp> |
|
||||||
|
|
||||||
#include "asset.h" |
|
||||||
#include "dumbLog.h" |
|
||||||
#include "dummy_shader.h" |
|
||||||
#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); |
|
||||||
|
|
||||||
// FIXME: replace with utilCStrMatch, and remove getShaderByHash, we don't
|
|
||||||
// need a hashing function if we're not storing in a hash table
|
|
||||||
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 = 0; 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; |
|
||||||
} |
|
||||||
|
|
||||||
GLBuffer* initGLBackingBuffer(GLContext* gl_ctx, |
|
||||||
MemoryArena* arena, |
|
||||||
const char* name, |
|
||||||
GLenum data_type, |
|
||||||
u32 buf_size, |
|
||||||
void* data) |
|
||||||
{ |
|
||||||
GLBuffer* buf = getFreeUBO(gl_ctx); |
|
||||||
glGenBuffers(1, &buf->id); |
|
||||||
buf->target = GL_UNIFORM_BUFFER; |
|
||||||
buf->data_type = data_type; |
|
||||||
buf->data_size = buf_size; |
|
||||||
buf->name = arenaCopyCStr(arena, name); |
|
||||||
|
|
||||||
glBindBuffer(buf->target, buf->id); |
|
||||||
glBufferData(buf->target, buf->data_size, data, GL_DYNAMIC_DRAW); |
|
||||||
buf->binding_idx = gl_ctx->binding_count++; |
|
||||||
glBindBufferBase(buf->target, buf->binding_idx, buf->id); |
|
||||||
|
|
||||||
return buf; |
|
||||||
} |
|
||||||
|
|
||||||
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, |
|
||||||
mat4* node_xform, |
|
||||||
ShaderProgram* shader, |
|
||||||
GLTexture* gl_tex, |
|
||||||
GLenum draw_mode) |
|
||||||
{ |
|
||||||
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(draw_mode, glmesh->num_indices, GL_UNSIGNED_SHORT, 0); |
|
||||||
glBindVertexArray(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) |
|
||||||
{ |
|
||||||
GLMesh glm = {0}; |
|
||||||
glm.num_indices = m.num_indices; |
|
||||||
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, mat4, 1); |
|
||||||
*glm.node_xform = 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; |
|
||||||
// FIXME: this should be allocated on the same arena as the parent GLBuffer
|
|
||||||
// with utilAllocateCStr
|
|
||||||
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 avoid copying GLMesh twice that way
|
|
||||||
GLMesh |
|
||||||
loadGLMesh(MemoryArena* arena, |
|
||||||
const Mesh& m, |
|
||||||
GLenum draw_mode, |
|
||||||
GLenum usage, |
|
||||||
GLTexture* diffuse_texture, |
|
||||||
u32 num_mappings, |
|
||||||
GLBufferToAttribMapping mappings[]) |
|
||||||
{ |
|
||||||
GLMesh glm = initGLMesh(arena, m, num_mappings); |
|
||||||
|
|
||||||
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, |
|
||||||
usage); |
|
||||||
glVertexAttribPointer(attrib->location, attrib->num_components, |
|
||||||
attrib->component_type, GL_FALSE, 0, 0); |
|
||||||
glEnableVertexAttribArray(attrib->location); |
|
||||||
} |
|
||||||
|
|
||||||
// FIXME: should we be re-using initGLAttribBuffer here?
|
|
||||||
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, |
|
||||||
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 (utilCStrMatch(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; |
|
||||||
} |
|
||||||
|
|
||||||
@ -0,0 +1,146 @@ |
|||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
|
|
||||||
@ -1,408 +0,0 @@ |
|||||||
|
|
||||||
#include <cassert> |
|
||||||
|
|
||||||
#define GLM_FORCE_XYZW_ONLY |
|
||||||
#include <glm/gtc/matrix_transform.hpp> |
|
||||||
|
|
||||||
#include "tangerine.h" |
|
||||||
#define UTIL_IMPLEMENTATION |
|
||||||
#include "util.h" |
|
||||||
#define GL_DEBUG_IMPLEMENTATION |
|
||||||
#include "GLDebug.h" |
|
||||||
|
|
||||||
|
|
||||||
// forward declarations
|
|
||||||
|
|
||||||
bool initGraphics(SDLHandles* handles, const char* title, uvec2 dims); |
|
||||||
|
|
||||||
LightsBuffer* initLights(RenderState* rs, u32 max_lights, vec4 ambient_color); |
|
||||||
|
|
||||||
|
|
||||||
// interface
|
|
||||||
|
|
||||||
RenderState* |
|
||||||
initRenderState(const char* window_title, |
|
||||||
uvec2 window_dims, |
|
||||||
u32 SDL_flags, |
|
||||||
GLClearColor clear_col, |
|
||||||
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); |
|
||||||
rs->window_dims = window_dims; |
|
||||||
rs->handles.SDL_flags = SDL_flags; |
|
||||||
|
|
||||||
if (!initGraphics(&rs->handles, window_title, window_dims)) { |
|
||||||
LOGF(Error, "error initializing renderer\n"); |
|
||||||
return nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
rs->gl_ctx = initGLContext(rs->assets.arena, |
|
||||||
max_shaders, |
|
||||||
max_textures, |
|
||||||
max_ubos); |
|
||||||
|
|
||||||
rs->camera = UTIL_ALLOC(1, Camera); |
|
||||||
GLBuffer* xforms_ubo = initGLBackingBuffer(rs->gl_ctx, |
|
||||||
rs->assets.arena, |
|
||||||
"matrices", |
|
||||||
GL_FLOAT, |
|
||||||
sizeof(Transforms), |
|
||||||
&rs->camera->xforms); |
|
||||||
assert(xforms_ubo); |
|
||||||
|
|
||||||
// FIXME: should this be an interface function?
|
|
||||||
rs->lights_buf = initLights(rs, max_lights, ambient_color); |
|
||||||
|
|
||||||
// FIXME: should have an error message instead of assert here, and
|
|
||||||
// clean up arenas/allocations before returning
|
|
||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
Entity* |
|
||||||
getEntityByName(RenderGroup* rg, const char* name) |
|
||||||
{ |
|
||||||
Entity* e_out = nullptr; |
|
||||||
|
|
||||||
for (u32 i = 0; i < rg->num_entities; i++) { |
|
||||||
Entity* e = &rg->entities[i]; |
|
||||||
|
|
||||||
if (utilCStrMatch(name, e->name)) |
|
||||||
e_out = e; |
|
||||||
} |
|
||||||
|
|
||||||
if (e_out == nullptr) |
|
||||||
LOGF(Error, "Entity with name, \"%s\", not found\n", name); |
|
||||||
|
|
||||||
return e_out; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
doRenderLoop(RenderState* rs, |
|
||||||
u32 framerate, |
|
||||||
frame_callback_fn cb_func_pre, |
|
||||||
frame_callback_fn cb_func_post, |
|
||||||
void* user_data) |
|
||||||
{ |
|
||||||
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, user_data); |
|
||||||
} 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, user_data); |
|
||||||
|
|
||||||
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, e->draw_mode); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
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, const char* title, uvec2 dims) |
|
||||||
{ |
|
||||||
handles->window = SDL_CreateWindow( |
|
||||||
title, |
|
||||||
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
|
||||||
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
|
||||||
dims.x, |
|
||||||
dims.y, |
|
||||||
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); |
|
||||||
|
|
||||||
if (SDL_Init(handles->SDL_flags) != 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(RenderState* rs, |
|
||||||
u32 max_lights, |
|
||||||
vec4 ambient_color) |
|
||||||
{ |
|
||||||
// FIXME: revisit for 'Scene' abstraction
|
|
||||||
// FIXME: see if we can simplify this with the use of offsetof()
|
|
||||||
// https://en.cppreference.com/w/cpp/types/offsetof
|
|
||||||
|
|
||||||
LightsBuffer* lb = ARENA_ALLOC(rs->assets.arena, LightsBuffer, 1); |
|
||||||
lb->buf_size = 8 * sizeof(u32) // NOTE: 'header'
|
|
||||||
+ sizeof(vec4) // NOTE: ambient color
|
|
||||||
+ 6 * max_lights * sizeof(vec4); // NOTE: vector arrays
|
|
||||||
LOGF(Debug, "buf_size: %d\n", lb->buf_size); |
|
||||||
lb->buffer = ARENA_ALLOC(rs->assets.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 = (vec4*) arr_start; |
|
||||||
*lb->ambient_color = ambient_color; |
|
||||||
|
|
||||||
// NOTE: set offsets for array pointers
|
|
||||||
u32 arr_size = max_lights * sizeof(vec4); |
|
||||||
lb->pl_positions = //(vec4*) arr_start;
|
|
||||||
(vec4*) arenaGetAddressOffset(arr_start, sizeof(vec4)); |
|
||||||
lb->pl_colors = |
|
||||||
(vec4*) arenaGetAddressOffset(lb->pl_positions, arr_size); |
|
||||||
lb->pl_intensities = |
|
||||||
(uvec4*) arenaGetAddressOffset(lb->pl_colors, arr_size); |
|
||||||
lb->dl_directions = |
|
||||||
(vec4*) arenaGetAddressOffset(lb->pl_intensities, arr_size); |
|
||||||
lb->dl_colors = |
|
||||||
(vec4*) arenaGetAddressOffset(lb->dl_directions, arr_size); |
|
||||||
lb->dl_intensities = |
|
||||||
(uvec4*) arenaGetAddressOffset(lb->dl_colors, arr_size); |
|
||||||
|
|
||||||
initGLBackingBuffer(rs->gl_ctx, |
|
||||||
rs->assets.arena, |
|
||||||
"lights", |
|
||||||
GL_BYTE, |
|
||||||
lb->buf_size, |
|
||||||
lb->buffer); |
|
||||||
|
|
||||||
return lb; |
|
||||||
} |
|
||||||
|
|
||||||
@ -1,5 +0,0 @@ |
|||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION |
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION |
|
||||||
#define TINYGLTF_IMPLEMENTATION |
|
||||||
#include "tiny_gltf.h" |
|
||||||
@ -0,0 +1,197 @@ |
|||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,59 @@ |
|||||||
|
|
||||||
|
#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