diff --git a/resources/default_shader.fs b/resources/default_shader.fs new file mode 100644 index 0000000..86eb57c --- /dev/null +++ b/resources/default_shader.fs @@ -0,0 +1,20 @@ +#version 330 + +in vec2 fragTexCoord; // texture coordinate +in vec3 fragNormal; // normal direction + +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +out vec4 finalColor; + +uniform vec3 lightDirection; // direction of light +uniform vec4 ambient; + +void main() { + vec4 texelColor = texture(texture0, fragTexCoord); + vec3 light_dot = vec3(pow(dot(fragNormal, lightDirection)-0.2, 3.0)); + finalColor = texelColor * colDiffuse * vec4(light_dot, 1.0); + finalColor += texelColor * (ambient/10.0) * colDiffuse; + finalColor = pow(finalColor, vec4(vec3(0.6), 1.0)); +} diff --git a/resources/default_shader.vs b/resources/default_shader.vs new file mode 100644 index 0000000..93dac26 --- /dev/null +++ b/resources/default_shader.vs @@ -0,0 +1,25 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec2 fragTexCoord; +out vec3 fragNormal; + +void main() +{ + // Send vertex attributes to fragment shader + fragTexCoord = vertexTexCoord; + fragNormal = normalize(vec3(matModel * vec4(vertexNormal, 1.0))); + + // Calculate final vertex position + gl_Position = mvp*vec4(vertexPosition, 1.0); +} diff --git a/resources/spacefighter.glb b/resources/spacefighter.glb index ea4aea3..7178bcd 100644 Binary files a/resources/spacefighter.glb and b/resources/spacefighter.glb differ diff --git a/resources/wabbit_alpha.png b/resources/wabbit_alpha.png deleted file mode 100644 index db4081f..0000000 Binary files a/resources/wabbit_alpha.png and /dev/null differ diff --git a/src/core/mesh_render_entity.c b/src/core/mesh_render_entity.c index f6de252..77949af 100644 --- a/src/core/mesh_render_entity.c +++ b/src/core/mesh_render_entity.c @@ -25,6 +25,7 @@ impl_Renderable_for(MeshRenderEntity, SceneNode *CreateMeshRenderEntity(char const *resource_path) { MeshRenderEntity *self = new(MeshRenderEntity); GetModelResource(resource_path, &self->model); + GetShaderResource("default_shader", &self->shader); return CreateSceneNode(MeshRenderEntity_as_SceneNodeEntity(self)); } @@ -36,15 +37,24 @@ void MeshRenderEntityEnterTree(MeshRenderEntity *self) { SceneNodeEntity parent_entity = self->node->parent->entity; self->transform = TC_CAST(parent_entity, Transformable); LoadResource(self->model.handle); + LoadResource(self->shader.handle); + Vector3 light_direction = Vector3Normalize((Vector3){1.f, 1.f, 0.f}); + SetShaderValue(*self->shader.resource, GetShaderLocation(*self->shader.resource, "lightDirection"), &light_direction, SHADER_UNIFORM_VEC3); + SetShaderValue(*self->shader.resource, GetShaderLocation(*self->shader.resource, "ambient"), &BLACK, SHADER_UNIFORM_VEC4); + for(size_t i = 0; i < self->model.resource->materialCount; ++i) + self->model.resource->materials[i].shader = *self->shader.resource; AddRenderable(MeshRenderEntity_as_Renderable(self)); } void MeshRenderEntityExitTree(MeshRenderEntity *self) { ReleaseResource(self->model.handle); + ReleaseResource(self->shader.handle); RemoveRenderable(MeshRenderEntity_as_Renderable(self)); } void MeshRenderEntityDraw(MeshRenderEntity *self) { self->model.resource->transform = TransformGetMatrix(self->transform.tc->get_global_transform(self->transform.data)); - DrawModel(*self->model.resource, (Vector3){0.f, 0.f, 0.f}, 1.f, WHITE); + BeginShaderMode(*self->shader.resource); + DrawModel(*self->model.resource, Vector3Zero(), 1.0f, WHITE); + EndShaderMode(); } diff --git a/src/core/mesh_render_entity.h b/src/core/mesh_render_entity.h index ca726cb..3e185cc 100644 --- a/src/core/mesh_render_entity.h +++ b/src/core/mesh_render_entity.h @@ -12,6 +12,7 @@ typedef struct MeshRenderEntity { SceneNode *node; Transformable transform; ModelResource model; + ShaderResource shader; } MeshRenderEntity; extern SceneNode *CreateMeshRenderEntity(char const *resource_path); diff --git a/src/core/resources.c b/src/core/resources.c index 3160d36..fc53900 100644 --- a/src/core/resources.c +++ b/src/core/resources.c @@ -8,7 +8,7 @@ char const *RESOURCE_DIRECTORY = "resources"; typedef enum ResourceType { - RESOURCE_MODEL, RESOURCE_TEXTURE, + RESOURCE_MODEL, RESOURCE_TEXTURE, RESOURCE_SHADER, RESOURCE_MAX } ResourceType; @@ -21,6 +21,7 @@ typedef struct ResourceContainer { union { Model model; Texture texture; + Shader shader; }; } ResourceContainer; @@ -31,9 +32,11 @@ static void Internal_LoadModelResource(ResourceContainer *resource); static void Internal_UnloadModelResource(ResourceContainer *resource); static void Internal_LoadTextureResource(ResourceContainer *resource); static void Internal_UnloadTextureResource(ResourceContainer *resource); +static void Internal_LoadShaderResource(ResourceContainer *resource); +static void Internal_UnloadShaderResource(ResourceContainer *resource); -LoadResourceFn g_load_functions[] = {Internal_LoadModelResource, Internal_LoadTextureResource}; -UnloadResourceFn g_unload_functions[] = {Internal_UnloadModelResource, Internal_UnloadTextureResource}; +LoadResourceFn g_load_functions[] = {Internal_LoadModelResource, Internal_LoadTextureResource, Internal_LoadShaderResource}; +UnloadResourceFn g_unload_functions[] = {Internal_UnloadModelResource, Internal_UnloadTextureResource, Internal_UnloadShaderResource}; static HashMap g_resource_map; static FilePathList g_resource_files; @@ -52,6 +55,9 @@ static ResourceType ResourceGetTypeFromPath(char const *path) { // Model files if(IsFileExtension(path, ".glb")) return RESOURCE_MODEL; if(IsFileExtension(path, ".gltf")) return RESOURCE_MODEL; + // Shader files + if(IsFileExtension(path, ".fg")) return RESOURCE_SHADER; + if(IsFileExtension(path, ".vs")) return RESOURCE_SHADER; return RESOURCE_MAX; } @@ -85,13 +91,23 @@ void Internal_IndexResourceDirectory() { .is_loaded = false, .use_counter = 0, .path = g_resource_files.paths[i], - .name = GetFileName(g_resource_files.paths[i]), + .name = GetFileNameWithoutExt(g_resource_files.paths[i]), .type = ResourceGetTypeFromPath(g_resource_files.paths[i]) }; // only index resources that the engine knows how to load - if(placeholder.type != RESOURCE_MAX) { + if(placeholder.type == RESOURCE_SHADER) { // shaders are made up of two files, so their paths are treated differently + // if the shader has already been added, don't accidentally add it twice. + ResourceContainer *found_container = hash_map_get_as(ResourceContainer, &g_resource_map, &placeholder.name); + if(found_container == NULL) { + placeholder.path = GetDirectoryPath(placeholder.path); + LOG_INFO("Internal_IndexResourceDirectory: Indexing %s as %s", placeholder.path, placeholder.name); + hash_map_insert(&g_resource_map, &placeholder.name, &placeholder); + LOG_INFO("hash: %zu", strhash(placeholder.name)); + } + } else if(placeholder.type != RESOURCE_MAX) { // regular resources LOG_INFO("Internal_IndexResourceDirectory: Indexing %s as %s", placeholder.path, placeholder.name); hash_map_insert(&g_resource_map, &placeholder.name, &placeholder); + LOG_INFO("hash: %zu", strhash(placeholder.name)); } } } @@ -104,7 +120,8 @@ void InitializeResourceSubsystem() { void CleanResourceSubsystem() { List resources = hash_map_values(&g_resource_map); list_foreach(ResourceContainer *,resource, &resources) - g_unload_functions[resource->type](resource); + if(resource->is_loaded) + g_unload_functions[resource->type](resource); hash_map_empty(&g_resource_map); UnloadDirectoryFiles(g_resource_files); } @@ -128,7 +145,7 @@ bool GetTextureResource(char const *path, TextureResource *out) { *out = ResourceEmpty(Texture); // assert some assumptions about the found resource ASSERT_RETURN(container != NULL, false, "GetTextureResource: Resource %s not in index.", path); - ASSERT_RETURN(container->type != RESOURCE_TEXTURE, false, "GetTextureResource: Resource %s is not a Texture.", path); + ASSERT_RETURN(container->type == RESOURCE_TEXTURE, false, "GetTextureResource: Resource %s is not a Texture.", path); *out = (TextureResource) { .handle = container, .resource = &container->texture @@ -136,6 +153,18 @@ bool GetTextureResource(char const *path, TextureResource *out) { return true; } +bool GetShaderResource(char const *path, ShaderResource *out) { + ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path); + *out = ResourceEmpty(Shader); + ASSERT_RETURN(container != NULL, false, "GetShaderResource: Resource %s not in index.", path); + ASSERT_RETURN(container->type == RESOURCE_SHADER, false, "GetShaderResource: Resource %s is not a Shader.", path); + *out = (ShaderResource) { + .handle = container, + .resource = &container->shader + }; + return true; +} + bool IsResourceLoaded(ResourceHandle handle) { return handle->is_loaded; } @@ -157,6 +186,7 @@ void ReleaseResource(ResourceHandle handle) { static void Internal_LoadModelResource(ResourceContainer *resource) { + LOG_INFO("Loading model from: %s", resource->path); resource->model = LoadModel(resource->path); } @@ -176,3 +206,16 @@ void Internal_UnloadTextureResource(ResourceContainer *resource) { UnloadTexture(resource->texture); resource->texture = (Texture){0}; } + +static +void Internal_LoadShaderResource(ResourceContainer *resource) { + resource->shader = LoadShader(TextFormat("%s/%s.vs", resource->path, resource->name), TextFormat("%s/%s.fs", resource->path, resource->name)); + ASSERT_RETURN(IsShaderReady(resource->shader),, "Internal_LoadShaderResource: Shader failed to load."); +} + +static +void Internal_UnloadShaderResource(ResourceContainer *resource) { + UnloadShader(resource->shader); + resource->shader = (Shader){0}; +} + diff --git a/src/core/resources.h b/src/core/resources.h index 1b1c5ca..2fd43c1 100644 --- a/src/core/resources.h +++ b/src/core/resources.h @@ -11,6 +11,7 @@ typedef struct ResourceContainer* ResourceHandle; ResourceType(Model); ResourceType(Texture); +ResourceType(Shader); //! Allocate and initialize memory required to operate resource subsystem extern void InitializeResourceSubsystem(); @@ -20,6 +21,8 @@ extern void CleanResourceSubsystem(); extern bool GetModelResource(char const *path, ModelResource *out); //! Get indexed texture from resources. extern bool GetTextureResource(char const *path, TextureResource *out); +//! Get indexed shader from resources. +extern bool GetShaderResource(char const *path, ShaderResource *out); //! Check if a resource is loaded or not. extern bool IsResourceLoaded(ResourceHandle handle); diff --git a/src/main.c b/src/main.c index fa079c6..9de5740 100644 --- a/src/main.c +++ b/src/main.c @@ -9,22 +9,30 @@ #include "utils/debug.h" Scene *CreateInitialScene() { + Transformable transformable; + Transform transform; SceneNode *root = CreateTransformNode(); + // create a model with a camera attached as a child of the root of the scene SceneNode *model_parent = CreateTransformNode(); SceneNodeAddChild(root, model_parent); - - SceneNode *camera_parent = CreateTransformNode(); - SceneNodeAddChild(model_parent, camera_parent); - // set camera parent location - Transformable transformable = TC_CAST(camera_parent->entity, Transformable); - Transform transform = transformable.tc->get_transform(transformable.data); - transform.translation = (Vector3){0.f, 3.f, -10.f}; + // create the rest of the scene with a mesh and a functionality node + SceneNodeAddChild(model_parent, CreateMeshRenderEntity("spacefighter")); + SceneNodeAddChild(model_parent, CreateTestObject()); + transformable = TC_CAST(model_parent->entity, Transformable); + transform = transformable.tc->get_global_transform(transformable.data); + transform.translation.y += 10; + transformable.tc->set_global_transform(transformable.data, transform); + // create a camera attached to it's own transform node + SceneNode *camera_parent = CreateTransformNode(); // the parent + SceneNodeAddChild(model_parent, camera_parent); // attached to the model parent + // set camera parent offset + transformable = TC_CAST(camera_parent->entity, Transformable); + transform = transformable.tc->get_transform(transformable.data); + transform.translation = (Vector3){-3.f, 3.f, -10.f}; transform.rotation = QuaternionFromEuler(7.5f * DEG2RAD, 0.f, 0.f); transformable.tc->set_transform(transformable.data, transform); - + // create the camera and attach it to the dedicated transform node SceneNodeAddChild(camera_parent, CreateCameraNode()); - SceneNodeAddChild(model_parent, CreateMeshRenderEntity("spacefighter.glb")); - SceneNodeAddChild(model_parent, CreateTestObject()); return CreateScene(root); } diff --git a/src/utils b/src/utils index c4ec92f..af50174 160000 --- a/src/utils +++ b/src/utils @@ -1 +1 @@ -Subproject commit c4ec92fa82ab0bbee24f5c60ce36455a37d3696d +Subproject commit af50174b71853f3ca887fc0e064e88258c412f2c