diff --git a/Makefile b/Makefile index db68f31..79782d5 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,10 @@ OBJDIR = build SRCDIR = src LIBNAME = libTangerine.a -RENDER_SOURCES = $(wildcard $(SRCDIR)/*.cpp) -RENDER_OBJECTS = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(RENDER_SOURCES)) +SOURCES = $(wildcard $(SRCDIR)/*.cpp) +OBJECTS = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(SOURCES)) +NDBG_SOURCES = $(wildcard $(SRCDIR)/*.cc) +NDBG_OBJS = $(patsubst $(SRCDIR)/%.cc, $(OBJDIR)/%.o, $(NDBG_SOURCES)) all: mkdirs $(LIBNAME) @@ -25,12 +27,18 @@ mkdirs: @mkdir -p $(OBJDIR) .PHONY: mkdirs -$(LIBNAME): $(RENDER_OBJECTS) - ar -crsuU $(OBJDIR)/$(LIBNAME) $(RENDER_OBJECTS) +$(LIBNAME): $(OBJECTS) $(NDBG_OBJS) + ar -crsuU $(OBJDIR)/$(LIBNAME) $(OBJECTS) $(NDBG_OBJS) -$(RENDER_OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp +$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp $(CXX) $(CXXFLAGS) -c -MMD $< -o $@ +# FIXME: re-using CXXFLAGS here defats the purpose of separating out NDBG_OJBS +# see shader_testing Makefile +$(NDBG_OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.cc + $(CXX) $(CXXFLAGS) -c $< -o $@ + strip -d $@ + examples: $(MAKE) -C $(EXAMPLEDIR) .PHONY: examples diff --git a/examples/Makefile b/examples/Makefile index 5ca0470..b54c414 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,26 +1,40 @@ SHELL = /bin/sh CXX = g++ -CXXFLAGS = -std=c++11 -g -ggdb3 -Wall -I../include -I/usr/include/SDL2 +CXXFLAGS = -std=c++11 -g -ggdb3 -Wall \ + -I../include \ + -I/usr/include/SDL2 \ + -I../ext/tinygltf LDFLAGS = -lSDL2 -lGLEW -lGL OBJDIR = build LIB = ../build/libTangerine.a BINDIR = bin -EXAMPLE_SOURCES = \ - render_groups/main.cpp \ - #assimp_loading/main.cpp - #hello_world/main.cpp \ - #simple_mesh/main.cpp -EXAMPLE_OBJECTS = $(patsubst %/, $(OBJDIR)/%.o, $(dir $(EXAMPLE_SOURCES))) +#EXAMPLE_SOURCES = \ +# render_groups/main.cpp \ +# #assimp_loading/main.cpp +# #hello_world/main.cpp \ +# #simple_mesh/main.cpp +#EXAMPLE_OBJECTS = $(patsubst %/, $(OBJDIR)/%.o, $(dir $(EXAMPLE_SOURCES))) +# +#all: mkdirs $(EXAMPLE_OBJECTS) +# +#-include $(OBJDIR)/*.d +# +#$(EXAMPLE_OBJECTS): $(LIB) +# $(CXX) $(CXXFLAGS) -c -MMD $(basename $(notdir $@))/main.cpp -o $@ +# $(CXX) -o $(BINDIR)/$(notdir $(basename $@)) $(LDFLAGS) $@ $(LIB) +# +#mkdirs: +# @mkdir -p $(BINDIR) $(OBJDIR) +#.PHONY: mkdirs +# +#clean: +# rm -rf $(OBJDIR)/* bin/* +#.PHONY: clean -all: mkdirs $(EXAMPLE_OBJECTS) - --include $(OBJDIR)/*.d - -$(EXAMPLE_OBJECTS): $(LIB) - $(CXX) $(CXXFLAGS) -c -MMD $(basename $(notdir $@))/main.cpp -o $@ - $(CXX) -o $(BINDIR)/$(notdir $(basename $@)) $(LDFLAGS) $@ $(LIB) +all: mkdirs + $(CXX) $(CXXFLAGS) main.cpp -o $(BINDIR)/testing $(LDFLAGS) $(LIB) mkdirs: @mkdir -p $(BINDIR) $(OBJDIR) diff --git a/examples/assimp_loading/animation_testing.cpp b/examples/assimp_loading/animation_testing.cpp deleted file mode 100644 index 9b545bb..0000000 --- a/examples/assimp_loading/animation_testing.cpp +++ /dev/null @@ -1,72 +0,0 @@ - -#include // snprintf -#include // std::cout - -#include -#include -#include - -#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"; -} diff --git a/examples/assimp_loading/main.cpp b/examples/assimp_loading/main.cpp deleted file mode 100644 index 5892f8f..0000000 --- a/examples/assimp_loading/main.cpp +++ /dev/null @@ -1,85 +0,0 @@ - -#include -#include - -#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; -} - diff --git a/examples/data/blender/Color Palette 140.png b/examples/data/blender/Color Palette 140.png deleted file mode 100644 index dd9ce0c..0000000 --- a/examples/data/blender/Color Palette 140.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:455cd0343c1784781ce4ab3b843ce76b3e9477c3e6c0aa158bfd14c1276c7326 -size 572604 diff --git a/examples/data/blender/icosphere.bin b/examples/data/blender/icosphere.bin deleted file mode 100644 index fb76838..0000000 Binary files a/examples/data/blender/icosphere.bin and /dev/null differ diff --git a/examples/data/blender/icosphere.blend b/examples/data/blender/icosphere.blend deleted file mode 100644 index 994c2c5..0000000 --- a/examples/data/blender/icosphere.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a5ff61b8f94488ade07c3b2077d831ba81523381e6e99a4f471970d3e07d7e7f -size 875348 diff --git a/examples/data/blender/icosphere.gltf b/examples/data/blender/icosphere.gltf deleted file mode 100644 index 97185e8..0000000 --- a/examples/data/blender/icosphere.gltf +++ /dev/null @@ -1,133 +0,0 @@ -{ - "asset" : { - "generator" : "Khronos glTF Blender I/O v1.6.16", - "version" : "2.0" - }, - "scene" : 0, - "scenes" : [ - { - "name" : "Scene", - "nodes" : [ - 0 - ] - } - ], - "nodes" : [ - { - "mesh" : 0, - "name" : "Icosphere" - } - ], - "materials" : [ - { - "doubleSided" : true, - "name" : "Material.001", - "pbrMetallicRoughness" : { - "baseColorTexture" : { - "index" : 0 - }, - "metallicFactor" : 0, - "roughnessFactor" : 0.5 - } - } - ], - "meshes" : [ - { - "name" : "Icosphere", - "primitives" : [ - { - "attributes" : { - "POSITION" : 0, - "NORMAL" : 1, - "TEXCOORD_0" : 2 - }, - "indices" : 3, - "material" : 0 - } - ] - } - ], - "textures" : [ - { - "sampler" : 0, - "source" : 0 - } - ], - "images" : [ - { - "mimeType" : "image/png", - "name" : "Color Palette 140", - "uri" : "Color%20Palette%20140.png" - } - ], - "accessors" : [ - { - "bufferView" : 0, - "componentType" : 5126, - "count" : 960, - "max" : [ - 1, - 1, - 0.9999999403953552 - ], - "min" : [ - -0.9999999403953552, - -1, - -0.9999999403953552 - ], - "type" : "VEC3" - }, - { - "bufferView" : 1, - "componentType" : 5126, - "count" : 960, - "type" : "VEC3" - }, - { - "bufferView" : 2, - "componentType" : 5126, - "count" : 960, - "type" : "VEC2" - }, - { - "bufferView" : 3, - "componentType" : 5123, - "count" : 960, - "type" : "SCALAR" - } - ], - "bufferViews" : [ - { - "buffer" : 0, - "byteLength" : 11520, - "byteOffset" : 0 - }, - { - "buffer" : 0, - "byteLength" : 11520, - "byteOffset" : 11520 - }, - { - "buffer" : 0, - "byteLength" : 7680, - "byteOffset" : 23040 - }, - { - "buffer" : 0, - "byteLength" : 1920, - "byteOffset" : 30720 - } - ], - "samplers" : [ - { - "magFilter" : 9729, - "minFilter" : 9987 - } - ], - "buffers" : [ - { - "byteLength" : 32640, - "uri" : "icosphere.bin" - } - ] -} diff --git a/examples/data/blender/spaceship.bin b/examples/data/blender/spaceship.bin deleted file mode 100644 index 7e0afdf..0000000 Binary files a/examples/data/blender/spaceship.bin and /dev/null differ diff --git a/examples/data/blender/spaceship.blend b/examples/data/blender/spaceship.blend deleted file mode 100644 index f2b19a8..0000000 --- a/examples/data/blender/spaceship.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b84405cbf54587bdacb6cf02b863fe7ae4e8b2965188f9aa3e9e420487c39a28 -size 888704 diff --git a/examples/data/blender/spaceship.gltf b/examples/data/blender/spaceship.gltf deleted file mode 100644 index d1ca2e4..0000000 --- a/examples/data/blender/spaceship.gltf +++ /dev/null @@ -1,453 +0,0 @@ -{ - "asset" : { - "generator" : "Khronos glTF Blender I/O v1.6.16", - "version" : "2.0" - }, - "scene" : 0, - "scenes" : [ - { - "name" : "Scene", - "nodes" : [ - 4 - ] - } - ], - "nodes" : [ - { - "mesh" : 0, - "name" : "Spaceship_Rear_Hatch", - "rotation" : [ - 2.8049830902432404e-08, - 8.798715533941959e-09, - 0.7938249707221985, - 0.6081464290618896 - ], - "scale" : [ - 0.17620019614696503, - 0.1893281489610672, - 0.1893281191587448 - ], - "translation" : [ - 0.9305203557014465, - -0.8287604479340512, - -1.1537752442336568e-07 - ] - }, - { - "children" : [ - 0 - ], - "name" : "Bone.001", - "rotation" : [ - -1.9689133878841858e-08, - -6.542580166524203e-08, - -0.12394285947084427, - 0.9922893643379211 - ], - "scale" : [ - 1, - 1.0000001192092896, - 1 - ], - "translation" : [ - 9.194034422677078e-17, - 1.0409293174743652, - 5.551115123125783e-17 - ] - }, - { - "mesh" : 1, - "name" : "Spaceship", - "rotation" : [ - -3.244472424057676e-08, - -1.6142790215667446e-08, - 0.7123286724090576, - 0.7018461227416992 - ], - "scale" : [ - 0.17620022594928741, - 0.1893281489610672, - 0.1893281191587448 - ], - "translation" : [ - 0.6980774998664856, - 0.008746981620788574, - 2.8927825468372248e-08 - ] - }, - { - "children" : [ - 1, - 2 - ], - "name" : "Bone", - "rotation" : [ - 1.2074783839466363e-08, - 2.1153852003408247e-08, - -0.007412227801978588, - 0.9999725222587585 - ], - "scale" : [ - 0.9999998807907104, - 1, - 1 - ] - }, - { - "children" : [ - 3 - ], - "name" : "Armature", - "rotation" : [ - 0, - 0, - -0.7071067690849304, - 0.7071068286895752 - ] - } - ], - "animations" : [ - { - "channels" : [ - { - "sampler" : 0, - "target" : { - "node" : 3, - "path" : "translation" - } - }, - { - "sampler" : 1, - "target" : { - "node" : 3, - "path" : "rotation" - } - }, - { - "sampler" : 2, - "target" : { - "node" : 3, - "path" : "scale" - } - }, - { - "sampler" : 3, - "target" : { - "node" : 1, - "path" : "translation" - } - }, - { - "sampler" : 4, - "target" : { - "node" : 1, - "path" : "rotation" - } - }, - { - "sampler" : 5, - "target" : { - "node" : 1, - "path" : "scale" - } - } - ], - "name" : "ArmatureAction.001", - "samplers" : [ - { - "input" : 8, - "interpolation" : "LINEAR", - "output" : 9 - }, - { - "input" : 8, - "interpolation" : "LINEAR", - "output" : 10 - }, - { - "input" : 8, - "interpolation" : "LINEAR", - "output" : 11 - }, - { - "input" : 8, - "interpolation" : "LINEAR", - "output" : 12 - }, - { - "input" : 8, - "interpolation" : "LINEAR", - "output" : 13 - }, - { - "input" : 8, - "interpolation" : "LINEAR", - "output" : 14 - } - ] - } - ], - "materials" : [ - { - "doubleSided" : true, - "name" : "Material.001", - "pbrMetallicRoughness" : { - "baseColorTexture" : { - "index" : 0 - }, - "metallicFactor" : 0, - "roughnessFactor" : 0.5 - } - } - ], - "meshes" : [ - { - "name" : "Cube.001", - "primitives" : [ - { - "attributes" : { - "POSITION" : 0, - "NORMAL" : 1, - "TEXCOORD_0" : 2 - }, - "indices" : 3, - "material" : 0 - } - ] - }, - { - "name" : "Cube.002", - "primitives" : [ - { - "attributes" : { - "POSITION" : 4, - "NORMAL" : 5, - "TEXCOORD_0" : 6 - }, - "indices" : 7, - "material" : 0 - } - ] - } - ], - "textures" : [ - { - "sampler" : 0, - "source" : 0 - } - ], - "images" : [ - { - "mimeType" : "image/png", - "name" : "Color Palette 140", - "uri" : "Color%20Palette%20140.png" - } - ], - "accessors" : [ - { - "bufferView" : 0, - "componentType" : 5126, - "count" : 56, - "max" : [ - 8.864760398864746, - 3.6763174533843994, - 2.2041707038879395 - ], - "min" : [ - 5.919982433319092, - 2.8251190185546875, - -2.2041707038879395 - ], - "type" : "VEC3" - }, - { - "bufferView" : 1, - "componentType" : 5126, - "count" : 56, - "type" : "VEC3" - }, - { - "bufferView" : 2, - "componentType" : 5126, - "count" : 56, - "type" : "VEC2" - }, - { - "bufferView" : 3, - "componentType" : 5123, - "count" : 90, - "type" : "SCALAR" - }, - { - "bufferView" : 4, - "componentType" : 5126, - "count" : 1487, - "max" : [ - 9.09161376953125, - 4.241826057434082, - 5.432835578918457 - ], - "min" : [ - -9.08266544342041, - 0.25478607416152954, - -5.432835578918457 - ], - "type" : "VEC3" - }, - { - "bufferView" : 5, - "componentType" : 5126, - "count" : 1487, - "type" : "VEC3" - }, - { - "bufferView" : 6, - "componentType" : 5126, - "count" : 1487, - "type" : "VEC2" - }, - { - "bufferView" : 7, - "componentType" : 5123, - "count" : 2334, - "type" : "SCALAR" - }, - { - "bufferView" : 8, - "componentType" : 5126, - "count" : 110, - "max" : [ - 4.583333333333333 - ], - "min" : [ - 0.041666666666666664 - ], - "type" : "SCALAR" - }, - { - "bufferView" : 9, - "componentType" : 5126, - "count" : 110, - "type" : "VEC3" - }, - { - "bufferView" : 10, - "componentType" : 5126, - "count" : 110, - "type" : "VEC4" - }, - { - "bufferView" : 11, - "componentType" : 5126, - "count" : 110, - "type" : "VEC3" - }, - { - "bufferView" : 12, - "componentType" : 5126, - "count" : 110, - "type" : "VEC3" - }, - { - "bufferView" : 13, - "componentType" : 5126, - "count" : 110, - "type" : "VEC4" - }, - { - "bufferView" : 14, - "componentType" : 5126, - "count" : 110, - "type" : "VEC3" - } - ], - "bufferViews" : [ - { - "buffer" : 0, - "byteLength" : 672, - "byteOffset" : 0 - }, - { - "buffer" : 0, - "byteLength" : 672, - "byteOffset" : 672 - }, - { - "buffer" : 0, - "byteLength" : 448, - "byteOffset" : 1344 - }, - { - "buffer" : 0, - "byteLength" : 180, - "byteOffset" : 1792 - }, - { - "buffer" : 0, - "byteLength" : 17844, - "byteOffset" : 1972 - }, - { - "buffer" : 0, - "byteLength" : 17844, - "byteOffset" : 19816 - }, - { - "buffer" : 0, - "byteLength" : 11896, - "byteOffset" : 37660 - }, - { - "buffer" : 0, - "byteLength" : 4668, - "byteOffset" : 49556 - }, - { - "buffer" : 0, - "byteLength" : 440, - "byteOffset" : 54224 - }, - { - "buffer" : 0, - "byteLength" : 1320, - "byteOffset" : 54664 - }, - { - "buffer" : 0, - "byteLength" : 1760, - "byteOffset" : 55984 - }, - { - "buffer" : 0, - "byteLength" : 1320, - "byteOffset" : 57744 - }, - { - "buffer" : 0, - "byteLength" : 1320, - "byteOffset" : 59064 - }, - { - "buffer" : 0, - "byteLength" : 1760, - "byteOffset" : 60384 - }, - { - "buffer" : 0, - "byteLength" : 1320, - "byteOffset" : 62144 - } - ], - "samplers" : [ - { - "magFilter" : 9729, - "minFilter" : 9987 - } - ], - "buffers" : [ - { - "byteLength" : 63464, - "uri" : "spaceship.bin" - } - ] -} diff --git a/examples/data/icosphere.glb b/examples/data/icosphere.glb deleted file mode 100644 index 73d3fe3..0000000 --- a/examples/data/icosphere.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:50b0efdbf32acbf2d9191856dc882405430d5788e96059b27176c1c7c1ddec70 -size 606540 diff --git a/examples/data/spaceship.glb b/examples/data/spaceship.glb deleted file mode 100644 index 0728575..0000000 --- a/examples/data/spaceship.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:78de8f432fcbfa9490f2bd4b8d0eba2af56fe3c2c1ffb67b92220b60f73647f2 -size 614084 diff --git a/examples/hello_world/main.cpp b/examples/hello_world/main.cpp deleted file mode 100644 index 1113839..0000000 --- a/examples/hello_world/main.cpp +++ /dev/null @@ -1,16 +0,0 @@ - -#include "renderer.h" - - -int -main() -{ - render_state* rs = renInit("Hello World"); - - if (rs == nullptr) - return 1; - - renDoRenderLoop(rs); - - return 0; -} diff --git a/examples/main.cpp b/examples/main.cpp new file mode 100644 index 0000000..cb62320 --- /dev/null +++ b/examples/main.cpp @@ -0,0 +1,331 @@ + +#include +#include + +#include + +#include "tangerine.h" + + +bool +loadLights(RenderState* rs) +{ + LightsBuffer* lb = rs->lights_buf; + u32 idx = 0; + +#if 0 + // NOTE: add a directional light + idx = (*lb->active_d_lights)++; + lb->dl_directions[idx] = glm::vec4(-2, 1, 3, 1); + lb->dl_colors[idx] = glm::vec4(0.5, 0.5, 0.3, 1); + lb->dl_intensities[idx] = glm::uvec4(1, 0, 0, 0); +#endif + // NOTE: add a point light + idx = (*lb->active_p_lights)++; + lb->pl_positions[idx] = glm::vec4(-10, 0, -10, 1); + lb->pl_colors[idx] = glm::vec4(1, 1, 0.8, 1); + lb->pl_intensities[idx] = glm::vec4(3, 0, 0, 0); + + GLBuffer* lights_ubo = getUBOByName(rs->gl_ctx, "lights"); + assert(lights_ubo != nullptr); + updateGLBuffer(lights_ubo, lb->buffer); + + // NOTE: add a debug mesh to view the point light source + RenderGroup* debug_group = getFreeRenderGroup(rs); + assert(debug_group); + ShaderProgram* s_debug = getShaderByName("debug", rs->gl_ctx); + assert(s_debug); + initRenderGroup(debug_group, rs->rg_arena, s_debug, 256, "debug_lights"); + Entity* e = getFreeEntity(debug_group); + assert(e); + if (!initEntity(e, + rs->gl_ctx, + rs->rg_arena, + &rs->assets.models[0], // tex_cube from loadScene() + s_debug->num_vertex_attribs, + s_debug->attrib_mappings, + "debug_light")) + { + LOGF(Error, "Error initializing debug entity for light\n"); + return false; + } + + setEntityPosition(e, glm::vec3(lb->pl_positions[idx])); + + return true; +} + +bool +loadCubes(RenderState* rs) +{ + // NOTE: load model + Model* tex_cube = getModelByPath(&rs->assets, "../data/textured_cube.gltf"); + if (!tex_cube) return false; + + // NOTE: load new shader, or get one of the defaults + // NOTE: the default shaders already have their attribute mappings created + // in initRenderState, but if you use a custom shader, you will need to + // create a GLBufferToAttribMapping manually + ShaderProgram* shader_lit = getShaderByName("full_lighting", rs->gl_ctx); + if (!shader_lit) return false; + + // NOTE: init new render group + RenderGroup* textured_cubes = getFreeRenderGroup(rs); + assert(textured_cubes); + initRenderGroup(textured_cubes, rs->rg_arena, shader_lit, 256, + "textured_cubes"); + + // NOTE: init entities + const u32 NUM_CUBES = 5; + glm::vec3 cube_locs[NUM_CUBES] = { + glm::vec3( 0, 0, 0), + glm::vec3(-10, 10, 0), + glm::vec3(-10, -10, 0), + glm::vec3( 10, 10, 0), + glm::vec3( 10, -10, 0), + }; + + for (u32 i = 0; i < NUM_CUBES; i++) { + Entity* e = getFreeEntity(textured_cubes); + assert(e); + char cube_name[256] = {0}; + snprintf(cube_name, 256, "textured_cube%d", i); + + if (!initEntity(e, + rs->gl_ctx, + rs->rg_arena, + tex_cube, + shader_lit->num_vertex_attribs, + shader_lit->attrib_mappings, + cube_name)) + { + return false; + } + + setEntityPosition(e, cube_locs[i]); + scaleEntity(e, 3); + } + + return true; +} + +// NOTE: test an entity with multiple meshes +bool +loadSpaceShip(RenderState* rs) +{ + Model* ship = getModelByPath(&rs->assets, "../data/spaceship.gltf"); + if (!ship) + return false; + + ShaderProgram* shader_lit = getShaderByName("full_lighting", rs->gl_ctx); + if (!shader_lit) + return false; + + // load model into gl + RenderGroup* rg = getFreeRenderGroup(rs); + assert(rs); + initRenderGroup(rg, rs->rg_arena, shader_lit, 256, "ships"); + Entity* e = getFreeEntity(rg); + assert(e); + + if (initEntity(e, rs->gl_ctx, + rs->rg_arena, + ship, + shader_lit->num_vertex_attribs, + shader_lit->attrib_mappings, + "ship 01")) + { + setEntityPosition(e, glm::vec3(0, -10, -15)); + } else { + return false; + } + + return true; +} + +bool +testColoredVertices(RenderState* rs) +{ + Mesh m = {0}; + m.num_vertices = 5; + m.num_indices = 12; + + glm::vec3 vertices[m.num_vertices] = { + { 0, 1, 0 }, + { -1, -1, -1 }, + { -1, -1, 1 }, + { 1, -1, 0.5 }, + }; + + u16 indices[m.num_indices] = { + 0, 1, 2, + 0, 2, 3, + 0, 3, 1, + 1, 2, 3 + }; + + glm::vec3 colors[m.num_vertices] = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + { 1, 1, 0 } + }; + + m.vertices = vertices; + m.colors = colors; + m.indices = indices; + glm::mat4 xform = glm::mat4(1); + m.xform = &xform; + Model mdl = {0}; + mdl.num_meshes = 1; + mdl.meshes = &m; + + ShaderProgram* shader = getShaderByName("colored_vertices", rs->gl_ctx); + RenderGroup* rg = getFreeRenderGroup(rs); + assert(shader && rg); + initRenderGroup(rg, rs->rg_arena, shader, 256, "colored_pyramids"); + Entity* e = getFreeEntity(rg); + assert(e); + + if (initEntity(e, rs->gl_ctx, + rs->rg_arena, + &mdl, + shader->num_vertex_attribs, + shader->attrib_mappings, + "colored pyramid 01")) + { + setEntityPosition(e, glm::vec3(0, -10, 15)); + } else { + return false; + } + + return true; +} + +bool +loadScene(RenderState* rs) +{ + if (!loadCubes(rs)) { + LOGF(Error, "Error loading cubes\n"); + return false; + } + + if (!testColoredVertices(rs)) { + LOGF(Error, "Error loading colored vertices\n"); + return false; + } +#if 1 + if (!loadSpaceShip(rs)) + return false; +#endif + + // NOTE: initilize the 'camera' + glm::vec3 cam_pos = { 0, 15, 40 }; + glm::vec3 look_pos = { 0, 0, 0 }; + glm::vec3 up = { 0, 1, 0 }; + rs->xforms->view_xform = glm::lookAt(cam_pos, look_pos, up); + GLBuffer* xforms_ubo = getUBOByName(rs->gl_ctx, "matrices"); + assert(xforms_ubo); + updateGLBuffer(xforms_ubo, rs->xforms); + + return loadLights(rs); +} + +void +orbitPositionZ0(glm::vec4* pos, float angle) +{ + // get radius length + float r = sqrt(pow(abs(pos->x), 2) + pow(abs(pos->z), 2)); + // get current angle about z axis + float a = atan2f(pos->z, pos->x); + // apply increment + a += angle; + // get new x/z components + pos->x = r * cos(a); + pos->z = r * sin(a); +} + +void +render_cb_pre(RenderState* rs) +{ + SDL_Event e; + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT || + (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) + { + rs->running = false; + break; + } + } + + // NOTE: orbit point light + LightsBuffer* lb = rs->lights_buf; + orbitPositionZ0(&lb->pl_positions[0], 2 * M_PI / 180); + RenderGroup* rg = getRenderGroupByName(rs, "debug_lights"); + Entity* ent = &rg->entities[0]; + setEntityPosition(ent, glm::vec3(lb->pl_positions[0])); + + GLBuffer* lights_ubo = getUBOByName(rs->gl_ctx, "lights"); + assert(lights_ubo); + updateGLBuffer(lights_ubo, lb->buffer); + + // NOTE: orbit camera + static glm::vec4 cam_pos; + static glm::vec3 look_pos; + static glm::vec3 up; + static bool initialized = false; + + if (!initialized) { + cam_pos = { 0, 15, 40, 1 }; + look_pos = { 0, 0, 0 }; + up = { 0, 1, 0 }; + initialized = true; + } + + orbitPositionZ0(&cam_pos, 0.5 * M_PI / 180); + rs->xforms->view_xform = + glm::lookAt(glm::vec3(cam_pos), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); + + static GLBuffer* xform_ubo = nullptr; + + if (!xform_ubo) { + xform_ubo = getUBOByName(rs->gl_ctx, "matrices"); + assert(xform_ubo != nullptr); + } + + updateGLBuffer(xform_ubo, rs->xforms); + + // NOTE: rotate cubes + RenderGroup* rg2 = getRenderGroupByName(rs, "textured_cubes"); + + for (u32 j = 0; j < rg2->num_entities; j++) { + Entity& e = rg2->entities[j]; + float direction = (j % 2 == 0) ? 1 : -1; + rotateEntity(&e, + glm::vec3(0, 1, 0), + direction * (float) M_PI / (3 * 60)); + } +} + +int +main() +{ + RenderState* rs = initRenderState(); + + if (rs) { + if (!loadScene(rs)) { + LOGF(Error, "error loading scene\n"); + return 1; + } + + doRenderLoop(rs, 60, render_cb_pre, nullptr); + + freeRenderState(rs); + return 0; + } + + LOGF(Error, "error loading scene\n"); + return 1; +} + diff --git a/examples/render_groups/main.cpp b/examples/render_groups/main.cpp deleted file mode 100644 index 633cff9..0000000 --- a/examples/render_groups/main.cpp +++ /dev/null @@ -1,233 +0,0 @@ - -#include -#include - -#include - -#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; -} - diff --git a/examples/simple_mesh/main.cpp b/examples/simple_mesh/main.cpp deleted file mode 100644 index fd67b6d..0000000 --- a/examples/simple_mesh/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ - -#include - -#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; -} diff --git a/include/GLDebug.h b/include/GLDebug.h new file mode 100644 index 0000000..eb13bfc --- /dev/null +++ b/include/GLDebug.h @@ -0,0 +1,147 @@ + +// NOTE: get useful debug messages from opengl +// https://www.khronos.org/opengl/wiki/Debug_Output + +#ifndef GL_DEBUG_H +#define GL_DEBUG_H + +#include + +#include "shader.h" + + +void dumpShader(GLuint prog_id); + +const char* glEnumToString(GLenum e); + +void openglDebugCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam); + +#endif // GL_DEBUG_H + + +#ifdef GL_DEBUG_IMPLEMENTATION + +const char* +glEnumToString(GLenum e) +{ + switch (e) { + case GL_FLOAT_MAT4: return "GL_FLOAT_MAT4"; + case GL_FLOAT_VEC3: return "GL_FLOAT_VEC3"; + case GL_FLOAT_VEC4: return "GL_FLOAT_VEC4"; + + case GL_BYTE: return "GL_BYTE"; + case GL_UNSIGNED_BYTE: return "GL_UNSIGNED_BYTE"; + case GL_SHORT: return "GL_SHORT"; + case GL_UNSIGNED_SHORT: return "GL_UNSIGNED_SHORT"; + case GL_INT: return "GL_INT"; + case GL_UNSIGNED_INT: return "GL_UNSIGNED_INT"; + case GL_FLOAT: return "GL_FLOAT"; + case GL_DOUBLE: return "GL_DOUBLE"; + + case GL_ARRAY_BUFFER: return "GL_ARRAY_BUFFER"; + case GL_ELEMENT_ARRAY_BUFFER: return "GL_ELEMENT_ARRAY_BUFFER"; + case GL_UNIFORM_BUFFER: return "GL_UNIFORM_BUFFER"; + case GL_TEXTURE_BUFFER: return "GL_TEXTURE_BUFFER"; + + case GL_DEBUG_TYPE_ERROR: return "GL_DEBUG_TYPE_ERROR"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + return "GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + return "GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR"; + case GL_DEBUG_TYPE_PORTABILITY: return "GL_DEBUG_TYPE_PORTABILITY"; + case GL_DEBUG_TYPE_PERFORMANCE: return "GL_DEBUG_TYPE_PERFORMANCE"; + case GL_DEBUG_TYPE_MARKER: return "GL_DEBUG_TYPE_MARKER"; + case GL_DEBUG_TYPE_PUSH_GROUP: return "GL_DEBUG_TYPE_PUSH_GROUP"; + case GL_DEBUG_TYPE_POP_GROUP: return "GL_DEBUG_TYPE_POP_GROUP"; + case GL_DEBUG_TYPE_OTHER: return "GL_DEBUG_TYPE_OTHER"; + + case GL_DEBUG_SEVERITY_HIGH: return "GL_DEBUG_SEVERITY_HIGH"; + case GL_DEBUG_SEVERITY_MEDIUM: return "GL_DEBUG_SEVERITY_MEDIUM"; + case GL_DEBUG_SEVERITY_LOW: return "GL_DEBUG_SEVERITY_LOW"; + case GL_DEBUG_SEVERITY_NOTIFICATION: + return "GL_DEBUG_SEVERITY_NOTIFICATION"; + + case GL_NO_ERROR: return "GL_NO_ERROR"; + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + + default: return "???"; + } +} + +void +openglDebugCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + // NOTE: filter out notification about using video memory for buffer object + if (id == 131185) + return; + + std::cout << "message id: " << id << ", " + << ((type == GL_DEBUG_TYPE_ERROR) ? "Error" : "Debug") + << (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "") + << ", type: " << glEnumToString(type) + << ", severity: " << glEnumToString(severity) + << ", message: " << message << "\n"; +} + +void +dumpShader(GLuint prog_id) +{ + LOGF(Debug, "------------------------\n"); + LOGF(Debug, "%s(), dumping shader info, program id: %d\n", + __FUNCTION__, prog_id); + + GLint active_uniforms; + GLint active_uniform_blocks; + GLint active_attribs; + + // NOTE: unused uniforms/attributes get optimized away + // https://www.khronos.org/opengl/wiki/Program_Introspection#Attributes + glGetProgramiv(prog_id, GL_ACTIVE_UNIFORMS, &active_uniforms); + glGetProgramiv(prog_id, GL_ACTIVE_UNIFORM_BLOCKS, &active_uniform_blocks); + glGetProgramiv(prog_id, GL_ACTIVE_ATTRIBUTES, &active_attribs); + + LOGF(Debug, "active uniforms: %d\n", active_uniforms); + LOGF(Debug, "active uniform blocks: %d\n", active_uniform_blocks); + LOGF(Debug, "active attributes: %d\n", active_attribs); + + GLchar uni_name[256]; + GLsizei length; + GLint size; + GLenum type; + + for (int i = 0; i < active_uniforms; i++) { + glGetActiveUniform(prog_id, i, sizeof(uni_name), + &length, &size, &type, uni_name); + LOGF(Debug, "uniform idx: %d, type: %s, name: %s \n", + i, glEnumToString(type), uni_name); + } + + for (int i = 0; i < active_attribs; i++) { + glGetActiveAttrib(prog_id, i, sizeof(uni_name), + &length, &size, &type, uni_name); + LOGF(Debug, "attribute idx: %d, type: %s, name: %s \n", + i, glEnumToString(type), uni_name); + } + + LOGF(Debug, "------------------------\n"); +} + +#endif // ifdef GL_DEBUG_IMPLEMENTATION + diff --git a/include/animation.h b/include/animation.h deleted file mode 100644 index 277ed0f..0000000 --- a/include/animation.h +++ /dev/null @@ -1,18 +0,0 @@ - -#pragma once - -#include - -#include "util.h" - - -struct render_object; - -struct node_animation -{ - render_object* ro; - glm::mat4* xform_array; - uint key_count; - uint cur_frame; -}; - diff --git a/include/asset.h b/include/asset.h index d0b1ba6..0d67609 100644 --- a/include/asset.h +++ b/include/asset.h @@ -4,73 +4,61 @@ #include #include -#include "animation.h" +#include "types.h" #include "util.h" -#include "util_image.h" +// NOTE: wrapper for stb_image +struct Texture +{ + i32 w; + i32 h; + i32 bits_per_channel; + i32 num_channels; + uint data_len; + u8* pixels; + u64 filepath_hash; + char file_path[256]; +}; + // NOTE: wrapper for tinygltf https://github.com/syoyo/tinygltf // https://github.com/KhronosGroup/glTF - -struct mesh +struct Mesh { - GLenum draw_mode; // NOTE: GL_LINES, GL_TRIANGLES - GLenum usage; // NOTE: GL_STATIC_DRAW, GL_DYNAMIC_DRAW - uint num_vertices; - uint num_indices; + u32 num_vertices; + u32 num_indices; glm::vec3* vertices; glm::vec3* normals; - glm::vec2* texture_coords; - uint* indices; + glm::vec2* uvs; + glm::vec3* colors; + u16* indices; // NOTE: u16 to match tinygltf library output glm::mat4* xform; }; -// TODO: will eventually need a tree structure with child nodes and transforms -// at each branch. Can then combine each transform down from the root node to -// the mesh, and send the final combination down to the shader - #define MAX_PATH_SIZE 256 -struct model +struct Model { - uint num_meshes; char* filepath; - uint64_t filepath_hash; - mesh* meshes; // NOTE: fixed sized array - // NOTE: pointer to a texture in render_state->textures - util_image* diffuse_texture; - // FIXME: node_animation has a pointer to a render_object. need to decouple - // that somehow - //node_animation* node_anim; -}; - -struct model_assets -{ - model* models; - uint count; - uint max; + u64 filepath_hash; + uint num_meshes; + Mesh* meshes; + Texture* diffuse_texture; }; -struct texture_assets +struct Assets { - util_image* images; - uint count; - uint max; + MemoryArena* arena; + u32 num_models; + u32 max_models; + Model* models; + + u32 num_textures; + u32 max_textures; + Texture* textures; }; -// TODO: would be nice to make these init functions generic -model_assets* -assetInitModelBlock(memory_arena* arena, uint asset_count); - -texture_assets* -assetInitTextureBlock(memory_arena* arena, uint asset_count); - -model* -assetLoadFromFile(model_assets* assets, - texture_assets* textures, - memory_arena* arena, - const char* filename); +Model* getModelByPath(Assets* assets, const char* filepath); -model* -assetGetCached(model_assets* assets, uint64_t path_hash); +Texture* getTextureByPath(Assets* assets, const char* filepath); diff --git a/include/camera.h b/include/camera.h deleted file mode 100644 index 4230fc4..0000000 --- a/include/camera.h +++ /dev/null @@ -1,66 +0,0 @@ - -#pragma once -#include -#include -#include "util.h" - - -struct camera -{ - float hAngle; - float vAngle; - - glm::vec3 position; - glm::vec3 forward; - glm::vec3 up; - glm::vec3 left; - glm::vec3 target; - glm::vec3 world_up; - - glm::mat4 model; - glm::mat4 view; - glm::mat4 projection; - glm::mat4 MVP; -}; - -enum projection_type -{ - PERSPECTIVE, - ORTHOGRAPHIC, -}; - - -v2f -cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height); - -v3f -cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims); - -bool -cameraIntersectPlane(camera& cam, - v3f ray, - v3f plane_origin, - v3f plane_normal, - v3f& intersection); - -void -cameraInitPerspective(camera* cam, - glm::vec3 position, - glm::vec3 target, - glm::vec3 world_up, - float aspect_ratio = 16.f / 9.f); - -void -cameraMove(camera& cam, - bool up, - bool left, - bool down, - bool right, - bool forward, - bool backward); - -void -cameraRotate(camera& cam, int32 xrel, int32 yrel); - -void -cameraRoll(camera& cam, bool CW, bool CCW); diff --git a/include/dumbLog.h b/include/dumbLog.h index 769cfa4..be27e25 100644 --- a/include/dumbLog.h +++ b/include/dumbLog.h @@ -24,7 +24,14 @@ struct dumbLog static dumbLog logger; #define LOG(level) *logger.OUT \ - << std::put_time(logger.getCurrentTime(), "%F %T.") << logger.getCurrentMS() << " " \ + << std::put_time(logger.getCurrentTime(), "%F %T.") \ + << logger.getCurrentMS() << " " \ << "[" << logger.logLevelToString(level) << "] " \ << "(" << __FUNCTION__ << ") " + +#include + +#define LOGF(level, format_str, ...) \ + dumbLogF(level, __FUNCTION__, format_str, ##__VA_ARGS__); +void dumbLogF(log_level l, const char* func, const char* fmt, ...); diff --git a/include/dummy_shader.h b/include/dummy_shader.h new file mode 100644 index 0000000..38316e7 --- /dev/null +++ b/include/dummy_shader.h @@ -0,0 +1,30 @@ + +#pragma once + + +const char* DUMMY_VERTEX_SHADER = R"VS( +#version 330 core +layout (location = 0) in vec3 position; + +uniform mat4 world_transform; +uniform mat4 MVP; + + +void main() +{ + gl_Position = MVP * world_transform * vec4(position, 1); +} +)VS"; + +const char* DUMMY_FRAGMENT_SHADER = R"FS( +#version 330 core + +out vec4 color; + + +void main() +{ + color = vec4(1, 0, 0, 1); +} +)FS"; + diff --git a/include/entity.h b/include/entity.h index aadf54e..b85a78f 100644 --- a/include/entity.h +++ b/include/entity.h @@ -1,30 +1,32 @@ #pragma once -#include - #include "asset.h" -#include "render_object.h" +#include "shader.h" #include "types.h" +#include "util.h" -struct entity +struct Entity { - glm::mat4 world_transform; - uint64_t model_id; // NOTE: filepath hash - render_objects* render_objs; + u32 num_meshes; + GLMesh* meshes; + GLTexture* diffuse_texture; // NOTE: pointer into gl_ctx->textures array + glm::mat4* model_xform; + char* name; }; -bool entInitModel(entity* e, model* mdl); - -// FIXME: might as well stay consistent and make all these pointers -void entFree(entity* e); - -void entSetWorldPosition(entity& e, glm::vec3 v); -void entTranslate(entity& e, glm::vec3 v); +bool initEntity(Entity* e, + GLContext* gl_ctx, + MemoryArena* arena, + Model* mdl, + u32 num_attrib_mappings, + GLBufferToAttribMapping* attrib_mappings, + const char* name = ""); -void entScale(entity& e, glm::vec3 v); +void setEntityPosition(Entity* e, glm::vec3 pos); -void entRotate(entity* e, float angle, glm::vec3 axis); +void rotateEntity(Entity* e, glm::vec3 axis, float radians); +void scaleEntity(Entity* e, float scale); diff --git a/include/input.h b/include/input.h deleted file mode 100644 index 806a716..0000000 --- a/include/input.h +++ /dev/null @@ -1,23 +0,0 @@ - -#pragma once - -#include - - -struct input_state -{ - bool window_closed; - bool escape; - bool left; - bool right; - bool up; - bool down; -}; - -void -inputProcessEvent(input_state* is, SDL_Event& e); - -// NOTE: convenience function that provides a while(SDL_PollEvents()) loop -void -inputProcessEvents(input_state* is); - diff --git a/include/lights.h b/include/lights.h deleted file mode 100644 index e5cc141..0000000 --- a/include/lights.h +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - -#include - -#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); - diff --git a/include/mesh.h b/include/mesh.h deleted file mode 100644 index 1fe81b6..0000000 --- a/include/mesh.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * mesh.h - * - wrapper for assimp http://www.assimp.org - */ - -#pragma once - -#include - -#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(); - diff --git a/include/platform_wait_for_vblank.h b/include/platform_wait_for_vblank.h deleted file mode 100644 index d11a353..0000000 --- a/include/platform_wait_for_vblank.h +++ /dev/null @@ -1,102 +0,0 @@ - -// attempt to fix vsync with opengl on windows -// https://bugs.chromium.org/p/chromium/issues/detail?id=467617 -// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmthk/nf-d3dkmthk-d3dkmtwaitforverticalblankevent -// NOTE: requires installing windows driver kit addon for msvc -// TODO: not working with hdc provided by SDL, try enumerating display devices as described here: -// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmthk/nf-d3dkmthk-d3dkmtopenadapterfromhdc -// NOTE: the above seems to make some difference in lowering cpu usage, but cpu stays at max frequency even when relatively idle. - -// TODO: if d3dkmt... doensn't work, try DWMFlush() https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmflush -// glfw uses this https://github.com/glfw/glfw/blob/master/src/wgl_context.c -// -// if that doesn't work... https://docs.microsoft.com/en-us/windows/desktop/api/timeapi/nf-timeapi-timebeginperiod -// to increase schedular granularity. can then use sleep(1) for 1ms resolution - -#pragma once - -#include - -#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 -} - diff --git a/include/render_object.h b/include/render_object.h deleted file mode 100644 index eefe9e8..0000000 --- a/include/render_object.h +++ /dev/null @@ -1,27 +0,0 @@ - -#pragma once - -#include -#include - -#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); - diff --git a/include/renderer.h b/include/renderer.h deleted file mode 100644 index 02eeafb..0000000 --- a/include/renderer.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * libTangerine, a small, modern openGL renderer specializing in flat shaded, - * solid color models using pallete textures. See README for build instructions - * and look in the examples folder for... examples - * - * fixmes) - * FIXME: load textures into texture asset array, and map in model structure - * FIXME: better shader abstraction so we can store a list of shaders on - * render_state - * FIXME: clean up examples with new asset system - * FIXME: remove mesh.h, mesh.cpp - * FIXME: merge render_group_fix branch back into master - * FIXME: remove platform_wait_for_vblank - * FIXME: make a test case for overflowing default array sizes, rg->assets - * rg_info->groups, render_group->enitities, rg->textures - * FIXME: look for glm::mat4 in structs, and replace with pointers - * - * todos) - * TODO: make assetInit* functions generic - * TODO: make initModel/initTexture functions generic - * TODO: resizable arrays for entities and asset system - * TODO: defaults include for various #defines - */ - -#pragma once - -#if defined (_WIN32) - #include -#else - #include -#endif -#include -#include - -#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); - diff --git a/include/shader.h b/include/shader.h new file mode 100644 index 0000000..898fa9e --- /dev/null +++ b/include/shader.h @@ -0,0 +1,222 @@ + +#pragma once + +#include +#include +#include + +#include "types.h" +#include "util.h" + + +enum UniformType +{ + UNIFORM_SAMPLER, + UNIFORM_NODE_XFORM, + UNIFORM_VIEW_XFORM, + UNIFORM_PROJECTION_XFORM, + UNIFORM_NORMAL_XFORM, + UNIFORM_BLOCK_XFORMS, + UNIFORM_BLOCK_LIGHTS, + UNIFORM_UNKNOWN, + UNIFORM_TYPE_COUNT +}; + +struct GLUniform +{ + // NOTE: would be nice to use idx as the location parameter because it seems + // to match when the uniform isn't part of a uniform block, at least with + // intel mesa driver, but apparently that's not guarantied + GLuint idx; + GLint location; + GLint block_idx; + UniformType uniform_type; + GLenum gl_type; // NOTE: GL_UNSIGNED_INT, GL_FLOAT_VEC4 + GLint num_elements; // NOTE: 1 unless uniform is an array of base types + GLint array_stride; // NOTE: bytes between array elements + GLint uniform_offset; // NOTE: byte offset from beginning of uniform + char* name; +}; + +struct GLUniformBlock +{ + GLuint block_id; + GLint binding_idx; + UniformType uniform_type; + GLuint num_uniforms; + GLUniform* uniforms; + char* name; +}; + +enum MeshBufferType +{ + VERTEX, + NORMAL, + UV, + COLOR, + MESH_BUFFER_TYPE_COUNT +}; + +// NOTE: we need another struct for vertex attributes that mirrors GLBuffer +// because an attribute is associated with a ShaderProgram while a buffer is +// associated with the vertex data passed to glBufferData() +struct GLVertexAttrib +{ + MeshBufferType buf_type; + GLenum data_type; + GLenum component_type; + u32 num_components; + GLuint location; + char* name; +}; + +// TODO: add a note explaining usage when we implement complex entities +struct GLBufferToAttribMapping +{ + GLVertexAttrib* attrib; + MeshBufferType buf_type; +}; + +struct ShaderProgram +{ + GLuint prog_id; + + u32 num_blocks; + GLUniformBlock* uniform_blocks; + + u32 num_uniforms; + GLUniform* uniforms; + + u32 num_vertex_attribs; + GLVertexAttrib* vertex_attribs; + GLBufferToAttribMapping* attrib_mappings; + + char* name; + u64 hash; // NOTE: hash of vs filpath + fs filepath concat +}; + +struct GLBuffer +{ + GLuint id; + GLenum target; + GLenum data_type; + GLuint data_size; // NOTE: size of buffer in bytes + GLint location; // NOTE: if used as backing for vertex attribute + GLint binding_idx; // NOTE: if used as backing from uniform buffer object + char* name; +}; + +struct GLTexture +{ + GLuint id; + GLenum pixel_format; // NOTE: GL_RGB or GL_RGBA + u32 width; + u32 height; + u64 filepath_hash; +}; + +struct GLContext +{ + GLuint binding_count; + GLint max_binding_points; + GLint max_vertex_blocks; + GLint max_fragment_blocks; + GLint max_ublock_size; + GLint max_vertex_attribs; + + u32 max_ubos; + u32 num_ubos; + GLBuffer* uniform_buffers; + + u32 max_shaders; + u32 num_shaders; + ShaderProgram* shaders; + + u32 max_textures; + u32 num_textures; + GLTexture* textures; +}; + +struct GLMesh +{ + u32 num_indices; + GLuint vao_id; + bool has_texture; + GLuint tex_id; + GLenum draw_mode; // NOTE: GL_LINES, GL_TRIANGLES + GLenum usage; // NOTE: GL_STATIC_DRAW, GL_DYNAMIC_DRAW + + glm::mat4* node_xform; + + u32 num_vertex_attrib_buffers; + GLBuffer* vertex_attrib_buffers; + GLBuffer* element_buf; +}; + +struct Animation; + +struct Transforms +{ + glm::mat4 view_xform; + glm::mat4 proj_xform; + glm::mat4 normal_xform; +}; + + +const float DEFAULT_FOV = 60.f; +const float NEAR_CLIP_PLANE = 5.f; +const float DEFAULT_ASPECT_RATIO = 16.f / 9.f; + + +GLContext* initGLContext(MemoryArena* arena, + u32 max_shaders, + u32 max_textures, + u32 max_ubos); + +// NOTE: every shader program is assumed to have one uniform block named +// "matrices" that contains the projection and view matrices, and one uniform +// named "node_xform" for the node matrix +bool addShaderProgram(MemoryArena* arena, + GLContext* gl_ctx, + const char* vs, + const char* fs, + const char* name); + +ShaderProgram* getShaderByName(const char* name, GLContext* gl_ctx); + +ShaderProgram* getShaderByID(GLContext* gl_ctx, GLuint prog_id); + +ShaderProgram* getShaderByHash(GLContext* gl_ctx, u64 hash); + +ShaderProgram* getFreeShader(GLContext* gl_ctx); + +GLBuffer* getFreeUBO(GLContext* gl_ctx); + +GLBuffer* getUBOByName(GLContext* gl_ctx, const char* name); + +GLTexture* getGLTexture(GLContext* gl_ctx, Texture* diffuse_img); + +void updateGLBuffer(GLBuffer* gl_buf, void* data); + +void renderVAO(GLMesh* glmesh, + glm::mat4* node_xform, + ShaderProgram* shader, + GLTexture* gl_tex); + +GLVertexAttrib* getVertexAttribByName(ShaderProgram* shader, const char* name); + +GLMesh loadGLMesh(MemoryArena* arena, + const Mesh& m, + GLenum draw_mode, + GLTexture* diffuse_texture, + u32 num_mappings, + GLBufferToAttribMapping mappings[]); + +void initTransforms(MemoryArena* arena, + Transforms* xforms, + GLBuffer* xform_ubo, + GLContext* gl_ctx, + float fov = DEFAULT_FOV, + float near_clip_plane = NEAR_CLIP_PLANE, + float aspect_ratio = DEFAULT_ASPECT_RATIO); + diff --git a/include/shader_program.h b/include/shader_program.h deleted file mode 100644 index dba1f55..0000000 --- a/include/shader_program.h +++ /dev/null @@ -1,57 +0,0 @@ - -#pragma once - -#include - -#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); diff --git a/include/tangerine.h b/include/tangerine.h new file mode 100644 index 0000000..d16958b --- /dev/null +++ b/include/tangerine.h @@ -0,0 +1,279 @@ +/* + * libTangerine, a small, modern openGL renderer specializing in flat shaded, + * solid color models using pallete textures. See README for build instructions + * and look in the examples folder for... examples + */ + +/* +* === TODO: === +* - add scene abstrastion for RenderState +* - add an example of dynamically switching shaders for an entity +* - fix debug load times (either by using cgltf, or hiding tinygltf.h) +* - RenderGroups and Entities need to come into and out of existence during +* gameplay. So, we need to extend MemoryArena to be a pool allocator +* instead of an allocate only linear allocator +* - add libTangerine namespace? +* - merge back into libTangerine +* - make separate shaders for per vertex, and per pixel/fragment lighting +* see red book chapter 7 +* - add a bloom/blur render to texture shader for light sources? +* https://learnopengl.com/Advanced-Lighting/Bloom +* - clean up examples with new asset system +* - merge render_group_fix branch back into master +* - make a test case for overflowing default array sizes, rg->assets +* rg_info->groups, render_group->enitities, rg->textures +* - look for glm::mat4 in structs, and replace with pointers (easier debugging) +* - resizable arrays for entities and asset system +* - defaults include for various #defines +* +* === TODONE: === +* - don't allocate Asset structure on asset arena +* - store on RenderState as reference +* - store asset arena to asset structure +* - condense get_X_ByPath functions with assetLoad functions +* - move to asset interface +* - load diffuse texture automatically when loading a model +* - move entity structure and functions to new file? +* - load diffuse texture into OpenGL, and store GLTexture structure onto +* GLContext +* - need a cache algorithm starting in initEntity() +* - check for GLTexture by path hash +* - load texture into gl if not cached +* - pass result GLTexture to loadGLMesh() +* - update loadScene function with textured models, and test texture loading +* - work on cleaner interface for initEntity and loadScene... +* - add a LOGF macro for printf style logging +* - replace instances of printf +* - rename data structures to be in the new format, eg) UpperCaseStyleNames +* - rename instances of 'model_xform' that refer to a mesh node to something +* like node_xform. model_xform should be reserved for the entity node +* - move default shaders out of git-lfs +* - full lighting model +* - add buffer backed storage for light arrays in GLSL +* - remove hard-coded values in full_lighting.frag +* - test buffer backed lights, probably need to pad any scalars/vec3s +* - add point light type with tweakable attenuation parameters +* - test complex entities +* - need a separate GLBufferToAttribMapping for each mesh on an entity +* - maybe fixed now with MeshBufferType enum and getMeshData() +* - allow entities to have an empty diffuse texture (see initEntity()) +* - use dynamic shader parsing to set 'sampler_id' for shaders with textures +* - add ambient light to LightsBuffer structure +* - remember to update offsets in initLights() +* - use rg_arena for store GLMeshes, see initGLMesh() +*/ + + +#pragma once + +#include + +#include "asset.h" +#include "entity.h" +#include "dumbLog.h" +#include "GLDebug.h" +#include "shader.h" +#include "types.h" +#include "util.h" + + +struct SDLHandles +{ + SDL_Window* window; + SDL_GLContext sdl_gl_ctx; + SDL_DisplayMode display_mode; +}; + +// TODO: node/tree structure for entities +struct Node; + +struct RenderGroup +{ + ShaderProgram* shader; + u32 num_entities; + u32 max_entities; + Entity* entities; + char* name; +}; + +#if 0 +struct Scene +{ + MemoryArena* rg_arena; + u32 num_render_groups; + u32 max_render_groups; + RenderGroup* render_groups; + + u32 num_point_lights; + u32 max_point_lights; + PointLight* p_lights; + + u32 num_d_lights; + u32 max_d_lights; + DirectionalLight* d_lights; + + u32 num_spot_lights; + u32 max_spot_lights; + SpotLight* s_lights; + + Node root_node; + + Camera cam; +}; +#endif + +struct GLClearColor +{ + GLfloat R; + GLfloat G; + GLfloat B; + GLfloat A; +}; + +// NOTE: structure to match the layout of the 'lights' uniform in a shader +// all the pointers are pointers into the address space of 'buffer'. the vec4 +// pointers are required to start on 16 byte boundaries as per layout std140, +// so there may be some padding added between the 'header', and the start of +// the arrays +// NOTE: this is also very fragile. Since c/c++ doesn't support reflection, we +// need to manually update 'initLights()' if we ever update this structure. +// And remember to update the 'padding' if the number of 'header' attributes +// change +struct LightsBuffer +{ + u32 buf_size; + u32* max_p_lights; + u32* active_p_lights; + u32* max_d_lights; + u32* active_d_lights; + + glm::vec4* ambient_color; + + glm::vec4* pl_positions; + glm::vec4* pl_colors; + glm::uvec4* pl_intensities; + + glm::vec4* dl_directions; + glm::vec4* dl_colors; + glm::uvec4* dl_intensities; + + void* buffer; +}; + +// FIXME: re-implement Camera interface +struct Camera; + +struct RenderState +{ + bool running; + GLClearColor clear_col; + Transforms* xforms; // NOTE: would be part of camera in libTangerine + SDLHandles handles; + GLContext* gl_ctx; + Assets assets; + + MemoryArena* rg_arena; + u32 num_render_groups; + u32 max_render_groups; + RenderGroup* render_groups; + + // TODO: should we have a 'scene' abstraction here? + // could have render groups, lights, camera + // could match up with gltf scene graph where everyting is part of a node + LightsBuffer* lights_buf; + + // FIXME: re-integrating missing libTangerine render_state properties + Camera* camera; + glm::vec2 viewport_dims; + /// +}; + + +#define DEFAULT_MODEL_COUNT 256 +#define DEFAULT_TEXTURE_COUNT 64 +#define DEFAULT_SHADER_COUNT 64 +#define DEFAULT_UBO_COUNT 32 +#define DEFAULT_RENDER_GROUP_COUNT 256 +#define DEFAULT_CLEAR_COLOR { 0.2, 0.2, 0.2, 1 } +#define DEFAULT_AMBIENT_COLOR { 0.1, 0.1, 0.1, 1 } +#define DEFAULT_MAX_LIGHTS 32 // NOTE: needs to match NUM_LIGHTS in shaders +RenderState* initRenderState(GLClearColor clear_col = DEFAULT_CLEAR_COLOR, + glm::vec4 ambient_color = DEFAULT_AMBIENT_COLOR, + u32 max_models = DEFAULT_MODEL_COUNT, + u32 max_textures = DEFAULT_TEXTURE_COUNT, + u32 max_shaders = DEFAULT_SHADER_COUNT, + u32 max_render_groups = DEFAULT_RENDER_GROUP_COUNT, + u32 max_ubos = DEFAULT_UBO_COUNT, + u32 max_lights = DEFAULT_MAX_LIGHTS); + +void freeRenderState(RenderState*& rs); + +#define DEFAULT_ENTITY_COUNT 256 +void initRenderGroup(RenderGroup* rg, + MemoryArena* arena, + ShaderProgram* shader, + u32 num_entities = DEFAULT_ENTITY_COUNT, + const char* name = ""); + +void freeRenderGroup(RenderGroup* rg, MemoryArena* arena); + +RenderGroup* getFreeRenderGroup(RenderState* rs); + +RenderGroup* getRenderGroupByName(RenderState* rs, const char* name); + +Entity* getFreeEntity(RenderGroup* rg); + +// NOTE: callback function signature to use with renDoRenderLoop() +typedef void (*frame_callback_fn) (RenderState*); + +// NOTE: There are 2 callbacks to use here, cb_func_pre is called before +// the call to renRenderFrame(), cb_fun_post is called after +// NOTE: if you use cb_func_pre, you will have to use SDL_PollEvent() manually +// and at minimum set rs->running = false on SDL_QUIT event +void doRenderLoop(RenderState* rs, + uint framerate = 60, + frame_callback_fn cb_func_pre = nullptr, + frame_callback_fn cb_func_post = nullptr); + +void renderFrame(RenderState* rs, const GLClearColor& clear_col); + + +// NOTE: automatic loading of default shaders + +struct ShaderInit +{ + const char* name; + const char* vert_path; + const char* frag_path; +}; + +#define SHADER_INIT_COUNT 4 +#define TEXTURE_ONLY_SHADER_INIT { "texture_only", \ + "../data/texture_only.vert", \ + "../data/texture_only.frag" } +#define FULL_LIGHTING_SHADER_INIT { "full_lighting", \ + "../data/full_lighting.vert", \ + "../data/full_lighting.frag" } +#define DEBUG_SHADER_INIT { "debug", \ + "../data/debug.vert", \ + "../data/debug.frag", } +#define COLORED_VERT_SHADER_INIT { "colored_vertices", \ + "../data/colored_vertices.vert", \ + "../data/colored_vertices.frag", } +const ShaderInit SHADER_INIT_LIST[SHADER_INIT_COUNT] +{ + TEXTURE_ONLY_SHADER_INIT, + FULL_LIGHTING_SHADER_INIT, + DEBUG_SHADER_INIT, + COLORED_VERT_SHADER_INIT +}; + +bool loadDefaultShaders(RenderState* rs, + u32 num_shaders = SHADER_INIT_COUNT, + const ShaderInit shaders[] = SHADER_INIT_LIST); + +// NOTE: only useful if the shader attribute names match the names in the +// default shaders. If loading a custom shader, this may not work as expected, +// and you should use getVertexAttribByName instead +GLVertexAttrib* getVertexAttribByType(ShaderProgram* shader, + MeshBufferType buf_type); diff --git a/include/types.h b/include/types.h index 59fcbe7..e2bbdbd 100644 --- a/include/types.h +++ b/include/types.h @@ -1,16 +1,13 @@ #pragma once +#include -enum shader_t -{ - SIMPLE_SHADER, - DEFAULT_SHADER -}; -enum mesh_t -{ - SIMPLE_MESH, - DEFAULT_MESHES -}; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int32_t i32; +typedef int64_t i64; diff --git a/include/util.h b/include/util.h index 237b37f..00a94a3 100644 --- a/include/util.h +++ b/include/util.h @@ -1,118 +1,193 @@ -#pragma once +#ifndef UTIL_H +#define UTIL_H -#include #include +#include +#include +#include +#include "dumbLog.h" +#include "types.h" -typedef float real32; -typedef float GLfloat; -typedef double real64; -typedef int32_t bool32; -typedef int32_t int32; -typedef int64_t int64; -typedef uint8_t uint8; -typedef uint32_t uint32; -typedef uint32_t uint; +//----------------- +// Hashing -struct v2f -{ - v2f(): x(0), y(0) {} - v2f(real64 a, real64 b): x(a), y(b) {} - real64 x; - real64 y; -}; +// NOTE: FNV1a hashing algorithm http://www.isthe.com/chongo/tech/comp/fnv/ +#define FNV1_64_INIT ((u64) 0xcbf29ce484222325ULL) +#define FNV_64_PRIME ((u64) 0x100000001b3ULL) +u64 utilFNV64a_str(const char* str, u64 hval = FNV1_64_INIT); -struct v2i -{ - v2i(int a, int b): x(a), y(b) {} - v2i() : x(0), y(0) {} - int32 x; - int32 y; -}; +//----------------- +// Memory allocation -struct v3f +struct MemoryArena { - v3f(): x(0), y(0), z(0) {} - v3f(real64 a, real64 b, real64 c): x(a), y(b), z(c) {} - real64 x; - real64 y; - real64 z; + size_t max_size; + size_t free_size; + void* head; + void* next_free; }; -inline int32 -SafeTruncateToInt32(int64 val) -{ - assert(val <= INT32_MAX && val >= INT32_MIN); - return (int32) val; -} +#define DEFAULT_ARENA_SIZE 10 * 1024 * 1024 // 10MB +MemoryArena* arenaInit(size_t initial_size = DEFAULT_ARENA_SIZE); -inline void -utilConvertColor(GLfloat buf[3], uint32 color) -{ - // NOTE: not using the alpha values - buf[0] = (GLfloat) ((color >> 24) & 0xFF) / (GLfloat) 255; - buf[1] = (GLfloat) ((color >> 16) & 0xFF) / (GLfloat) 255; - buf[2] = (GLfloat) ((color >> 8) & 0xFF) / (GLfloat) 255; -} +void arenaFree(MemoryArena*& arena); -//----------------- -// C Strings +u32 arenaGetFreeSize(MemoryArena* arena); + +#define ARENA_ALLOC(arena, type, count) \ + (type*) arenaAllocateBlock(arena, sizeof(type) * count) +void* arenaAllocateBlock(MemoryArena* arena, size_t block_size); + +void* arenaGetAddressOffset(void* address, u32 offset); + +#define MAX_NAME_LENGTH 256 +char* arenaCopyCStr(MemoryArena* arena, + const char* input, + u32 max_len = MAX_NAME_LENGTH); + +#define UTIL_ALLOC(count, type) (type*) utilAllocate(count, sizeof(type)) +void* utilAllocate(u32 count, u32 type_size); + +char* utilAllocateCStr(const char* str, u32 max_len = 256); -// NOTE: max_len should be the allocated size of dest -bool utilCopyCStr(char* dest, const char* src, uint max_len); +void utilSafeFree(void* p); -// NOTE: returns new string with '/' between -// NOTE: max_len should be the size of return buffer. -char* utilConcatPath(char* out, const char* base_dir, const char* file_name, uint max_len); +//--------------- +// C string utils -const char* utilBaseName(const char* path_str); +bool utilCStrMatch(const char* str1, const char* str2); -// NOTE: returns true if the first 'sz' characters from each string match -bool utilMatchPrefix(const char* lhs, const char* rhs, int sz); + +#endif // UTIL_H + + +#ifdef UTIL_IMPLEMENTATION //----------------- // Hashing -// NOTE: FNV1a hashing algorithm http://www.isthe.com/chongo/tech/comp/fnv/ -#define FNV1_64_INIT ((uint64_t) 0xcbf29ce484222325ULL) -#define FNV_64_PRIME ((uint64_t) 0x100000001b3ULL) -uint64_t -utilFNV64a_str(const char *str, uint64_t hval = FNV1_64_INIT); +u64 +utilFNV64a_str(const char* str, u64 hval) +{ + unsigned char* s = (unsigned char *)str; // unsigned string + + // FNV-1a hash each octet of the string + while (*s) { + // xor the bottom with the current octet + hval ^= (uint64_t)*s++; + // multiply by the 64 bit FNV magic prime mod 2^64 + hval *= FNV_64_PRIME; + } + + return hval; +} //----------------- // Memory allocation -// NOTE: Wrapper for calloc that will send error message on out of memory -#define UTIL_ALLOC(len, type) (type *) utilLogAlloc((len), sizeof(type), __FILE__, __LINE__) -void* utilLogAlloc(uint item_count, uint type_size, const char* file_name, const int line); +MemoryArena* +arenaInit(size_t initial_size) +{ + u32 sz = sizeof(MemoryArena); + MemoryArena* arena = + (MemoryArena*) std::calloc(initial_size + sz, sizeof(u8)); + arena->head = arena->next_free = (uint8_t*) arena + sz; + arena->max_size = initial_size; + return arena; +} -// TODO: replace instances of 'utilSafeFree()' with macro that casts to void -// pointer reference. Can then set the pointer to nullptr in free function -#define UTIL_FREE(mem_ptr) utilSafeFree((void*&) mem_ptr) -void utilSafeFree(const void* mem); +void +arenaFree(MemoryArena*& arena) +{ + if (arena != nullptr) { + std::free(arena); + arena = nullptr; + } +} -struct memory_arena +uint +arenaGetFreeSize(MemoryArena* arena) { - size_t max_size; - void* head; - void* next_free; -}; + return (uint8_t*) arena->head + + arena->max_size + - (uint8_t*) arena->next_free; +} -#define DEFAULT_ARENA_SIZE 10 * 1024 * 1024 // 10MB -memory_arena* arenaInit(size_t initial_size = DEFAULT_ARENA_SIZE); +void* +arenaAllocateBlock(MemoryArena* arena, size_t block_size) +{ + // TODO: resizable memory arena + assert(arenaGetFreeSize(arena) >= block_size); + assert(block_size > 0); + void* ret = arena->next_free; + arena->next_free = (uint8_t*) arena->next_free + block_size; + arena->free_size = arenaGetFreeSize(arena); + return ret; +} + +void* +arenaGetAddressOffset(void* address, u32 offset) +{ + return (void*) ((u8*) address + offset); +} -void arenaFree(memory_arena*& arena); +char* +arenaCopyCStr(MemoryArena* arena, const char* input, u32 max_len) +{ + u32 name_len = std::strlen(input) + 1; + assert(name_len > 1 && name_len < max_len); + char* out = ARENA_ALLOC(arena, char, name_len); + std::strncpy(out, input, name_len); + return out; +} -uint arenaGetFreeSize(memory_arena* arena); +void* +utilAllocate(u32 count, u32 type_size) +{ + void* out = std::calloc(count, type_size); + assert(out != nullptr); + return out; +} -void* arenaAllocateBlock(memory_arena* arena, size_t block_size); +char* +utilAllocateCStr(const char* str, u32 max_len) +{ + u32 len = strlen(str) + 1; -//----------------- -// File I/O + if (len > max_len) { + LOGF(Error, "%s , longer than %i\n", str, max_len); + return nullptr; + } -char* utilDumpTextFile(const char* filename); + char* out = (char*) std::calloc(len, sizeof(u8)); + strncpy(out, str, len - 1); + return out; +} -bool utilWriteTextFile(const char* filename, const char* text); +void +utilSafeFree(void* p) +{ + if (p) + free(p); + else + LOGF(Error, "free called on nullptr\n"); +} + +//--------------- +// C string utils + +bool +utilCStrMatch(const char* str1, const char* str2) +{ + assert(str1 != nullptr && str2 != nullptr); + u32 l1 = strlen(str1); + u32 l2 = strlen(str2); + + return (l1 == l2) + && (strncmp(str1, str2, l1) == 0); +} +#endif diff --git a/include/util_image.h b/include/util_image.h deleted file mode 100644 index 1d27d34..0000000 --- a/include/util_image.h +++ /dev/null @@ -1,35 +0,0 @@ - -#pragma once - -#include "util.h" - - -// NOTE: wrapper for stb_image -struct util_image -{ - int32 w; - int32 h; - int32 bits_per_channel; - int32 num_channels; - uint data_len; - uint8* pixels; - uint64_t filepath_hash; - // FIXME: should use a pointer here, and just add the length of file_path - // onto the allocation for util_image - char file_path[256]; -}; - -struct util_RGBA -{ - real32 R; - real32 G; - real32 B; - real32 A; -}; - -util_image utilLoadImagePath(const char* full_path); - -util_image utilLoadImageBytes(const unsigned char* bytes, uint length); - -void utilFreeImage(util_image image); - diff --git a/src/asset.cpp b/src/asset.cpp index db15512..289d294 100644 --- a/src/asset.cpp +++ b/src/asset.cpp @@ -10,49 +10,82 @@ // forward declarations + +// TODO: move to GLDebug.h? void dumpNodes(tinygltf::Model t_mdl); -model* initModel(model_assets* assets, - memory_arena* arena, - tinygltf::Model t_mdl, - const char* filename); -bool parseMeshNode(mesh* m, - texture_assets* textures, - memory_arena* arena, +bool parseMeshNode(Mesh* m, + MemoryArena* arena, const tinygltf::Node& node, const tinygltf::Model& t_mdl); +Model* getCachedModel(Assets* assets, u64 path_hash); +Model* loadModelFile(Assets* assets, const char* filename); +Texture* getCachedTexture(Assets* assets, u64 path_hash); + // interface -model_assets* -assetInitModelBlock(memory_arena* arena, uint asset_count) +Model* +getModelByPath(Assets* assets, const char* filepath) { - model_assets* assets = - (model_assets*) arenaAllocateBlock(arena, sizeof(model_assets)); - assets->models = - (model*) arenaAllocateBlock(arena, asset_count * sizeof(model)); - assets->max = asset_count; + Model* mdl = getCachedModel(assets, utilFNV64a_str(filepath)); + + if (!mdl) + mdl = loadModelFile(assets, filepath); - return assets; + return mdl; } -texture_assets* -assetInitTextureBlock(memory_arena* arena, uint asset_count) +Texture* +getTextureByPath(Assets* assets, const char* filepath) { - texture_assets* assets = - (texture_assets*) arenaAllocateBlock(arena, sizeof(texture_assets)); - assets->images = (util_image*) arenaAllocateBlock( - arena, asset_count * sizeof(util_image)); - assets->max = asset_count; + Texture* texture = + getCachedTexture(assets, utilFNV64a_str(filepath)); + + // NOTE: the texture should be loaded when the model is loaded, so it's an + // error if we don't find it in cache + if (!texture) { + LOG(Error) << "texture file, " << filepath << " not loaded\n"; + return nullptr; + } - return assets; + return texture; } -// FIXME: move to internal when finished -util_image* -copyDiffuseTexture(texture_assets* textures, - memory_arena* arena, - const tinygltf::Model& t_mdl) + +// internal + +Model* +initModel(Assets* assets, + tinygltf::Model t_mdl, + const char* filename) +{ + // TODO: re-alloc array when out of space + assert(assets->num_models < assets->max_models && assets->arena != nullptr); + Model* mdl = &assets->models[assets->num_models]; + assets->num_models++; + + uint buf_count = t_mdl.bufferViews.size(); + mdl->meshes = ARENA_ALLOC(assets->arena, Mesh, buf_count); + mdl->num_meshes = t_mdl.meshes.size(); + mdl->filepath = arenaCopyCStr(assets->arena, filename, MAX_PATH_SIZE); + mdl->filepath_hash = utilFNV64a_str(mdl->filepath); + + return mdl; +} + +Texture* +getFreeTexture(Assets* assets) +{ + if (assets->num_textures < assets->max_textures) + return &assets->textures[assets->num_textures++]; + + LOGF(Error, "no free textures\n"); + return nullptr; +} + +Texture* +copyDiffuseTexture(Assets* assets, const tinygltf::Model& t_mdl) { // NOTE: assuming material[0] since we're using pallete texture assert(t_mdl.materials.size() == 1 @@ -60,30 +93,29 @@ copyDiffuseTexture(texture_assets* textures, && t_mdl.images.size() == 1 && t_mdl.images[0].image.size() > 0); tinygltf::Image t_img = t_mdl.images[0]; + Texture* dtex = getFreeTexture(assets); - // TODO: re-alloc array when out of space - assert(textures->count < textures->max && arena != nullptr); - util_image* dtex = &textures->images[textures->count]; - textures->count++; + if (!dtex) { + LOG(Error) << "Error Loading diffuse texture\n"; + // TODO: reclaim arena memory + return nullptr; + } dtex->w = t_img.width; dtex->h = t_img.height; dtex->bits_per_channel = t_img.bits; dtex->num_channels = t_img.component; dtex->data_len = t_img.image.size(); - dtex->pixels = (uint8*) arenaAllocateBlock(arena, dtex->data_len); - std::strncpy(dtex->file_path, t_img.uri.c_str(), t_img.uri.size()); + dtex->pixels = ARENA_ALLOC(assets->arena, u8, dtex->data_len); + std::strncpy(dtex->file_path, t_img.uri.c_str(), sizeof(dtex->file_path)); dtex->filepath_hash = utilFNV64a_str(t_img.uri.c_str()); std::memcpy(dtex->pixels, t_img.image.data(), dtex->data_len); return dtex; } -model* -assetLoadFromFile(model_assets* assets, - texture_assets* textures, - memory_arena* arena, - const char* filename) +Model* +loadModelFile(Assets* assets, const char* filename) { tinygltf::Model t_mdl; tinygltf::TinyGLTF gltf_ctx; @@ -97,28 +129,25 @@ assetLoadFromFile(model_assets* assets, << " , msg: " << err << "\n"; return nullptr; } + #if 0 dumpNodes(t_mdl); #endif + // NOTE: assume we're working with a single buffer assert(t_mdl.buffers.size() == 1); - model* mdl = initModel(assets, arena, t_mdl, filename); - // FIXME: check for header overwriting here as seen in shader_testing - mdl->diffuse_texture = copyDiffuseTexture(textures, arena, t_mdl); -#if 1 - if (mdl->diffuse_texture == nullptr) { - LOG(Error) << "Error Loading diffuse texture\n"; - // TODO: reclaim arena memory - return nullptr; - } -#endif + Model* mdl = initModel(assets, t_mdl, filename); + mdl->diffuse_texture = copyDiffuseTexture(assets, t_mdl); + if (mdl->diffuse_texture == nullptr) return nullptr; uint mesh_idx = 0; for (tinygltf::Node node : t_mdl.nodes) { if (node.mesh >= 0) { if (!parseMeshNode(&mdl->meshes[mesh_idx++], - textures, arena, node, t_mdl)) + assets->arena, + node, + t_mdl)) { LOG(Error) << "Error parsing node\n"; return nullptr; @@ -129,47 +158,33 @@ assetLoadFromFile(model_assets* assets, return mdl; } -model* -assetGetCached(model_assets* assets, uint64_t path_hash) +Model* +getCachedModel(Assets* assets, u64 path_hash) { - for (uint i = 0; i < assets->count; i++) { + for (u32 i = 0; i < assets->num_models; i++) { if (assets->models[i].filepath_hash == path_hash) return &assets->models[i]; } - LOG(Debug) << "asset not cached: " << path_hash << "\n"; + LOG(Debug) << "asset not cached, hash: " << path_hash << "\n"; return nullptr; } - -// internal - -model* -initModel(model_assets* assets, - memory_arena* arena, - tinygltf::Model t_mdl, - const char* filename) +Texture* +getCachedTexture(Assets* assets, u64 path_hash) { - // TODO: re-alloc array when out of space - assert(assets->count < assets->max && arena != nullptr); - model* mdl = &assets->models[assets->count]; - assets->count++; - - uint buf_count = t_mdl.bufferViews.size(); - mdl->meshes = (mesh*) arenaAllocateBlock(arena, buf_count * sizeof(mesh)); - mdl->num_meshes = t_mdl.meshes.size(); - uint name_len = std::strlen(filename); - assert(name_len < MAX_PATH_SIZE); - mdl->filepath = (char*) arenaAllocateBlock(arena, name_len + 1); - std::strncpy(mdl->filepath, filename, name_len); - mdl->filepath_hash = utilFNV64a_str(mdl->filepath); + for (u32 i = 0; i < assets->num_textures; i++) { + if (assets->textures[i].filepath_hash == path_hash) + return &assets->textures[i]; + } - return mdl; + LOG(Debug) << "asset not cached, hash: " << path_hash << "\n"; + return nullptr; } bool copyBuffer(uint8_t*& buffer_ref, - memory_arena* arena, + MemoryArena* arena, int acc_idx, const tinygltf::Model& t_mdl) { @@ -191,7 +206,7 @@ copyBuffer(uint8_t*& buffer_ref, } assert(bv.byteStride == 0); - buffer_ref = (uint8_t*) arenaAllocateBlock(arena, bv.byteLength); + buffer_ref = ARENA_ALLOC(arena, u8, bv.byteLength); std::memcpy(buffer_ref, &t_buf.data[bv.byteOffset], bv.byteLength); return buffer_ref != nullptr; @@ -199,14 +214,13 @@ copyBuffer(uint8_t*& buffer_ref, // FIXME: need to implement tree structure for blender models to work properly glm::mat4* -parseNodeTransform(memory_arena* arena, const tinygltf::Node* node) +parseNodeTransform(MemoryArena* arena, const tinygltf::Node* node) { if (node->rotation.size() == 4 && node->scale.size() == 3 && node->translation.size() == 3) { - glm::mat4* xform = - (glm::mat4*) arenaAllocateBlock(arena, sizeof(glm::mat4)); + glm::mat4* xform = ARENA_ALLOC(arena, glm::mat4, 1); *xform = glm::mat4(1.0f); *xform = glm::rotate(*xform, (float) node->rotation[3], glm::vec3((float) node->rotation[0], @@ -232,9 +246,8 @@ parseNodeTransform(memory_arena* arena, const tinygltf::Node* node) } bool -parseMeshNode(mesh* m, - texture_assets* textures, - memory_arena* arena, +parseMeshNode(Mesh* m, + MemoryArena* arena, const tinygltf::Node& node, const tinygltf::Model& t_mdl) { @@ -254,11 +267,9 @@ parseMeshNode(mesh* m, const tinygltf::Accessor& index_acc = t_mdl.accessors[prim.indices]; m->num_vertices = vert_acc.count; m->num_indices = index_acc.count; - m->draw_mode = prim.mode; - m->usage = GL_STATIC_DRAW; // TODO: logic for updating dynamic meshes // FIXME: the node transforms from blender only work as part of a node tree #if 1 - m->xform = (glm::mat4*) arenaAllocateBlock(arena, sizeof(glm::mat4)); + m->xform = ARENA_ALLOC(arena, glm::mat4, 1); *m->xform = glm::mat4(1.0f); #else m->xform = parseNodeTransform(arena, &node); @@ -269,7 +280,7 @@ parseMeshNode(mesh* m, prim.attributes["POSITION"], t_mdl) && copyBuffer((uint8_t*&) m->normals, arena, prim.attributes["NORMAL"], t_mdl) - && copyBuffer((uint8_t*&) m->texture_coords, arena, + && copyBuffer((uint8_t*&) m->uvs, arena, prim.attributes["TEXCOORD_0"], t_mdl) && copyBuffer((uint8_t*&) m->indices, arena, prim.indices, t_mdl)) @@ -330,11 +341,11 @@ getDrawMode(int drawMode) } void -dumpBuffer(tinygltf::Model model, tinygltf::Accessor acc) +dumpBuffer(tinygltf::Model mdl, tinygltf::Accessor acc) { size_t bv_idx = acc.bufferView; - assert(bv_idx >= 0 && bv_idx < model.bufferViews.size()); - tinygltf::BufferView bv = model.bufferViews[bv_idx]; + assert(bv_idx >= 0 && bv_idx < mdl.bufferViews.size()); + tinygltf::BufferView bv = mdl.bufferViews[bv_idx]; LOG(Debug) << "-----------------------\n"; LOG(Debug) << "buf idx: " << bv_idx << "\n"; diff --git a/src/camera.cpp b/src/camera.cpp deleted file mode 100644 index 13ab4a5..0000000 --- a/src/camera.cpp +++ /dev/null @@ -1,204 +0,0 @@ - -#include - -#include - -#include "camera.h" - -// TODO: add these props to scene json -#define MOVE_SPEED 5.f -#define ROTATE_SPEED 0.005f -#define CAMERA_Z_CLAMP_ANGLE 85.f -#define FOV 60.f -#define NEAR_CLIP_PLANE 20.f - - -// forward declarations -inline glm::vec3 convertv3f(v3f v); - - -// interface - -void -cameraInitPerspective(camera* cam, - glm::vec3 position, - glm::vec3 target, - glm::vec3 world_up, - float aspect_ratio) -{ - assert(aspect_ratio > 0); - - cam->position = position; - cam->target = target; - cam->world_up = world_up; - cam->projection = glm::infinitePerspective(glm::radians(FOV), - aspect_ratio, - NEAR_CLIP_PLANE); - - cam->forward = glm::normalize(target - position); - cam->left = glm::normalize(glm::cross(cam->world_up, cam->forward)); - cam->up = glm::normalize(glm::cross(cam->forward, cam->left)); - - cam->hAngle = glm::atan(cam->forward.x, cam->forward.y); - // NOTE: get absolute value of relative axis for vAngle component - real32 len = glm::sqrt(glm::pow(cam->forward.y, 2) + - glm::pow(cam->forward.x, 2)); - cam->vAngle = glm::atan(cam->forward.z, len); - - cam->view = - glm::lookAt(cam->position, cam->position + cam->forward, cam->up); - cam->model = glm::mat4(1.0f); - cam->MVP = cam->projection * cam->view * cam->model; -} - -void -cameraInitOrthographic(/*camera& cam, */) -{ -#if 0 - // left, right, bottom, top, zNear, zFar - cam.projection = glm::ortho(0.f, 1280.0f, 0.f, 720.0f, 0.1f, 100.0f); - cam.view = glm::lookAt( - glm::vec3(0.0f, 0.0f, 1.0f), // camera position - glm::vec3(0.0f, 0.0f, 0.0f), // look at position - glm::vec3(0,1,0) // "up" vector - ); - - cam.model = glm::mat4(1.0f); - cam.MVP = cam.projection * cam.view * cam.model; -#endif -} - -v2f -cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height) -{ - // NOTE: using depth buffer may not be as accurate as doing ray-cast - GLfloat depth; - glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); - glm::vec4 viewport = glm::vec4(0, 0, vp_width, vp_height); - glm::vec3 wincoord = glm::vec3(x, y, depth); - glm::vec3 vU = glm::unProject(wincoord, cam.view, cam.projection, viewport); - v2f v(vU.x, vU.y); - - return v; -} - -v3f -cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims) -{ - // NOTE: http://antongerdelan.net/opengl/raycasting.html - float x = 2.f * vp_coords.x / vp_dims.x - 1.f; - float y = 2.f * vp_coords.y / vp_dims.y - 1.f; - glm::vec4 ray_clip = glm::vec4(x, y, -1.f, 1.f); - glm::vec4 ray_eye = glm::inverse(cam.projection) * ray_clip; - ray_eye = glm::vec4(ray_eye.x, ray_eye.y, -1.f, 0); // NOTE: reset as ray - glm::vec4 ray_world = glm::normalize(glm::inverse(cam.view) * ray_eye); - - return v3f(ray_world.x, ray_world.y, ray_world.z); -} - -bool -cameraIntersectPlane(camera& cam, v3f ray, v3f plane_origin, v3f plane_normal, v3f& intersection) -{ - // NOTE: https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection - glm::vec3 c_o = cam.position; - glm::vec3 r = convertv3f(ray); - glm::vec3 p_o = convertv3f(plane_origin); - glm::vec3 p_n = convertv3f(plane_normal); - float divisor = glm::dot(r, p_n); - - if (divisor <= 0.000001f && divisor >= -0.000001f) // NOTE: ray and plane are co-planar - return false; - - float distance = glm::dot((p_o - c_o), p_n) / divisor; - glm::vec3 xsect = c_o + (r * distance); - intersection = v3f(xsect.x, xsect.y, xsect.z); - - return true; -} - -void -cameraMove(camera& cam, bool up, bool left, bool down, bool right, bool forward, bool backward) -{ - if (!up && !left && !down && !right && !forward && !backward) - return; - - glm::vec3 f = cam.forward; - glm::vec3 u = cam.up; - glm::vec3 old = cam.position; - glm::vec3 &p = cam.position; - glm::vec3 v(0.f); // normalized direction - - // TODO: still seems like we're adding magnitude when moving in 2 directions -#if 0 - if (forward) v = glm::normalize(v + f); - if (backward) v = glm::normalize(v - f); - if (up) v = glm::normalize(v + u); - if (down) v = glm::normalize(v - u); - if (left) v -= glm::normalize(glm::cross(f, u)); - if (right) v -= glm::normalize(glm::cross(u, f)); -#else - if (forward) v += f; - if (backward) v -= f; - if (up) v += u; - if (down) v -= u; - if (left) v -= glm::cross(f, u); - if (right) v -= glm::cross(u, f); -#endif - - p += (v * MOVE_SPEED); - glm::vec3 diff = old - p; - cam.view = glm::translate(cam.view, diff); - cam.MVP = cam.projection * cam.view * cam.model; -} - -void -cameraRotate(camera& cam, int32 xrel, int32 yrel) -{ - float &h = cam.hAngle; - float &v = cam.vAngle; - h += ROTATE_SPEED * xrel; - v -= ROTATE_SPEED * yrel; - - // clamp vAngle to prevent gimbal lock - float a = glm::radians(CAMERA_Z_CLAMP_ANGLE); - if (v < (-1 * a)) v = (-1 * a); - if (v > a) v = a; - - cam.forward = glm::vec3( - glm::cos(v) * glm::sin(h), - glm::cos(v) * glm::cos(h), - glm::sin(v) - ); - - glm::normalize(cam.forward); - cam.left = glm::normalize(glm::cross(cam.forward, cam.world_up)); - cam.up = glm::normalize(glm::cross(cam.left, cam.forward)); - - cam.view = glm::lookAt(cam.position, cam.position + cam.forward, cam.up); - cam.MVP = cam.projection * cam.view * cam.model; -} - -void -cameraRoll(camera& cam, bool CW, bool CCW) -{ - if ((!CW && !CCW) || (CW && CCW)) - return; - - float a = 0.005f; - if (CW) a *= 1; - if (CCW) a *= -1; - glm::mat4 m = glm::rotate(glm::mat4(1.f), a, cam.forward); - glm::vec4 v(cam.up.x, cam.up.y, cam.up.z, 0); - v = v * m; - cam.up = glm::vec3(v.x, v.y, v.z); - cam.view *= m; - cam.MVP = cam.projection * cam.view * cam.model; -} - -// internal - -inline glm::vec3 -convertv3f(v3f v) -{ - return glm::vec3(v.x, v.y, v.z); -} diff --git a/src/default_shaders.cpp b/src/default_shaders.cpp deleted file mode 100644 index 063f5dd..0000000 --- a/src/default_shaders.cpp +++ /dev/null @@ -1,162 +0,0 @@ - - -// NOTE: default shader - -const char* DEFAULT_VERTEX_SHADER = R"VS( -#version 330 core - -layout (location = 0) in vec3 vertexPosition_modelspace; -layout (location = 1) in vec3 normal; -layout (location = 2) in vec3 texCoord; - -out vec3 fragVertex; -out vec3 fragNormal; -out vec2 fragUV; - -uniform mat4 world_transform; -uniform mat4 model; -uniform mat4 view; -uniform mat4 projection; - - -void main() -{ - fragNormal = vec4(world_transform * vec4(normal, 1)).xyz; - fragVertex = vertexPosition_modelspace; - fragUV = texCoord.st; - gl_Position = projection * view * - world_transform * model * vec4(vertexPosition_modelspace, 1); -} -)VS"; - -// TODO: there's a bug here with the array size of 'lights' -// with intel opengl, size can be >1000 -// but with nvidea opengl size of >200 gives the following error: -// error C6020: Constant register limit exceeded at sampler; -// more than 1024 registers needed to compile program -const char* DEFAULT_FRAGMENT_SHADER = R"FS( -#version 330 core - -in vec3 fragVertex; -in vec3 fragNormal; -in vec2 fragUV; - -out vec4 color; - -uniform mat4 model; -uniform mat3 normal_matrix; -uniform sampler2D sampler; -uniform uint num_lights = 0u; - -struct point_light { - uint light_ID; - vec3 position; - vec3 color; - float intensity; -}; - -uniform point_light lights[200]; - - -void main() -{ - vec3 normal = normalize(normal_matrix * fragNormal); - vec3 fragPosition = vec3(model * vec4(fragVertex, 1)); - float totalBrightness = 0; - - for (uint i = 0u; i < num_lights; i++) { - vec3 surfaceToLight = lights[i].position - fragPosition; - //float brightness = dot(normal, surfaceToLight) / (length(surfaceToLight) * length(normal)); - float brightness = dot(normal, surfaceToLight) / length(surfaceToLight); - totalBrightness += brightness; - } - - color = clamp(totalBrightness, 0, 1) * texture(sampler, fragUV.st); -} -)FS"; - - -// NOTE: debug shader - -const char* DEBUG_VERTEX_SHADER = R"DVS( -#version 330 core - -layout (location = 0) in vec3 vertexPosition_modelspace; -layout (location = 1) in vec3 normal; -layout (location = 2) in vec3 texCoord; - -out vec3 fragVertex; -out vec3 fragNormal; - -uniform mat4 world_transform; -uniform mat4 model; -uniform mat4 view; -uniform mat4 projection; -uniform mat3 normal_matrix; - - -void main() -{ - fragNormal = vec4(world_transform * vec4(normal, 1)).xyz; - fragVertex = vertexPosition_modelspace; - gl_Position = projection * view * - world_transform * model * vec4(vertexPosition_modelspace, 1); -} -)DVS"; - -const char* DEBUG_FRAGMENT_SHADER = R"DFS( -#version 330 core - -in vec3 fragVertex; -in vec3 fragNormal; - -out vec4 color; - -uniform mat4 model; -uniform mat3 normal_matrix; - - -void main() -{ - color = vec4(normalize( - vec3(fragNormal.x, fragNormal.y, -1 * fragNormal.z) - ), 1.0); -} -)DFS"; - - -// NOTE: simple shader - -const char* SIMPLE_VERTEX_SHADER = R"SVS( -#version 330 core - -layout (location = 0) in vec3 position; -layout (location = 1) in vec3 color; - -out vec3 frag_color; - -uniform mat4 world_transform; -uniform mat4 MVP; - - -void main() -{ - frag_color = color; - gl_Position = MVP * world_transform * vec4(position, 1); -} -)SVS"; - -const char* SIMPLE_FRAGMENT_SHADER = R"SFS( -#version 330 core - -in vec3 frag_color; - -out vec4 color; - - -void main() -{ - color = vec4(frag_color.rgb, 1); -} -)SFS"; - diff --git a/src/dumbLog.cpp b/src/dumbLog.cpp index 13b7f14..a31b9be 100644 --- a/src/dumbLog.cpp +++ b/src/dumbLog.cpp @@ -3,6 +3,8 @@ #include #include "dumbLog.h" +#include "types.h" + void dumbLog::setOutputStream(std::ostream* out) { @@ -16,6 +18,7 @@ dumbLog::logLevelToString(log_level level) case log_level::Error: return "Error"; case log_level::Warning: return "Warning"; case log_level::Info: return "Info"; + case log_level::Debug: return "Debug"; default: return "Potato"; } }; @@ -32,7 +35,36 @@ int dumbLog::getCurrentMS() { auto now = std::chrono::system_clock::now(); - long long total_ms = std::chrono::duration_cast(now.time_since_epoch()).count(); + u64 total_ms = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); return int(total_ms % 1000); } + +#include +#include + + +void +dumbLogF(log_level l, const char* func, const char* fmt, ...) +{ + const char* level = logger.logLevelToString(l); + char time_str[100]; + timespec ts; + timespec_get(&ts, TIME_UTC); + i64 ms = ts.tv_nsec / 1000000; + + if (strftime(time_str, sizeof(time_str), "%T", localtime(&ts.tv_sec))) { + // NOTE: print prefix, "H:M:S.ms, log_level, function(), " + printf("%s.%03ld [%s] %s(), ", time_str, ms, level, func); + + // NOTE: append user args + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } else { + printf("%s(), error getting time\n", __FUNCTION__); + } +} diff --git a/src/entity.cpp b/src/entity.cpp index 081884c..7c8f7f5 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -1,76 +1,68 @@ -#include - -#include +#include #include "dumbLog.h" #include "entity.h" -#include "util.h" - - -// forward declarations -void initDefaults(entity& e); - - -// interface bool -entInitModel(entity* e, model* mdl) +initEntity(Entity* e, + GLContext* gl_ctx, + MemoryArena* arena, + Model* mdl, + u32 num_attrib_mappings, + GLBufferToAttribMapping* attrib_mappings, + const char* name) { - e->render_objs = roInitModel(mdl); - e->world_transform = glm::mat4(1); - e->model_id = mdl->filepath_hash; + e->num_meshes = mdl->num_meshes; + e->meshes = ARENA_ALLOC(arena, GLMesh, e->num_meshes); + e->model_xform = ARENA_ALLOC(arena, glm::mat4, 1); + *e->model_xform = glm::mat4(1.f); + e->name = arenaCopyCStr(arena, name); - if (e->render_objs == nullptr) { - entFree(e); - return false; - } + if (mdl->diffuse_texture) { + e->diffuse_texture = getGLTexture(gl_ctx, mdl->diffuse_texture); - return true; -} - -void -entFree(entity* e) -{ - roFree(e->render_objs); - e->render_objs = nullptr; -} + if (!e->diffuse_texture) + return false; + } -void -entSetWorldPosition(entity& e, glm::vec3 v) -{ - e.world_transform[3][0] = v.x; - e.world_transform[3][1] = v.y; - e.world_transform[3][2] = v.z; -} + for (u32 i = 0; i< e->num_meshes; i++) { + GLMesh* glm = &e->meshes[i]; + *glm = loadGLMesh(arena, + mdl->meshes[i], + GL_TRIANGLES, + e->diffuse_texture, + num_attrib_mappings, + attrib_mappings); + + if (glm->vao_id == 0) { + LOGF(Error, "error initializing entity\n"); + return false; + } + } -void -entTranslate(entity& e, glm::vec3 v) -{ - e.world_transform = glm::translate(e.world_transform, v); + return true; } void -entScale(entity& e, glm::vec3 v) +setEntityPosition(Entity* e, glm::vec3 pos) { - e.world_transform = glm::scale(e.world_transform, v); + (*e->model_xform)[3][0] = pos.x; + (*e->model_xform)[3][1] = pos.y; + (*e->model_xform)[3][2] = pos.z; } void -entRotate(entity* e, float angle, glm::vec3 axis) +rotateEntity(Entity* e, glm::vec3 axis, float radians) { - e->world_transform = glm::rotate(e->world_transform, angle, axis); + *e->model_xform = glm::rotate(*e->model_xform, radians, axis); } - -// internal - void -initDefaults(entity& e) +scaleEntity(Entity* e, float scale) { - e.world_transform = glm::mat4(1.0); - entScale(e, glm::vec3(1.0)); - entSetWorldPosition(e, glm::vec3(0, 0, 0)); + *e->model_xform = + glm::scale(*e->model_xform, glm::vec3(scale, scale, scale)); } diff --git a/src/input.cpp b/src/input.cpp deleted file mode 100644 index 627360d..0000000 --- a/src/input.cpp +++ /dev/null @@ -1,42 +0,0 @@ - -#include "input.h" - - -void -inputProcessEvent(input_state* is, SDL_Event& e) -{ - switch (e.type) { - case SDL_QUIT: - is->window_closed = true; - break; - case SDL_KEYDOWN: - switch (e.key.keysym.sym) { - case SDLK_ESCAPE: is->escape = true; break; - case SDLK_LEFT: is->left = true; break; - case SDLK_RIGHT: is->right = true; break; - case SDLK_UP: is->up = true; break; - case SDLK_DOWN: is->down = true; break; - } - break; - case SDL_KEYUP: - switch (e.key.keysym.sym) { - case SDLK_ESCAPE: is->escape = false; break; - case SDLK_LEFT: is->left = false; break; - case SDLK_RIGHT: is->right = false; break; - case SDLK_UP: is->up = false; break; - case SDLK_DOWN: is->down = false; break; - } - break; - default: break; - } -} - -void -inputProcessEvents(input_state* is) -{ - SDL_Event e; - - while (SDL_PollEvent(&e)) - inputProcessEvent(is, e); -} - diff --git a/src/libs.cpp b/src/libs.cpp deleted file mode 100644 index e04c0b5..0000000 --- a/src/libs.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -// NOTE: put all the header-only libs in a separate compilation unit to save on -// compile times - -#define TINYGLTF_IMPLEMENTATION -#include "tiny_gltf.h" - -#define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image.h" -#include "stb_image_write.h" - diff --git a/src/lights.cpp b/src/lights.cpp deleted file mode 100644 index 340689a..0000000 --- a/src/lights.cpp +++ /dev/null @@ -1,55 +0,0 @@ - -#include // 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; -} - diff --git a/src/mesh.cpp b/src/mesh.cpp deleted file mode 100644 index 6020251..0000000 --- a/src/mesh.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include - -#if 0 -#include -#include -#include -#endif - -#include -#include -#include - -#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 - diff --git a/src/render_object.cpp b/src/render_object.cpp deleted file mode 100644 index d67a1c5..0000000 --- a/src/render_object.cpp +++ /dev/null @@ -1,246 +0,0 @@ - -#include - -#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); -} - diff --git a/src/renderer.cpp b/src/renderer.cpp deleted file mode 100644 index 2d28e9b..0000000 --- a/src/renderer.cpp +++ /dev/null @@ -1,369 +0,0 @@ - -#if defined (_WIN32) - #include -#else - #include -#endif -#include -#include -#include - -#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; -} diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 0000000..dbfabfc --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,751 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#include "asset.h" +#include "dumbLog.h" +#include "dummy_shader.h" +#define GL_DEBUG_IMPLEMENTATION +#include "GLDebug.h" +#include "shader.h" +#include "util.h" + + +// NOTE: forward declarations + +bool parseShader(MemoryArena* arena, GLContext* gl_ctx, ShaderProgram* s); + +void loadDummyShader(); + +void initCTXSizes(GLContext* gl_ctx); + +bool compileAndLinkShader(ShaderProgram* shader, + const char* vert_src, + const char* frag_src, + GLuint& vs_id, + GLuint& fs_id); + +u32 getGLTypeSize(GLenum e); + +GLTexture* getFreeGLTexture(GLContext* gl_ctx); + +bool loadGLTexture(Texture* image, GLuint& tex_id); + +void* getMeshData(const Mesh& m, const GLBufferToAttribMapping& mapping); + + +// NOTE: interface + +GLContext* +initGLContext(MemoryArena* arena, + u32 max_shaders, + u32 max_textures, + u32 max_ubos) +{ + GLContext* gl_ctx = ARENA_ALLOC(arena, GLContext, 1); + gl_ctx->max_shaders = max_shaders; + gl_ctx->shaders = ARENA_ALLOC(arena, ShaderProgram, max_shaders); + gl_ctx->max_ubos = max_ubos; + gl_ctx->uniform_buffers = ARENA_ALLOC(arena, GLBuffer, max_ubos); + + // NOTE: initialize GLBuffer struct members to sane defaults + for (u32 i = 0; i < max_ubos; i++) { + GLBuffer& buf = gl_ctx->uniform_buffers[i]; + buf.location = -1; + buf.binding_idx = -1; + } + + gl_ctx->max_textures = max_textures; + gl_ctx->textures = ARENA_ALLOC(arena, GLTexture, max_textures); + + // NOTE: load a dummy shader to avoid chicken and egg problem where we need + // GLContext info before we can parse a shader, which needs GLContext info + loadDummyShader(); + initCTXSizes(gl_ctx); + + return gl_ctx; +} + +bool +addShaderProgram(MemoryArena* arena, + GLContext* gl_ctx, + const char* vs, + const char* fs, + const char* name) +{ + LOGF(Info, "loading shader, %s\n", name); + const u32 max_len = 256; + char input_str[max_len]; + snprintf(input_str, max_len, "%s%s", vs, fs); + u64 hash = utilFNV64a_str(input_str); + + if (getShaderByHash(gl_ctx, hash)) { + LOGF(Error, "shader is already loaded\n"); + return false; + } + + ShaderProgram* s = getFreeShader(gl_ctx); + + if (s) { + s->name = arenaCopyCStr(arena, name); + s->hash = hash; + + size_t vs_size, fs_size; + const char* v_str = (const char*) SDL_LoadFile(vs, &vs_size); + const char* f_str = (const char*) SDL_LoadFile(fs, &fs_size); + GLuint vs_id, fs_id; + + if (compileAndLinkShader(s, v_str, f_str, vs_id, fs_id)) { + glDetachShader(s->prog_id, vs_id); + glDetachShader(s->prog_id, fs_id); + glDeleteShader(vs_id); + glDeleteShader(fs_id); + + return parseShader(arena, gl_ctx, s); + } + + LOGF(Error, "Error linking shader\n"); + return false; + } + + LOGF(Error, "error loading shader\n"); + return false; +} + +ShaderProgram* +getFreeShader(GLContext* gl_ctx) +{ + if (gl_ctx->num_shaders >= gl_ctx->max_shaders) { + LOGF(Error, "GLContext->shaders full\n"); + return nullptr; + } + + ShaderProgram* s = &gl_ctx->shaders[gl_ctx->num_shaders++]; + return s; +} + +ShaderProgram* +getShaderByHash(GLContext* gl_ctx, u64 hash) +{ + for (u32 i; i < gl_ctx->num_shaders; i++) { + if (gl_ctx->shaders[i].hash == hash) + return &gl_ctx->shaders[i]; + } + + return nullptr; +} + +ShaderProgram* +getShaderByName(const char* name, GLContext* gl_ctx) +{ + for (u32 i = 0; i < gl_ctx->num_shaders; i++) { + if (utilCStrMatch(name, gl_ctx->shaders[i].name)) + return &gl_ctx->shaders[i]; + } + + LOGF(Error, "shader not found, %s\n", name); + return nullptr; +} + +ShaderProgram* +getShaderByID(GLContext* gl_ctx, GLuint prog_id) +{ + for (u32 i = 0; i < gl_ctx->num_shaders; i++) { + if (gl_ctx->shaders[i].prog_id) + return &gl_ctx->shaders[i]; + } + + LOGF(Error, "shader not found, %d\n", prog_id); + return nullptr; +} + +GLBuffer* +getFreeUBO(GLContext* gl_ctx) +{ + if (gl_ctx->num_ubos < gl_ctx->max_ubos) + return &gl_ctx->uniform_buffers[gl_ctx->num_ubos++]; + + LOGF(Error, "no free Uniform Buffer Objects\n"); + return nullptr; +} + +GLBuffer* +getUBOByName(GLContext* gl_ctx, const char* name) +{ + GLBuffer* ubo_out = nullptr; + + for (u32 i = 0; i < gl_ctx->num_ubos; i++) { + GLBuffer* buf = &gl_ctx->uniform_buffers[i]; + + if (utilCStrMatch(name, buf->name)) + ubo_out = buf; + } + + if (ubo_out == nullptr) + LOGF(Error, "GLBuffer, \"%s\", not found\n", name); + + return ubo_out; +} + +GLTexture* +getGLTexture(GLContext* gl_ctx, Texture* diffuse_img) +{ + u64 fp_hash = utilFNV64a_str(diffuse_img->file_path); + + for (u32 i = 0; i < gl_ctx->num_textures; i++) { + GLTexture* glt = &gl_ctx->textures[i]; + + if (glt->filepath_hash == fp_hash) + return glt; + } + + GLTexture* glt = getFreeGLTexture(gl_ctx); + if (!glt) return nullptr; + + glt->pixel_format = (diffuse_img->num_channels == 3) ? GL_RGB : GL_RGBA; + glt->width = diffuse_img->w; + glt->height = diffuse_img->h; + glt->filepath_hash = diffuse_img->filepath_hash; + + if (loadGLTexture(diffuse_img, glt->id)) + return glt; + + LOGF(Error, "Error, unable to load texture\n"); + return nullptr; +} + +void +updateGLBuffer(GLBuffer* gl_buf, void* data) +{ + assert(gl_buf && data); + glBindBuffer(gl_buf->target, gl_buf->id); + glBufferSubData(gl_buf->target, 0, gl_buf->data_size, data); +} + +void +renderVAO(GLMesh* glmesh, + glm::mat4* node_xform, + ShaderProgram* shader, + GLTexture* gl_tex) +{ + glUseProgram(shader->prog_id); + glBindVertexArray(glmesh->vao_id); + + for (u32 i = 0; i < shader->num_uniforms; i++) { + const GLUniform& uniform = shader->uniforms[i]; + + if (uniform.uniform_type == UNIFORM_NODE_XFORM) { + glUniformMatrix4fv(uniform.location, 1, GL_FALSE, + (float*) node_xform); + } + + else if (glmesh->has_texture && + uniform.uniform_type == UNIFORM_SAMPLER) + { + glBindTexture(GL_TEXTURE_2D, gl_tex->id); + glUniform1i(uniform.location, 0); + } + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glmesh->element_buf->id); + glDrawElements( + glmesh->draw_mode, glmesh->num_indices, GL_UNSIGNED_SHORT, 0); + glBindVertexArray(0); +} + +void +initTransforms(MemoryArena* arena, + Transforms* xforms, + GLBuffer* xform_ubo, + GLContext* gl_ctx, + float fov, + float near_clip_plane, + float aspect_ratio) +{ + xforms->proj_xform = glm::infinitePerspective( + glm::radians(fov), aspect_ratio, near_clip_plane); + + glGenBuffers(1, &xform_ubo->id); + xform_ubo->target = GL_UNIFORM_BUFFER; + xform_ubo->data_type = GL_FLOAT; + xform_ubo->name = arenaCopyCStr(arena, "matrices"); + xform_ubo->data_size = sizeof(*xforms); + + glBindBuffer(xform_ubo->target, xform_ubo->id); + glBufferData(xform_ubo->target, xform_ubo->data_size, xforms, + GL_DYNAMIC_DRAW); + + // NOTE: bindbufferbase + xform_ubo->binding_idx = gl_ctx->binding_count++; + glBindBufferBase(xform_ubo->target, xform_ubo->binding_idx, xform_ubo->id); + glBindBuffer(xform_ubo->target, 0); +} + +GLVertexAttrib* +getVertexAttribByName(ShaderProgram* shader, const char* name) +{ + for (u32 i = 0; i < shader->num_vertex_attribs; i++) { + if (strncmp(shader->vertex_attribs[i].name, name, 256) == 0) + return &shader->vertex_attribs[i]; + } + + LOGF(Debug, "attribute: %s, not found on shader: %s\n", name, shader->name); + return nullptr; +} + +GLMesh +initGLMesh(MemoryArena* arena, + const Mesh& m, + u32 num_mappings, + GLenum draw_mode) +{ + GLMesh glm = {0}; + glm.num_indices = m.num_indices; + glm.draw_mode = draw_mode; + glm.usage = GL_STATIC_DRAW; // TODO: logic for updating dynamic meshes + glm.num_vertex_attrib_buffers = num_mappings; + glm .vertex_attrib_buffers = ARENA_ALLOC(arena, GLBuffer, num_mappings); + glm.element_buf = ARENA_ALLOC(arena, GLBuffer, 1); + glm.node_xform = ARENA_ALLOC(arena, glm::mat4, 1); + *glm.node_xform = glm::mat4(1); + + return glm; +} + +void +initGLAttribBuffer(GLBuffer* buf, GLenum target, GLVertexAttrib* attrib) +{ + glGenBuffers(1, &buf->id); + buf->target = target; + buf->data_type = attrib->data_type; + buf->location = attrib->location; + buf->name = utilAllocateCStr(attrib->name); +} + +// TODO: might as well pass in pointer to GLMesh, since that's how we're +// going to use this, can void copying GLMesh twice that way +GLMesh +loadGLMesh(MemoryArena* arena, + const Mesh& m, + GLenum draw_mode, + GLTexture* diffuse_texture, + u32 num_mappings, + GLBufferToAttribMapping mappings[]) +{ + GLMesh glm = initGLMesh(arena, m, num_mappings, draw_mode); + + if (diffuse_texture) { + glm.has_texture = true; + glm.tex_id = diffuse_texture->id; + } + + glGenVertexArrays(1, &glm.vao_id); + glBindVertexArray(glm.vao_id); + + for (u32 i = 0; i < num_mappings; i++) { + GLBuffer& buf = glm.vertex_attrib_buffers[i]; + GLVertexAttrib* attrib = mappings[i].attrib; + attrib->buf_type = mappings[i].buf_type; + u32 type_size = getGLTypeSize(attrib->data_type); + assert(type_size > 0); + buf.data_size = m.num_vertices * type_size; + + void* mesh_buf_data = getMeshData(m, mappings[i]); + assert(mesh_buf_data); + initGLAttribBuffer(&buf, GL_ARRAY_BUFFER, attrib); + glBindBuffer(buf.target, buf.id); + glBufferData(buf.target, + buf.data_size, + mesh_buf_data, + glm.usage); + glVertexAttribPointer(attrib->location, attrib->num_components, + attrib->component_type, GL_FALSE, 0, 0); + glEnableVertexAttribArray(attrib->location); + } + + glGenBuffers(1, &glm.element_buf->id); + glm.element_buf->target = GL_ELEMENT_ARRAY_BUFFER; + glm.element_buf->data_type = GL_UNSIGNED_SHORT; + glm.element_buf->data_size = m.num_indices * sizeof(u16); + glBindBuffer(glm.element_buf->target, glm.element_buf->id); + glBufferData(glm.element_buf->target, + glm.element_buf->data_size, + m.indices, + glm.usage); + + // TODO: many of these GL functions can set an error state + // TODO: return error status + glBindVertexArray(0); + + return glm; +} + + +// NOTE: internal + +void* +getMeshData(const Mesh& m, const GLBufferToAttribMapping& mapping) +{ + switch (mapping.buf_type) { + case VERTEX: return m.vertices; + case NORMAL: return m.normals; + case UV: return m.uvs; + case COLOR: return m.colors; + default: return nullptr; + } +} + +GLTexture* +getFreeGLTexture(GLContext* gl_ctx) +{ + if (gl_ctx->num_textures < gl_ctx->max_textures) + return &gl_ctx->textures[gl_ctx->num_textures++]; + + LOGF(Error, "no free textures\n"); + return nullptr; +} + +bool +loadGLTexture(Texture* image, GLuint& tex_id) +{ + glGenTextures(1, &tex_id); + glBindTexture(GL_TEXTURE_2D, tex_id); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + GLenum pixel_format = (image->num_channels == 3) ? GL_RGB : GL_RGBA; + glTexImage2D(GL_TEXTURE_2D, 0, pixel_format, image->w, image->h, 0, + pixel_format, GL_UNSIGNED_BYTE, image->pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + return (glGetError() == GL_NO_ERROR); +} + +bool +compileAndLinkShader(ShaderProgram* shader, + const char* vert_src, + const char* frag_src, + GLuint& vs_id, + GLuint& fs_id) +{ + if (vert_src && frag_src && strlen(vert_src) > 0 && strlen(frag_src) > 0) { + vs_id = glCreateShader(GL_VERTEX_SHADER); + fs_id = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(vs_id, 1, &vert_src, NULL); + glShaderSource(fs_id, 1, &frag_src, NULL); + + glCompileShader(vs_id); + assert(glGetError() == GL_NO_ERROR); + glCompileShader(fs_id); + assert(glGetError() == GL_NO_ERROR); + + shader->prog_id = glCreateProgram(); + glAttachShader(shader->prog_id, vs_id); + glAttachShader(shader->prog_id, fs_id); + + glLinkProgram(shader->prog_id); + GLint is_linked = 0; + glGetProgramiv(shader->prog_id, GL_LINK_STATUS, &is_linked); + + // NOTE: log shader linking errors + if (is_linked != GL_TRUE) { + const u32 max_len = 512; + char err_str[max_len]; + i32 write_len; + glGetProgramInfoLog( + shader->prog_id, max_len, &write_len, &err_str[0]); + LOGF(Error, "Info Log: %s\n", err_str); + glDeleteProgram(shader->prog_id); + } + + return (is_linked == GL_TRUE); + } + + LOGF(Error, "empty shader source\n"); + return false; +} + +void +loadDummyShader() +{ + GLuint vs_id = 0, fs_id = 0; + ShaderProgram temp_shader = {0}; + bool ret = compileAndLinkShader(&temp_shader, + DUMMY_VERTEX_SHADER, + DUMMY_FRAGMENT_SHADER, + vs_id, + fs_id); + assert(ret); + glDeleteProgram(temp_shader.prog_id); +} + +u32 +getGLTypeSize(GLenum e) +{ + switch (e) { + case GL_FLOAT_VEC2: return 2 * sizeof(GLfloat); + case GL_FLOAT_VEC3: return 3 * sizeof(GLfloat); + case GL_FLOAT_VEC4: return 4 * sizeof(GLfloat); + case GL_FLOAT_MAT4: return 16 * sizeof(GLfloat); + default: + LOGF(Error, "unknown GLenum\n"); + return 0; + } +} + +// NOTE: returns sizes based on GLSL layout std140 +// https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout +u32 +getGLTypeSizeStd140(GLenum e) +{ + switch (e) { + case GL_FLOAT_VEC3: return 4 * sizeof(GLfloat); + case GL_FLOAT_VEC4: return 4 * sizeof(GLfloat); + case GL_FLOAT_MAT4: return 16 * sizeof(GLfloat); + default: + LOGF(Error, "unknown GLenum\n"); + return 0; + } +} + +UniformType +getUniformType(const char* name) +{ + if (utilCStrMatch(name, "sampler")) + return UNIFORM_SAMPLER; + else if (utilCStrMatch(name, "node_xform")) + return UNIFORM_NODE_XFORM; + else if (utilCStrMatch(name, "normal_xform")) + return UNIFORM_NORMAL_XFORM; + else if (utilCStrMatch(name, "view_xform")) + return UNIFORM_VIEW_XFORM; + else if (utilCStrMatch(name, "proj_xform")) + return UNIFORM_PROJECTION_XFORM; + else if (utilCStrMatch(name, "matrices")) + return UNIFORM_BLOCK_XFORMS; + else if (utilCStrMatch(name, "lights")) + return UNIFORM_BLOCK_LIGHTS; + else + return UNIFORM_UNKNOWN; +} + +const GLUniform +parseUniform(MemoryArena* arena, ShaderProgram* s, u32 uniform_idx) +{ + GLUniform unif = {0}; + GLchar unif_name[256] = {0}; + GLsizei name_len = 0; + unif.idx = uniform_idx; + + glGetActiveUniform(s->prog_id, + uniform_idx, + sizeof(unif_name), + &name_len, + &unif.num_elements, + &unif.gl_type, + unif_name); + + glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_BLOCK_INDEX, + &unif.block_idx); + glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_ARRAY_STRIDE, + &unif.array_stride); + glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_OFFSET, + &unif.uniform_offset); + + unif.name = arenaCopyCStr(arena, unif_name); + unif.uniform_type = getUniformType(unif.name); + unif.location = glGetUniformLocation(s->prog_id, unif.name); + + return unif; +} + +bool +parseShaderUniforms(MemoryArena* arena, ShaderProgram* s, GLContext* gl_ctx) +{ + // NOTE: only add uniforms in the default block to the base uniform array + GLint num_uniforms_total = 0; + glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORMS, &num_uniforms_total); + GLint indices[num_uniforms_total]; + + for (u32 i = 0; i < (u32) num_uniforms_total; i++) { + GLint block_idx = 0; + glGetActiveUniformsiv(s->prog_id, 1, &i, + GL_UNIFORM_BLOCK_INDEX, &block_idx); + + if (block_idx == -1) { + indices[s->num_uniforms] = i; + s->num_uniforms++; + } + } + + s->uniforms = ARENA_ALLOC(arena, GLUniform, s->num_uniforms); + + for (u32 i = 0; i < s->num_uniforms; i++) { + const GLUniform unif = parseUniform(arena, s, indices[i]); + std::memcpy(&s->uniforms[i], &unif, sizeof(unif)); + } + + return true; +} + +void +initCTXSizes(GLContext* gl_ctx) +{ + // NOTE: see https://docs.gl/gl3/glGet for other useful context info + if (gl_ctx->max_binding_points == 0) { + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, + &gl_ctx->max_binding_points); + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &gl_ctx->max_vertex_blocks); + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, + &gl_ctx->max_fragment_blocks); + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gl_ctx->max_ublock_size); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_ctx->max_vertex_attribs); + +#if 1 + LOGF(Debug, "context size info set\n"); + LOGF(Debug, "GL_MAX_UNIFORM_BUFFER_BINDINGS: %d\n", + gl_ctx->max_binding_points); + LOGF(Debug, "GL_MAX_VERTEX_UNIFORM_BLOCKS: %d\n", + gl_ctx->max_vertex_blocks); + LOGF(Debug, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS: %d\n", + gl_ctx->max_fragment_blocks); + LOGF(Debug, "GL_MAX_UNIFORM_BLOCK_SIZE: %d\n", + gl_ctx->max_ublock_size); + LOGF(Debug, "GL_MAX_VERTEX_ATTRIBS: %d\n", gl_ctx->max_vertex_attribs); +#endif + } +} + +i32 +ctxGetUniformBlockBinding(GLContext* gl_ctx, const char* name) +{ + for (u32 i = 0; i < gl_ctx->num_ubos; i++) { + GLBuffer& ubo = gl_ctx->uniform_buffers[i]; + + if (std::strstr(ubo.name, name)) + return ubo.binding_idx; + } + + LOGF(Error, "no buffer found with name: %s\n", name); + return -1; +} + +bool +parseUniformBlocks(MemoryArena* arena, ShaderProgram* s, GLContext* gl_ctx) +{ + glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORM_BLOCKS, + (GLint*) &s->num_blocks); + s->uniform_blocks = ARENA_ALLOC(arena, GLUniformBlock, s->num_blocks); + + for (u32 i = 0; i < s->num_blocks; i++) { + GLUniformBlock& ub = s->uniform_blocks[i]; + ub.block_id = i; + + GLchar block_name[256] = {0}; + GLsizei name_len = 0; + glGetActiveUniformBlockName( + s->prog_id, i, 256, &name_len, block_name); + ub.name = arenaCopyCStr(arena, block_name); + ub.uniform_type = getUniformType(ub.name); + + glGetActiveUniformBlockiv(s->prog_id, i, + GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, (GLint*) &ub.num_uniforms); + ub.uniforms = ARENA_ALLOC(arena, GLUniform, ub.num_uniforms); + GLint indices[ub.num_uniforms] = {0}; + glGetActiveUniformBlockiv(s->prog_id, i, + GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, (GLint*) &indices); + + for (u32 j = 0; j < ub.num_uniforms; j++) { + const GLUniform unif = parseUniform(arena, s, indices[j]); + std::memcpy(&ub.uniforms[j], &unif, sizeof(unif)); + } + + ub.binding_idx = ctxGetUniformBlockBinding(gl_ctx, ub.name); + + if (ub.binding_idx < 0) + return false; + + glUniformBlockBinding(s->prog_id, i, ub.binding_idx); + } + + // TODO: would be helpful for debugging if we sort the uniforms in a block + // by their uniform_offset instead of their idx + + return true; +} + +u32 +getNumAttribComponents(GLenum type) +{ + switch (type) { + case GL_FLOAT_VEC3: return 3; + case GL_FLOAT_VEC2: return 2; + default: + LOGF(Error, "unknown GLenum\n"); + return 0; + } +} + +GLenum +getAttribComponentType(GLenum type) +{ + switch (type) { + case GL_FLOAT_VEC3: return GL_FLOAT; + case GL_FLOAT_VEC2: return GL_FLOAT; + default: + LOGF(Error, "unknown GLenum\n"); + return 0; + } +} + +bool +parseAttributes(MemoryArena* arena, ShaderProgram* s, GLContext* gl_ctx) +{ + GLint num_attribs; + glGetProgramiv(s->prog_id, GL_ACTIVE_ATTRIBUTES, &num_attribs); + s->num_vertex_attribs = num_attribs; + s->vertex_attribs = ARENA_ALLOC(arena, GLVertexAttrib, num_attribs); + s->attrib_mappings = + ARENA_ALLOC(arena, GLBufferToAttribMapping, num_attribs); + + GLchar attrib_name[256] = {0}; + GLsizei length; + GLint size; + GLenum type; + + for (int i = 0; i < num_attribs; i++) { + glGetActiveAttrib(s->prog_id, i, sizeof(attrib_name), + &length, &size, &type, attrib_name); + GLint location = glGetAttribLocation(s->prog_id, attrib_name); + GLVertexAttrib* attrib = &s->vertex_attribs[i]; + attrib->data_type = type; + attrib->location = location; + attrib->num_components = getNumAttribComponents(type); + assert(attrib->num_components > 0); + attrib->component_type = getAttribComponentType(type); + assert(attrib->component_type > 0); + attrib->name = arenaCopyCStr(arena, attrib_name, sizeof(attrib_name)); + } + + return true; +} + +bool +parseShader(MemoryArena* arena, GLContext* gl_ctx, ShaderProgram* s) +{ + if (parseShaderUniforms(arena, s, gl_ctx) + && parseUniformBlocks(arena, s, gl_ctx) + && parseAttributes(arena, s, gl_ctx)) + { + return true; + } + + LOGF(Error, "Error parsing shader\n"); + return false; +} + diff --git a/src/shader_program.cpp b/src/shader_program.cpp deleted file mode 100644 index f2d94cb..0000000 --- a/src/shader_program.cpp +++ /dev/null @@ -1,146 +0,0 @@ - -#include "dumbLog.h" -#include "shader_program.h" -#include "util.h" - - -// forward declarations -bool checkShaderErrors(GLuint program_id); -void cleanUpShader(GLuint program_id, GLuint vs_id, GLuint fs_id); -bool compileProgram(GLuint& program_id_out, - GLuint& vs_id_out, - GLuint& fs_id_out, - const char* vertex_code, - const char* frag_code); - - -//interface - -default_shader_program* -shaderInitDefault(const char* vertex_code, const char* frag_code) -{ - LOG(Info) << "loading default shader\n"; - default_shader_program* sp = UTIL_ALLOC(1, default_shader_program); - GLuint vs_id = 0; - GLuint fs_id = 0; - - compileProgram(sp->program_id, vs_id, fs_id, vertex_code, frag_code); - glGenVertexArrays(1, &sp->vertex_array_id); - glBindVertexArray(sp->vertex_array_id); - - // assign uniforms - sp->world_transform_id = - glGetUniformLocation(sp->program_id, "world_transform"); - sp->model_matrix_id = glGetUniformLocation(sp->program_id, "model"); - sp->view_matrix_id = glGetUniformLocation(sp->program_id, "view"); - sp->projection_matrix_id = - glGetUniformLocation(sp->program_id, "projection"); - sp->normal_matrix_id = - glGetUniformLocation(sp->program_id, "normal_matrix"); - // FIXME: re-enable textures -#if 0 - sp->num_lights_id = glGetUniformLocation(sp->program_id, "num_lights"); - sp->sampler_id = glGetUniformLocation(sp->program_id, "sampler"); -#endif - - cleanUpShader(sp->program_id, vs_id, fs_id); - - if (!checkShaderErrors(sp->program_id)) { - glDeleteProgram(sp->program_id); - utilSafeFree(sp); - return nullptr; - } - - return sp; -} - -simple_shader_program* -shaderInitSimple(const char* vertex_code, const char* frag_code) -{ - LOG(Info) << "loading simple shader\n"; - simple_shader_program* sp = UTIL_ALLOC(1, simple_shader_program); - GLuint vs_id = 0; - GLuint fs_id = 0; - compileProgram(sp->program_id, vs_id, fs_id, vertex_code, frag_code); - glGenVertexArrays(1, &sp->vertex_array_id); - glBindVertexArray(sp->vertex_array_id); - - // assign uniforms - sp->world_transform_id = - glGetUniformLocation(sp->program_id, "world_transform"); - sp->MVP_id = glGetUniformLocation(sp->program_id, "MVP"); - - if (!checkShaderErrors(sp->program_id)) { - glDeleteProgram(sp->program_id); - utilSafeFree(sp); - return nullptr; - } - - return sp; -} - -void -shaderFree(uint program_id) -{ - // TODO: can check for valid id here - glDeleteProgram(program_id); -} - - -// internal - -bool -checkShaderErrors(uint program_id) -{ - GLint isLinked = 0; - GLint info_len = 0; - glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked); - - if (isLinked == GL_FALSE) { - glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_len); - char* infoLog = UTIL_ALLOC(info_len, char); - glGetProgramInfoLog(program_id, info_len, &info_len, &infoLog[0]); - LOG(Error) << infoLog << "\n"; - utilSafeFree(infoLog); - glDeleteProgram(program_id); - - return false; - } - - return true; -} - -void -cleanUpShader(GLuint program_id, GLuint vs_id, GLuint fs_id) -{ - glDetachShader(program_id, vs_id); - glDetachShader(program_id, fs_id); - glDeleteShader(vs_id); - glDeleteShader(fs_id); -} - -bool -compileProgram(GLuint& program_id_out, - GLuint& vs_id_out, - GLuint& fs_id_out, - const char* vertex_code, - const char* frag_code) -{ - vs_id_out = glCreateShader(GL_VERTEX_SHADER); - fs_id_out = glCreateShader(GL_FRAGMENT_SHADER); - - glShaderSource(vs_id_out, 1, &vertex_code, NULL); - glShaderSource(fs_id_out, 1, &frag_code, NULL); - glCompileShader(vs_id_out); - glCompileShader(fs_id_out); - // TODO: can check for error here - - program_id_out = glCreateProgram(); - glAttachShader(program_id_out, vs_id_out); - glAttachShader(program_id_out, fs_id_out); - glLinkProgram(program_id_out); - // TODO: can check for error here - - return true; -} - diff --git a/src/tangerine.cpp b/src/tangerine.cpp new file mode 100644 index 0000000..f799512 --- /dev/null +++ b/src/tangerine.cpp @@ -0,0 +1,386 @@ + +#include + +#include "tangerine.h" +#define UTIL_IMPLEMENTATION +#include "util.h" + + +// forward declarations + +bool initGraphics(SDLHandles* handles); + +LightsBuffer* initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights, + glm::vec4 ambient_color); + + +// interface + +RenderState* +initRenderState(GLClearColor clear_col, + glm::vec4 ambient_color, + u32 max_models, + u32 max_textures, + u32 max_shaders, + u32 max_render_groups, + u32 max_ubos, + u32 max_lights) +{ + LOGF(Info, "Initializing Renderer\n"); + RenderState* rs = UTIL_ALLOC(1, RenderState); + + if (rs) { + rs->clear_col = clear_col; + rs->assets.arena = arenaInit(DEFAULT_ARENA_SIZE); + rs->assets.max_models = max_models; + rs->assets.models = ARENA_ALLOC(rs->assets.arena, Model, max_models); + rs->assets.max_textures = max_textures; + rs->assets.textures = + ARENA_ALLOC(rs->assets.arena, Texture, max_textures); + + rs->rg_arena = arenaInit(DEFAULT_ARENA_SIZE); + rs->max_render_groups = DEFAULT_RENDER_GROUP_COUNT; + rs->render_groups = ARENA_ALLOC(rs->rg_arena, RenderGroup, + DEFAULT_RENDER_GROUP_COUNT); + + if (!initGraphics(&rs->handles)) { + LOGF(Error, "error initializing renderer\n"); + return nullptr; + } + + rs->gl_ctx = initGLContext(rs->assets.arena, + max_shaders, + max_textures, + max_ubos); + + rs->xforms = ARENA_ALLOC(rs->assets.arena, Transforms, 1); + GLBuffer* xforms_ubo = getFreeUBO(rs->gl_ctx); + assert(xforms_ubo); + initTransforms(rs->assets.arena, rs->xforms, xforms_ubo, rs->gl_ctx); + + rs->lights_buf = initLights(rs->rg_arena, rs->gl_ctx, max_lights, + ambient_color); + + bool ret = loadDefaultShaders(rs); + assert(ret); + } + + return rs; +} + +void +freeRenderState(RenderState*& rs) +{ + if (rs) { + SDL_GL_DeleteContext(rs->handles.sdl_gl_ctx); + SDL_DestroyWindow(rs->handles.window); + arenaFree(rs->assets.arena); + arenaFree(rs->rg_arena); + utilSafeFree(rs); + rs = nullptr; + } + + SDL_Quit(); +} + +void +initRenderGroup(RenderGroup* rg, + MemoryArena* arena, + ShaderProgram* shader, + u32 num_entities, + const char* name) +{ + rg->max_entities = num_entities; + rg->shader = shader; + rg->name = arenaCopyCStr(arena, name); + rg->entities = ARENA_ALLOC(arena, Entity, num_entities); +} + +void +freeRenderGroup(RenderGroup* rg, MemoryArena* arena) +{ + LOGF(Info, "should probably look into freeing arena memory?\n"); + assert(0); +} + +RenderGroup* +getFreeRenderGroup(RenderState* rs) +{ + if (rs->num_render_groups < rs->max_render_groups) + return &rs->render_groups[rs->num_render_groups++]; + + LOGF(Error, "no free render group\n"); + return nullptr; +} + +RenderGroup* +getRenderGroupByName(RenderState* rs, const char* name) +{ + RenderGroup* rg_out = nullptr; + + for (u32 i = 0; i < rs->num_render_groups; i++) { + RenderGroup* rg = &rs->render_groups[i]; + + if (utilCStrMatch(name, rg->name)) + rg_out = rg; + } + + if (rg_out == nullptr) + LOGF(Error, "render group with name, \"%s\", not found\n", name); + + return rg_out; +} + +Entity* +getFreeEntity(RenderGroup* rg) +{ + if (rg->num_entities < rg->max_entities) + return &rg->entities[rg->num_entities++]; + + LOGF(Error, "render group full\n"); + return nullptr; +} + +void +doRenderLoop(RenderState* rs, + u32 framerate, + frame_callback_fn cb_func_pre, + frame_callback_fn cb_func_post) +{ + u32 delay = (framerate > 0) ? 1 / framerate : 0; + u32 frameStart, frameTime; + rs->running = true; + SDL_Event e; + + while (rs->running) { + frameStart = SDL_GetTicks(); + + if (cb_func_pre != nullptr) { + cb_func_pre(rs); + } else { + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT || + (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) + { + rs->running = false; + break; + } + } + } + + renderFrame(rs, rs->clear_col); + + if (cb_func_post != nullptr) + cb_func_post(rs); + + SDL_GL_SwapWindow(rs->handles.window); + frameTime = SDL_GetTicks() - frameStart; + + if (delay > frameTime) + SDL_Delay(delay - frameTime); + } +} + +void +renderFrame(RenderState* rs, const GLClearColor& clear_col) +{ + glClearColor(clear_col.R, + clear_col.G, + clear_col.B, + clear_col.A); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + for (u32 i = 0; i < rs->num_render_groups; i++) { + RenderGroup* rg = &rs->render_groups[i]; + + for (u32 j = 0; j < rg->num_entities; j++) { + Entity* e = &rg->entities[j]; + + for (u32 k = 0; k < e->num_meshes; k++) { + GLMesh& glm = e->meshes[k]; + renderVAO(&glm, e->model_xform, rg->shader, + e->diffuse_texture); + } + } + } +} + +bool +loadDefaultShaders(RenderState* rs, + u32 num_shaders, + const ShaderInit shaders[]) +{ + for (u32 i = 0; i < num_shaders; i++) { + const ShaderInit& si = shaders[i]; + + if (!addShaderProgram(rs->assets.arena, + rs->gl_ctx, + si.vert_path, + si.frag_path, + si.name)) + { + LOG(Error) << "failed to load shader " << si.name << "\n"; + return false; + } + + ShaderProgram* shader = getShaderByName(si.name, rs->gl_ctx); + assert(shader); + + // NOTE: not every buffer will be available for every shader, so we + // enumerate them all, and store the ones that are present + u32 attrib_idx = 0; + + for (u32 i = 0; i < MESH_BUFFER_TYPE_COUNT; i++) { + MeshBufferType buf_type = (MeshBufferType) i; + GLVertexAttrib* attrib = getVertexAttribByType(shader, buf_type); + + if (attrib) + shader->attrib_mappings[attrib_idx++] = { attrib, buf_type }; + } + } + + return true; +} + +GLVertexAttrib* +getVertexAttribByType(ShaderProgram* shader, MeshBufferType buf_type) +{ + switch (buf_type) { + case VERTEX: return getVertexAttribByName(shader, "position"); + case NORMAL: return getVertexAttribByName(shader, "normal"); + case UV: return getVertexAttribByName(shader, "uv"); + case COLOR: return getVertexAttribByName(shader, "color"); + default: return nullptr; + } +} + + +// internal + +bool +initGraphics(SDLHandles* handles) +{ + handles->window = SDL_CreateWindow( + "shader_testing", + SDL_WINDOWPOS_CENTERED_DISPLAY(0), + SDL_WINDOWPOS_CENTERED_DISPLAY(0), + 1280, + 720, + SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + std::cout << "error, sdl init: " << SDL_GetError() << "\n"; + return false; + } + + SDL_GL_SetSwapInterval(1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, + SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GetCurrentDisplayMode(0, &handles->display_mode); + + handles->sdl_gl_ctx = SDL_GL_CreateContext(handles->window); + + if (!handles->sdl_gl_ctx) { + std::cout << "error creating context\n"; + return false; + } + + if (glewInit()) { + std::cout << "error initializing opengl\n"; + return false; + } + + std::cout << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; + std::cout << "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; + std::cout << "opengl version: " << glGetString(GL_VERSION) << "\n"; + + glEnable(GL_DEPTH_TEST); + glEnable(GL_LINE_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glEnable (GL_DEBUG_OUTPUT); + glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); + + return handles->window != nullptr; +} + +LightsBuffer* +initLights(MemoryArena* arena, + GLContext* gl_ctx, + u32 max_lights, + glm::vec4 ambient_color) +{ + // FIXME: revisit for 'Scene' abstraction + + LightsBuffer* lb = ARENA_ALLOC(arena, LightsBuffer, 1); + lb->buf_size = 8 * sizeof(u32) // NOTE: 'header' + + sizeof(glm::vec4) // NOTE: ambient color + + 6 * max_lights * sizeof(glm::vec4); // NOTE: vector arrays + LOGF(Debug, "buf_size: %d\n", lb->buf_size); + lb->buffer = ARENA_ALLOC(arena, u8, lb->buf_size); + + lb->max_p_lights = (u32*) lb->buffer; + lb->active_p_lights = + (u32*) arenaGetAddressOffset(lb->max_p_lights, sizeof(u32)); + lb->max_d_lights = + (u32*) arenaGetAddressOffset(lb->active_p_lights, sizeof(u32)); + lb->active_d_lights = + (u32*) arenaGetAddressOffset(lb->max_d_lights, sizeof(u32)); + + *lb->max_p_lights = max_lights; + *lb->max_d_lights = max_lights; + + // NOTE: add padding, we're not actually using this since 4 * u32 is on a + // 16 byte boundary, but will be helpful if we need to add more 'headers' + // in the future + void* arr_start = arenaGetAddressOffset(lb->buffer, 8 * sizeof(u32)); + + // NOTE: ambient color + lb->ambient_color = (glm::vec4*) arr_start; + *lb->ambient_color = ambient_color; + + // NOTE: set offsets for array pointers + u32 arr_size = max_lights * sizeof(glm::vec4); + lb->pl_positions = //(glm::vec4*) arr_start; + (glm::vec4*) arenaGetAddressOffset(arr_start, sizeof(glm::vec4)); + lb->pl_colors = + (glm::vec4*) arenaGetAddressOffset(lb->pl_positions, arr_size); + lb->pl_intensities = + (glm::uvec4*) arenaGetAddressOffset(lb->pl_colors, arr_size); + lb->dl_directions = + (glm::vec4*) arenaGetAddressOffset(lb->pl_intensities, arr_size); + lb->dl_colors = + (glm::vec4*) arenaGetAddressOffset(lb->dl_directions, arr_size); + lb->dl_intensities = + (glm::uvec4*) arenaGetAddressOffset(lb->dl_colors, arr_size); + + GLBuffer* lights_ubo = getFreeUBO(gl_ctx); + assert(lights_ubo); + + lights_ubo->target = GL_UNIFORM_BUFFER; + lights_ubo->data_type = GL_BYTE; // NOTE: mixed types in structure + lights_ubo->data_size = lb->buf_size; + lights_ubo->name = arenaCopyCStr(arena, "lights"); + assert((GLint) gl_ctx->binding_count < gl_ctx->max_binding_points); + lights_ubo->binding_idx = gl_ctx->binding_count++; + + glGenBuffers(1, &lights_ubo->id); + glBindBuffer(lights_ubo->target, lights_ubo->id); + glBufferData(lights_ubo->target, + lb->buf_size, + lb->buffer, + GL_DYNAMIC_DRAW); + glBindBufferBase(lights_ubo->target, + lights_ubo->binding_idx, + lights_ubo->id); + + return lb; +} + diff --git a/src/tiny_gltf.cc b/src/tiny_gltf.cc new file mode 100644 index 0000000..e5a9eba --- /dev/null +++ b/src/tiny_gltf.cc @@ -0,0 +1,5 @@ + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#define TINYGLTF_IMPLEMENTATION +#include "tiny_gltf.h" diff --git a/src/util.cpp b/src/util.cpp deleted file mode 100644 index 4ab78f8..0000000 --- a/src/util.cpp +++ /dev/null @@ -1,197 +0,0 @@ - -#include -#include -#include -#include -#include - -#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; -} - diff --git a/src/util_image.cpp b/src/util_image.cpp deleted file mode 100644 index ed44be5..0000000 --- a/src/util_image.cpp +++ /dev/null @@ -1,59 +0,0 @@ - -#include - -#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; -}