diff --git a/14-refactor/assets/popcorn_model_a.glb b/14-refactor/assets/popcorn_model_a.glb index dc02c85..9bcc5db 100644 Binary files a/14-refactor/assets/popcorn_model_a.glb and b/14-refactor/assets/popcorn_model_a.glb differ diff --git a/14-refactor/shaders/14-frag.glsl b/14-refactor/shaders/14-frag.glsl new file mode 100644 index 0000000..01f8c14 --- /dev/null +++ b/14-refactor/shaders/14-frag.glsl @@ -0,0 +1,150 @@ +#version 330 core +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +out vec4 FragColor; +in vec3 FragPos; +in vec3 Normal; +in vec2 TexCoords; + +uniform vec3 viewPos; +uniform Material material; + +struct DirLight { + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct PointLight { + vec3 position; + vec3 ambient; + vec3 diffuse; + vec3 specular; + float constant; + float linear; + float quadratic; +}; + +struct SpotLight { + vec3 direction; + vec3 position; + vec3 diffuse; + vec3 specular; + vec3 ambient; + + float constant; + float linear; + float quadratic; + float cutOff; + float outerCutOff; +}; + + +#define NR_POINT_LIGHTS 4 +uniform int pointLightCount; +uniform PointLight pointLights[NR_POINT_LIGHTS]; +uniform DirLight dirLight; +uniform SpotLight spotLight; + +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) +{ + vec3 mat_tex = vec3(texture(material.diffuse, TexCoords)); + vec3 spec_tex = vec3(texture(material.specular, TexCoords)); + + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + + vec3 ambient = light.ambient * mat_tex; + vec3 diffuse = light.diffuse * diff * mat_tex; + vec3 specular = light.specular * spec * spec_tex; + + return ambient + diffuse + specular; +} + +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 mat_tex = vec3(texture(material.diffuse, TexCoords)); + vec3 spec_tex = vec3(texture(material.specular, TexCoords)); + vec3 lightDir = normalize(light.position - fragPos); + + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + + vec3 ambient = light.ambient * mat_tex; + vec3 diffuse = light.diffuse * diff * mat_tex; + vec3 specular = light.specular * spec * spec_tex; + + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + + return ambient + diffuse + specular; +} + +vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 mat_tex = vec3(texture(material.diffuse, TexCoords)); + vec3 spec_tex = vec3(texture(material.specular, TexCoords)); + vec3 lightDir = normalize(light.position - fragPos); + + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + + vec3 ambient = light.ambient * mat_tex; + vec3 diffuse = light.diffuse * diff * mat_tex; + vec3 specular = light.specular * spec * spec_tex; + + float theta = dot(lightDir, normalize(-light.direction)); + float epsilon = light.cutOff - light.outerCutOff; + float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); + diffuse *= intensity; + specular *= intensity; + + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + + return ambient + diffuse + specular; +} + +void main() +{ + vec3 norm = normalize(Normal); + vec3 viewDir = normalize(viewPos - FragPos); + + vec3 result = CalcDirLight(dirLight, norm, viewDir); + + for(int i = 0; i < pointLightCount; i++) { + result += CalcPointLight(pointLights[i], norm, FragPos, viewDir); + } + + result += CalcSpotLight(spotLight, norm, FragPos, viewDir); + + FragColor = vec4(result, 1.0); +} diff --git a/14-refactor/shaders/14-lightsource.frag.glsl b/14-refactor/shaders/14-lightsource.frag.glsl new file mode 100644 index 0000000..30728b2 --- /dev/null +++ b/14-refactor/shaders/14-lightsource.frag.glsl @@ -0,0 +1,9 @@ +#version 330 core +out vec4 FragColor; + +uniform vec3 diffuse; + +void main() +{ + FragColor = vec4(diffuse, 1.0); // set all 4 vector values to 1.0 +} diff --git a/14-refactor/shaders/14-vert.glsl b/14-refactor/shaders/14-vert.glsl new file mode 100644 index 0000000..276f393 --- /dev/null +++ b/14-refactor/shaders/14-vert.glsl @@ -0,0 +1,20 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; + +out vec3 FragPos; +out vec3 Normal; +out vec2 TexCoords; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0f); + FragPos = vec3(model * vec4(aPos, 1.0)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; +} diff --git a/14-refactor/src/data.hpp b/14-refactor/src/data.hpp index 36b8442..d9487c8 100644 --- a/14-refactor/src/data.hpp +++ b/14-refactor/src/data.hpp @@ -1,63 +1,18 @@ #pragma once #include -#define CUBE_COUNT 10 - -float vertices[] = { - // positions // normals // texture coords - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - - -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - - -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, - -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, - -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, - - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f -}; +#define CUBE_COUNT 9 // positions all containers -glm::vec3 cube_positions[] = { - glm::vec3( 0.0f, 0.0f, 0.0f), - glm::vec3( 2.0f, 5.0f, -15.0f), - glm::vec3(-1.5f, -2.2f, -2.5f), - glm::vec3(-3.8f, -2.0f, -12.3f), - glm::vec3( 2.4f, -0.4f, -3.5f), - glm::vec3(-1.7f, 3.0f, -7.5f), - glm::vec3( 1.3f, -2.0f, -2.5f), - glm::vec3( 1.5f, 2.0f, -2.5f), - glm::vec3( 1.5f, 0.2f, -1.5f), - glm::vec3(-1.3f, 1.0f, -1.5f) +glm::vec3 popcorn_positions[] = { + {0.0f, 0.0f, 0.0f}, + {2.0f, 5.0f, -15.0f}, + {-1.5f, -2.2f, -2.5f}, + {-3.8f, -2.0f, -12.3f}, + { 2.4f, -0.4f, -3.5f}, + {-1.7f, 3.0f, -7.5f}, + { 1.3f, -2.0f, -2.5f}, + { 1.5f, 2.0f, -2.5f}, + { 1.5f, 0.2f, -1.5f}, + {-1.3f, 1.0f, -1.5f} }; diff --git a/14-refactor/src/main.cpp b/14-refactor/src/main.cpp index bc13ed6..000d253 100644 --- a/14-refactor/src/main.cpp +++ b/14-refactor/src/main.cpp @@ -25,6 +25,7 @@ const float AMBIENT_LEVEL = 0.25f; struct Config { Shader shader; Shader lightShader; + std::vector models; Lighting light{ .directional={ @@ -86,9 +87,6 @@ struct Config { .specularMap = 0, }; - unsigned int cubeVAO = 0; - unsigned int lightCubeVAO = 0; - unsigned int VBO = 0; float deltaTime = 0.0f; float lastFrame = 0.0f; @@ -208,95 +206,27 @@ Config setup() { glEnable(GL_DEPTH_TEST); Config config{ - .shader={"shaders/13-vert.glsl", "shaders/13-frag.glsl"}, - .lightShader={"shaders/13-vert.glsl", "shaders/13-lightsource.frag.glsl"}, + .shader={"shaders/14-vert.glsl", "shaders/14-frag.glsl"}, + .lightShader={"shaders/14-vert.glsl", "shaders/14-lightsource.frag.glsl"}, + .models={ + {"assets/popcorn_model_a.glb"} + }, }; - unsigned int VBO = 0; - unsigned int lightCubeVAO = 0; - unsigned int cubeVAO = 0; - - // cube vao configure, this is the light target - glGenVertexArrays(1, &cubeVAO); - glGenBuffers(1, &VBO); - - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glBindVertexArray(cubeVAO); - - // position attribute - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0); - glEnableVertexAttribArray(0); - - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(3 * sizeof(float))); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(6 * sizeof(float))); - glEnableVertexAttribArray(2); - - // light vao configure, this is the light source - glGenVertexArrays(1, &lightCubeVAO); - glBindVertexArray(lightCubeVAO); - - glBindBuffer(GL_ARRAY_BUFFER, VBO); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - - config.lightCubeVAO = lightCubeVAO; - config.cubeVAO = cubeVAO; - config.VBO = VBO; - - config.cubeMaterial.diffuseMap = load_texture("resources/textures/container2.png"); - config.cubeMaterial.specularMap = load_texture("resources/textures/container2_specular.png"); - - config.shader.use(); - config.shader.setInt("material.diffuse", 0); - config.shader.setInt("material.specular", 1); - return config; } -void draw_cube(size_t pos, Config& config, glm::mat4& projection, glm::mat4& view) { - config.shader.setMat4("view", view); - config.shader.setMat4("projection", projection); - config.shader.setVec3("viewPos", config.camera.pos); - +void draw_popcorn(Config& config, size_t model_i, size_t model_pos, glm::mat4& projection, glm::mat4& view) +{ + glm::vec3 objectColor = {1.0, 0.94, 0.78}; config.shader.applyMaterial(config.cubeMaterial); glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, cube_positions[pos]); - - float angle = glfwGetTime() * (float)pos; - model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + model = glm::translate(model, popcorn_positions[model_pos]); config.shader.setMat4("model", model); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, config.cubeMaterial.diffuseMap); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, config.cubeMaterial.specularMap); - - glBindVertexArray(config.cubeVAO); - glDrawArrays(GL_TRIANGLES, 0, 36); -} - -void draw_light(Config& config, Light& light, glm::mat4& projection, glm::mat4& view) { - config.lightShader.use(); - - config.lightShader.setVec3("diffuse", light.diffuse); - config.lightShader.setMat4("projection", projection); - config.lightShader.setMat4("view", view); - - glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, light.position); - model = glm::scale(model, glm::vec3(0.2f)); - config.lightShader.setMat4("model", model); - - glBindVertexArray(config.lightCubeVAO); - glDrawArrays(GL_TRIANGLES, 0, 36); + config.models[model_i].Draw(config.shader); } void render(GLFWwindow* window, Config& config) { @@ -317,16 +247,16 @@ void render(GLFWwindow* window, Config& config) { projection = glm::perspective(glm::radians(config.camera.fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); config.shader.use(); + config.shader.setMat4("view", view); + config.shader.setMat4("projection", projection); + config.shader.setVec3("viewPos", config.camera.pos); + config.light.spot.position = config.camera.pos; config.light.spot.direction = config.camera.front; config.shader.applyLighting(config.light); for(size_t i = 0; i < CUBE_COUNT; i++) { - draw_cube(i, config, projection, view); - } - - for(auto& light : config.light.positioned) { - draw_light(config, light, projection, view); + draw_popcorn(config, 0, i, projection, view); } glfwSwapBuffers(window); @@ -334,9 +264,6 @@ void render(GLFWwindow* window, Config& config) { } void cleanup(Config& config) { - glDeleteVertexArrays(1, &config.lightCubeVAO); - glDeleteBuffers(1, &config.lightCubeVAO); - glDeleteBuffers(1, &config.VBO); config.shader.cleanup(); } diff --git a/14-refactor/src/mesh.cpp b/14-refactor/src/mesh.cpp index a05e18b..e31c83e 100644 --- a/14-refactor/src/mesh.cpp +++ b/14-refactor/src/mesh.cpp @@ -1,4 +1,5 @@ #include "mesh.hpp" +#include "dbc.hpp" void Mesh::Draw(Shader &shader) { unsigned int diffuseNr = 1; @@ -8,21 +9,25 @@ void Mesh::Draw(Shader &shader) { for(unsigned int i = 0; i < textures.size(); i++) { glActiveTexture(GL_TEXTURE0 + i); - std::string number; - std::string name = textures[i].type; + unsigned int number = 0; + std::string& name = textures[i].type; if(name == "texture_diffuse") { - number = std::to_string(diffuseNr++); + number = diffuseNr++; } else if(name == "texture_specular") { - number = std::to_string(specularNr++); + number = specularNr++; } else if(name == "texture_normal") { - number = std::to_string(normalNr++); + number = normalNr++; } else if(name == "texture_height") { - number = std::to_string(heightNr++); + number = heightNr++; + } else { + dbc::sentinel($F("Invalid texture type name {}", name)); } + std::string target = std::format("{}{}", name, number); + // TODO: Get this uniform ID once and cache it? - glUniform1i(glGetUniformLocation(shader.ID, (name + number).c_str()), i); + glUniform1i(glGetUniformLocation(shader.ID, target.c_str()), i); glBindTexture(GL_TEXTURE_2D, textures[i].id); } @@ -46,6 +51,7 @@ void Mesh::setupMesh() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW); + // this matches the locations in the vertex shader glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Position)); diff --git a/14-refactor/src/model.cpp b/14-refactor/src/model.cpp index e6b2fdd..0d8b99f 100644 --- a/14-refactor/src/model.cpp +++ b/14-refactor/src/model.cpp @@ -1,20 +1,14 @@ #include "model.hpp" #include "dbc.hpp" +#include -unsigned int TextureFromFile(const std::string& path, const std::string& directory, bool gamma) -{ - std::string filename = path + "/" + filename; +unsigned int TextureFromFile(const std::string& path, const std::string& directory); +unsigned int TextureFromInternal(const aiScene *scene, size_t index); +unsigned int to_gl_texture(unsigned char *data, int width, int height, int nrComponents) { unsigned int textureID; glGenTextures(1, &textureID); - int width = 0; - int height = 0; - int nrComponents = 0; - - unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0); - dbc::check(data != nullptr, $F("Failed to load texture {}", filename)); - GLenum format = GL_RGB; if (nrComponents == 1) { @@ -34,6 +28,48 @@ unsigned int TextureFromFile(const std::string& path, const std::string& directo glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + return textureID; +} + +unsigned int TextureFromInternal(const aiScene *scene, size_t index) { + const aiTexture *aiTex = scene->mTextures[index]; + unsigned int textureID = 0; + int width = 0; + int height = 0; + int nrComponents = 0; + unsigned char *data = nullptr; + const unsigned char* dataBytes = (const unsigned char*)aiTex->pcData; + + if(aiTex->mHeight == 0) { + // compressed texture (PNG or JPG) + size_t dataSize = aiTex->mWidth; + data = stbi_load_from_memory(dataBytes, dataSize, &width, &height, &nrComponents, 0); + textureID = to_gl_texture(data, width, height, nrComponents); + stbi_image_free(data); + } else { + data = (unsigned char*)aiTex->pcData; + width = aiTex->mWidth; + height = aiTex->mHeight; + nrComponents = 4; // either BGRA8888 or RGBA8888 + textureID = to_gl_texture(data, width, height, nrComponents); + } + + return textureID; +} + +unsigned int TextureFromFile(const std::string& path, const std::string& directory) +{ + std::string filename = directory + "/" + path; + + int width = 0; + int height = 0; + int nrComponents = 0; + + unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0); + dbc::check(data != nullptr, $F("Failed to load texture {}", filename)); + + unsigned int textureID = to_gl_texture(data, width, height, nrComponents); + stbi_image_free(data); return textureID; @@ -45,9 +81,14 @@ void Model::Draw(Shader &shader) { } } -void Model::loadModel(std::string path) { +void Model::loadModel(const std::string& path) { Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace); + + const aiScene* scene = importer.ReadFile(path, + aiProcess_Triangulate | + aiProcess_GenSmoothNormals | + aiProcess_FlipUVs | + aiProcess_CalcTangentSpace); dbc::check(scene != nullptr, "Assimp ReadFile return null"); dbc::check(!(scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE), "Assimp says incomplete."); @@ -118,54 +159,53 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { // normal: texture_normalN // 1. diffuse maps - std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"); - textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); + loadMaterialTextures(textures, scene, material, aiTextureType_DIFFUSE, "texture_diffuse"); // 2. specular maps - std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"); - textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); + loadMaterialTextures(textures, scene, material, aiTextureType_SPECULAR, "texture_specular"); // 3. normal maps - std::vector normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); - textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); + loadMaterialTextures(textures, scene, material, aiTextureType_HEIGHT, "texture_normal"); // 4. height maps - std::vector heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height"); - textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); + loadMaterialTextures(textures, scene, material, aiTextureType_AMBIENT, "texture_height"); return Mesh(vertices, indices, textures); } -std::vector Model::loadMaterialTextures(aiMaterial *mat, aiTextureType type, std::string typeName) +void Model::loadMaterialTextures(std::vector& textures, const aiScene *scene, aiMaterial *mat, aiTextureType type, std::string typeName) { fmt::println("TEXTURE COUNT: {}, {}, {}", (int)type, typeName, mat->GetTextureCount(type)); - std::vector textures; for(unsigned int i = 0; i < mat->GetTextureCount(type); i++) { aiString str; + mat->GetTexture(type, i, &str); std::string tx_str{str.C_Str()}; + dbc::check(!tx_str.empty(), "Texture has empty path, should be impossible?"); - bool skip = false; - for(unsigned int j = 0; j < textures_loaded.size(); j++) { - if(textures_loaded[j].path == tx_str) { - textures.push_back(textures_loaded[j]); - skip = true; - break; + if(textures_loaded.contains(tx_str)) { + textures.push_back(textures_loaded.at(tx_str)); + } else { + unsigned int tx_id = 0; + + // if it's a *# style path then it's internal + if(tx_str[0] == '*') { + // get the texture from the internal version + tx_id = TextureFromInternal(scene, std::stoi(tx_str.substr(1))); + } else { + // else get it from a file + tx_id = TextureFromFile(tx_str, directory); } - } - if(!skip) { Texture texture{ - .id = TextureFromFile(tx_str, directory), + .id = tx_id, .type = typeName, .path = tx_str, }; textures.push_back(texture); - textures_loaded.push_back(texture); + textures_loaded.try_emplace(tx_str, texture); } } - - return textures; } diff --git a/14-refactor/src/model.hpp b/14-refactor/src/model.hpp index 8d23203..12493f8 100644 --- a/14-refactor/src/model.hpp +++ b/14-refactor/src/model.hpp @@ -18,12 +18,10 @@ #include #include -unsigned int TextureFromFile(const std::string& path, const std::string& directory, bool gamma = false); - struct Model { - std::vector textures_loaded; + std::map textures_loaded; std::vector meshes; - std::string directory; + std::string directory{"./"}; bool gammaCorrection; Model(const char *path) { @@ -32,9 +30,9 @@ struct Model { void Draw(Shader &shader); - void loadModel(std::string path); + void loadModel(const std::string& path); void processNode(aiNode *node, const aiScene *scene); Mesh processMesh(aiMesh *mesh, const aiScene *scene); - std::vector loadMaterialTextures(aiMaterial *mat, aiTextureType type, std::string typeName); + void loadMaterialTextures(std::vector& textures, const aiScene *scene, aiMaterial *mat, aiTextureType type, std::string typeName); }; diff --git a/14-refactor/tests/main.cpp b/14-refactor/tests/main.cpp index 48b605b..a401b6b 100644 --- a/14-refactor/tests/main.cpp +++ b/14-refactor/tests/main.cpp @@ -4,6 +4,7 @@ #include "dbc.hpp" TEST_SET(shader_tests); +TEST_SET(model_tests); using namespace fuc2; @@ -31,7 +32,8 @@ int main(int argc, char* argv[]) { } std::vector tests{ - shader_tests::TESTS + shader_tests::TESTS, + model_tests::TESTS }; return run_tests(tests, argc, argv); diff --git a/14-refactor/tests/meson.build b/14-refactor/tests/meson.build index 90c7a91..877a6d3 100644 --- a/14-refactor/tests/meson.build +++ b/14-refactor/tests/meson.build @@ -1,4 +1,5 @@ fuc2_tests = files( 'main.cpp', 'shader_tests.cpp', + 'model_tests.cpp', ) diff --git a/14-refactor/tests/model_tests.cpp b/14-refactor/tests/model_tests.cpp new file mode 100644 index 0000000..fa50fd1 --- /dev/null +++ b/14-refactor/tests/model_tests.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include "model.hpp" + +using namespace fuc2; + +namespace model_tests { + void test_load_textures() { + Model model{"assets/popcorn_model_a.glb"}; + } + + + fuc2::Set TESTS{ + .name="models", + .options={ .fail_fast=false }, + .tests={ + TEST(test_load_textures), + } + }; +}