diff --git a/data b/data index 41b7c54..0ed0baf 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 41b7c54fb2819da830a47d0e5a5756eed5a0310d +Subproject commit 0ed0baf703fdc72c524d8c779c3ae2d92402d041 diff --git a/lib/emergent b/lib/emergent index ce417f4..c79c7f5 160000 --- a/lib/emergent +++ b/lib/emergent @@ -1 +1 @@ -Subproject commit ce417f4eafa15035b3a7613363e23157b8e59900 +Subproject commit c79c7f5a714472ae123a12400d462e59c05bd443 diff --git a/src/application.cpp b/src/application.cpp index 6d8216d..f058349 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -2075,6 +2075,8 @@ void Application::loadLevel(std::size_t index) { albedoOpacityMap->setValue(&pheromoneTexture); } + + material->setFlags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_DISABLE_SHADOW_CASTING); //material->shadowCaster = false; //material->flags |= (unsigned int)PhysicalMaterial::Flags::TRANSLUCENT; diff --git a/src/configuration.hpp.in b/src/configuration.hpp.in index 905935e..7ec0d41 100644 --- a/src/configuration.hpp.in +++ b/src/configuration.hpp.in @@ -46,7 +46,7 @@ const float WORLD_WIDTH = 100.0f; // cm const float WORLD_HEIGHT = 100.0f; // cm const float FRAMES_PER_SECOND = 60.0f; const float TIMESTEP = 1.0f / FRAMES_PER_SECOND; -const float PHEROMONE_MATRIX_RESOLUTION = 5.12f; +const float PHEROMONE_MATRIX_RESOLUTION = 5.12f; // pheromone cells per cm const int PHEROMONE_MATRIX_COLUMNS = (int)(WORLD_WIDTH * PHEROMONE_MATRIX_RESOLUTION); const int PHEROMONE_MATRIX_ROWS = (int)(WORLD_HEIGHT * PHEROMONE_MATRIX_RESOLUTION); const Vector2 WORLD_BOUNDS_MIN = Vector2(-WORLD_WIDTH * 0.5f, -WORLD_HEIGHT * 0.5f); @@ -59,4 +59,7 @@ const float HOMING_PHEROMONE_COLOR[] = {0.55f, 0.55f, 0.00f, 0.0f}; // CMYK const float RECRUITMENT_PHEROMONE_COLOR[] = {0.00f, 0.55f, 0.55f, 0.0f}; // CMYK const float ALARM_PHEROMONE_COLOR[] = {0.00f, 0.00f, 1.00f, 0.0f}; // CMYK +const std::uint64_t MATERIAL_FLAG_TRANSLUCENT = 0x0000000001; +const std::uint64_t MATERIAL_FLAG_DISABLE_SHADOW_CASTING = 0x0000000002; + #endif // CONFIGURATION_HPP diff --git a/src/material-loader.cpp b/src/material-loader.cpp index 66fef64..7f0964b 100644 --- a/src/material-loader.cpp +++ b/src/material-loader.cpp @@ -78,151 +78,244 @@ Material* MaterialLoader::load(const std::string& filename) return nullptr; } - // Parse lines std::string line; + std::size_t lineNumber = 0; + const std::string whitespace = " \t"; + + // Parse lines while (file.good() && std::getline(file, line)) { - const std::string whitespace = " \t"; + // Increment current line number + ++lineNumber; // Skip empty lines if (line.empty()) { continue; } - - // Find position of first character in variable name - std::size_t variableNamePosition = line.find_first_not_of(whitespace, 0); - if (variableNamePosition == std::string::npos) - { - // Skip whitespace-only lines - continue; - } - // Find position of equals sign - std::size_t equalsSignPosition = line.find_first_of("=", variableNamePosition); - if (equalsSignPosition == std::string::npos) + // Find position of first character in the command + std::size_t commandPosition = line.find_first_not_of(whitespace, 0); + if (commandPosition == std::string::npos) { - // Skip lines with no equals sign + // Skip whitespace-only lines continue; } - // Find position of first character in variable type - std::size_t variableTypePosition = line.find_first_not_of(whitespace, equalsSignPosition + 1); - if (variableTypePosition == std::string::npos) - { - // Skip lines with no variable type definition - continue; - } + // Determine command type + std::string command = line.substr(commandPosition, line.find_first_of(" \t=", commandPosition) - commandPosition); - // Count parentheses - std::size_t leftParenthesisCount = std::count(line.begin() + variableNamePosition, line.end(), '('); - std::size_t rightParenthesisCount = std::count(line.begin() + variableNamePosition, line.end(), ')'); - if (leftParenthesisCount != rightParenthesisCount || leftParenthesisCount == 0) + // Parse command + if (command == "shader" || command == "flags") { - // Skip lines with invalid number of parentheses - continue; + // Find position of equals sign + std::size_t equalsSignPosition = line.find_first_of("=", commandPosition); + if (equalsSignPosition == std::string::npos) + { + // Skip lines with no equals sign + std::cerr << "MaterialLoader::load(): Invalid line " << lineNumber << " in \"" << filename << "\"" << std::endl; + continue; + } + + // Find position of first character in the value string + std::size_t valueStartPosition = line.find_first_not_of(whitespace, equalsSignPosition + 1); + if (valueStartPosition == std::string::npos) + { + // Skip lines with no value + std::cerr << "MaterialLoader::load(): Invalid line " << lineNumber << " in \"" << filename << "\"" << std::endl; + continue; + } + + // Find position the end of the value string + std::size_t valueEndPosition = line.find_first_of(" \t;", valueStartPosition); + + // Determine value string + std::string valueString; + if (valueEndPosition == std::string::npos) + { + valueString = line.substr(valueStartPosition); + } + else + { + valueString = line.substr(valueStartPosition, valueEndPosition - valueStartPosition); + } + + // Execute command + if (command == "shader") + { + // Load shader + Shader* shader = loadShader(valueString); + if (!shader) + { + std::cerr << "MaterialLoader::load(): Failed to load shader \"" << valueString << "\" on line " << lineNumber << " in \"" << filename << "\"" << std::endl; + } + else + { + material->setShader(shader); + } + } + else + { + // Parse flags + std::uint64_t flags; + std::stringstream stream; + stream << valueString; + stream >> flags; + + material->setFlags(flags); + } } - - std::string variableName = line.substr(variableNamePosition, line.find_first_of(" \t=", variableNamePosition) - variableNamePosition); - std::string variableType = line.substr(variableTypePosition, line.find_first_of(" \t[(", variableTypePosition) - variableTypePosition); - std::size_t elementCount = leftParenthesisCount; - - std::size_t currentPosition = variableTypePosition; - std::vector> elements; - bool invalid = false; - for (std::size_t i = 0; i < elementCount; ++i) + else if (command == "var") { - std::size_t leftParenthesisPosition = line.find_first_of("(", currentPosition); - std::size_t rightParenthesisPosition = line.find_first_of(")", leftParenthesisPosition + 1); + // Find position of first character in variable name + std::size_t variableNamePosition = line.find_first_not_of(whitespace, commandPosition + command.length()); + if (variableNamePosition == std::string::npos) + { + // Skip lines with no variable name + std::cerr << "MaterialLoader::load(): Invalid variable on line " << lineNumber << " in \"" << filename << "\"" << std::endl; + continue; + } + + // Find position of equals sign + std::size_t equalsSignPosition = line.find_first_of("=", variableNamePosition); + if (equalsSignPosition == std::string::npos) + { + // Skip lines with no equals sign + std::cerr << "MaterialLoader::load(): Invalid variable on line " << lineNumber << " in \"" << filename << "\"" << std::endl; + continue; + } - if (leftParenthesisPosition == std::string::npos || rightParenthesisPosition == std::string::npos) + // Find position of first character in variable type + std::size_t variableTypePosition = line.find_first_not_of(whitespace, equalsSignPosition + 1); + if (variableTypePosition == std::string::npos) { - invalid = true; - break; + // Skip lines with no variable type definition + std::cerr << "MaterialLoader::load(): Invalid variable on line " << lineNumber << " in \"" << filename << "\"" << std::endl; + continue; } - currentPosition = leftParenthesisPosition + 1; - std::size_t argumentCount = std::count(line.begin() + leftParenthesisPosition + 1, line.begin() + rightParenthesisPosition, ',') + 1; - std::vector arguments; - for (std::size_t j = 0; j < argumentCount; ++j) + // Count parentheses + std::size_t leftParenthesisCount = std::count(line.begin() + variableNamePosition, line.end(), '('); + std::size_t rightParenthesisCount = std::count(line.begin() + variableNamePosition, line.end(), ')'); + if (leftParenthesisCount != rightParenthesisCount || leftParenthesisCount == 0) { - std::size_t argumentStart = line.find_first_not_of(whitespace, currentPosition); - std::size_t argumentEnd = line.find_first_of(" \t,)", argumentStart + 1); + // Skip lines with invalid number of parentheses + std::cerr << "MaterialLoader::load(): Invalid variable on line " << lineNumber << " in \"" << filename << "\"" << std::endl; + continue; + } + + std::string variableName = line.substr(variableNamePosition, line.find_first_of(" \t=", variableNamePosition) - variableNamePosition); + std::string variableType = line.substr(variableTypePosition, line.find_first_of(" \t[(", variableTypePosition) - variableTypePosition); + std::size_t elementCount = leftParenthesisCount; + + std::size_t currentPosition = variableTypePosition; + std::vector> elements; + bool invalid = false; + for (std::size_t i = 0; i < elementCount; ++i) + { + std::size_t leftParenthesisPosition = line.find_first_of("(", currentPosition); + std::size_t rightParenthesisPosition = line.find_first_of(")", leftParenthesisPosition + 1); - if (argumentStart == std::string::npos || argumentEnd == std::string::npos) + if (leftParenthesisPosition == std::string::npos || rightParenthesisPosition == std::string::npos) { - // Unable to parse argument invalid = true; break; } - std::string argument = line.substr(argumentStart, argumentEnd - argumentStart); - arguments.push_back(argument); + currentPosition = leftParenthesisPosition + 1; + std::size_t argumentCount = std::count(line.begin() + leftParenthesisPosition + 1, line.begin() + rightParenthesisPosition, ',') + 1; + std::vector arguments; + for (std::size_t j = 0; j < argumentCount; ++j) + { + std::size_t argumentStart = line.find_first_not_of(whitespace, currentPosition); + std::size_t argumentEnd = line.find_first_of(" \t,)", argumentStart + 1); + + if (argumentStart == std::string::npos || argumentEnd == std::string::npos) + { + // Unable to parse argument + invalid = true; + break; + } + + std::string argument = line.substr(argumentStart, argumentEnd - argumentStart); + arguments.push_back(argument); + + currentPosition = argumentEnd + 1; + } - currentPosition = argumentEnd + 1; + if (invalid) + { + // Unable to parse element + break; + } + + elements.push_back(arguments); + + currentPosition = rightParenthesisPosition + 1; } if (invalid) { - // Unable to parse element - break; + // Unable to parse line + continue; } - elements.push_back(arguments); - - currentPosition = rightParenthesisPosition + 1; - } - - if (invalid) - { - // Unable to parse line - continue; - } - - if (variableType == "int") - { - ShaderInt* variable = material->addVariable(variableName, elements.size()); - loadShaderInt(variable, elements); - } - else if (variableType == "float") - { - ShaderFloat* variable = material->addVariable(variableName, elements.size()); - loadShaderFloat(variable, elements); - } - else if (variableType == "vec2") - { - ShaderVector2* variable = material->addVariable(variableName, elements.size()); - loadShaderVector2(variable, elements); - } - else if (variableType == "vec3") - { - ShaderVector3* variable = material->addVariable(variableName, elements.size()); - loadShaderVector3(variable, elements); - } - else if (variableType == "vec4") - { - ShaderVector4* variable = material->addVariable(variableName, elements.size()); - loadShaderVector4(variable, elements); - } - else if (variableType == "mat3") - { - ShaderMatrix3* variable = material->addVariable(variableName, elements.size()); - loadShaderMatrix3(variable, elements); - } - else if (variableType == "mat4") - { - ShaderMatrix4* variable = material->addVariable(variableName, elements.size()); - loadShaderMatrix4(variable, elements); - } - else if (variableType == "texture") - { - ShaderTexture2D* variable = material->addVariable(variableName, elements.size()); - loadShaderTexture2D(variable, elements); + if (variableType == "int") + { + ShaderInt* variable = material->addVariable(variableName, elements.size()); + loadShaderInt(variable, elements); + } + else if (variableType == "float") + { + ShaderFloat* variable = material->addVariable(variableName, elements.size()); + loadShaderFloat(variable, elements); + } + else if (variableType == "vec2") + { + ShaderVector2* variable = material->addVariable(variableName, elements.size()); + loadShaderVector2(variable, elements); + } + else if (variableType == "vec3") + { + ShaderVector3* variable = material->addVariable(variableName, elements.size()); + loadShaderVector3(variable, elements); + } + else if (variableType == "vec4") + { + ShaderVector4* variable = material->addVariable(variableName, elements.size()); + loadShaderVector4(variable, elements); + } + else if (variableType == "mat3") + { + ShaderMatrix3* variable = material->addVariable(variableName, elements.size()); + loadShaderMatrix3(variable, elements); + } + else if (variableType == "mat4") + { + ShaderMatrix4* variable = material->addVariable(variableName, elements.size()); + loadShaderMatrix4(variable, elements); + } + else if (variableType == "texture") + { + ShaderTexture2D* variable = material->addVariable(variableName, elements.size()); + loadShaderTexture2D(variable, elements); + } + else if (variableType == "textureCube") + { + ShaderTextureCube* variable = material->addVariable(variableName, elements.size()); + loadShaderTextureCube(variable, elements); + } } - else if (variableType == "textureCube") + else { - ShaderTextureCube* variable = material->addVariable(variableName, elements.size()); - loadShaderTextureCube(variable, elements); + if (command[0] == '#') + { + // Skip comments + continue; + } + + // Invalid command + std::cerr << "MaterialLoader::load(): Invalid command \"" << command << "\" on line " << lineNumber << " in \"" << filename << "\"" << std::endl; } } @@ -235,6 +328,30 @@ Material* MaterialLoader::load(const std::string& filename) return material; } +Shader* MaterialLoader::loadShader(const std::string& filename) +{ + auto it = shaderCache.find(filename); + if (it != shaderCache.end()) + { + return it->second; + } + + std::string fullFilename = std::string("data/shaders/") + filename; + + // Load shader + Shader* shader = new Shader(); + if (!shader->loadSource(fullFilename)) + { + delete shader; + return nullptr; + } + + // Add shader to cache + shaderCache[filename] = shader; + + return shader; +} + Texture2D* MaterialLoader::loadTexture2D(const std::string& filename) { // Check if texture exists in cache diff --git a/src/render-passes.cpp b/src/render-passes.cpp index e26ac9f..4aa10cc 100644 --- a/src/render-passes.cpp +++ b/src/render-passes.cpp @@ -18,6 +18,7 @@ */ #include "render-passes.hpp" +#include "configuration.hpp" #include "ui/ui.hpp" #include #include @@ -386,27 +387,14 @@ void ShadowMapRenderPass::render(RenderContext* renderContext) // Render operations for (const RenderOperation& operation: *operations) { - // Skip render operations with unsupported materials - /* - if (operation.material->getMaterialFormatID() != static_cast(MaterialFormat::PHYSICAL)) + // Skip operations with no materials and materials with no shadows + if (operation.material == nullptr || (operation.material->getFlags() & MATERIAL_FLAG_DISABLE_SHADOW_CASTING)) { continue; } - */ - - // Skip non shadow casters - /* - const PhysicalMaterial* material = static_cast(operation.material); - if (!material->shadowCaster) - { - continue; - } - */ - // TODO: Perform culling for subfrustums - // Select permutation std::uint32_t targetPermutation = (operation.pose != nullptr) ? skinnedPermutation : unskinnedPermutation; if (permutation != targetPermutation) @@ -585,8 +573,7 @@ void LightingRenderPass::render(RenderContext* renderContext) // Enable alpha blending //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_BLEND); - + glDisable(GL_BLEND); Vector4 splitDistances; @@ -658,19 +645,23 @@ void LightingRenderPass::render(RenderContext* renderContext) GLuint boundVAO = 0; // Sort operations - //operations->sort(RenderOpCompare()); - + operations->sort(RenderOpCompare()); + // Render operations for (const RenderOperation& operation: *operations) { - /* - bool hasTranslucency = material->flags & (unsigned int)PhysicalMaterial::Flags::TRANSLUCENT; + // Skip operations without materials + if (!operation.material) + { + continue; + } + + bool hasTranslucency = operation.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT; if (hasTranslucency && !blending) { glEnable(GL_BLEND); blending = true; } - */ // Select permutation std::uint32_t targetPermutation = (operation.pose != nullptr) ? skinnedPermutation : unskinnedPermutation; @@ -738,10 +729,6 @@ void LightingRenderPass::render(RenderContext* renderContext) } operation.material->upload(); } - else - { - //std::cerr << "NULL MATERIAL!!!" << std::endl; - } if (boundVAO != operation.vao) { @@ -758,31 +745,21 @@ void LightingRenderPass::render(RenderContext* renderContext) bool LightingRenderPass::RenderOpCompare::operator()(const RenderOperation& opA, const RenderOperation& opB) const { - /* - // Skip render operations with unsupported materials - if (opA.material->getMaterialFormatID() != static_cast(MaterialFormat::PHYSICAL)) - { + if (!opA.material) return false; - } - else if (opB.material->getMaterialFormatID() != static_cast(MaterialFormat::PHYSICAL)) - { + else if (!opB.material); return true; - } - - // Cast materials - const PhysicalMaterial* materialA = static_cast(opA.material); - const PhysicalMaterial* materialB = static_cast(opB.material); // Determine transparency - bool transparentA = materialA->flags & (unsigned int)PhysicalMaterial::Flags::TRANSLUCENT; - bool transparentB = materialB->flags & (unsigned int)PhysicalMaterial::Flags::TRANSLUCENT; + bool transparentA = opA.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT; + bool transparentB = opB.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT; if (transparentA) { if (transparentB) { // A and B are both transparent, render back to front - return (opA.depth <= opB.depth); + return (opA.depth >= opB.depth); } else { @@ -799,15 +776,25 @@ bool LightingRenderPass::RenderOpCompare::operator()(const RenderOperation& opA, } else { - // A and B are both opaque, sort by material - return (opA.material < opB.material); - - // A and B are both opaque, render front to back - //return (opA.depth > opB.depth); + // A and B are both opaque + if (opA.material->getShader() == opB.material->getShader()) + { + // A and B have the same shader + if (opA.vao == opB.vao) + { + // A and B have the same VAO, sort by depth + return (opA.depth < opB.depth); + } + else + { + // Sort by VAO + return (opA.vao < opB.vao); + } + } } } - */ + // A and B are both opaque and have different shaders, sort by shader return (opA.material->getShader() < opB.material->getShader()); }