From 5f8669bc1effe67c41f5d6049408795e7faf3415 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 17 Apr 2021 05:20:38 +0800 Subject: [PATCH] Make material loader support new JSON-based material format --- src/ecs/systems/terrain-system.cpp | 2 +- src/game/states/play-state.cpp | 6 +- src/resources/material-loader.cpp | 559 +++++++++++++---------------- 3 files changed, 253 insertions(+), 314 deletions(-) diff --git a/src/ecs/systems/terrain-system.cpp b/src/ecs/systems/terrain-system.cpp index 7261fa8..62d2f02 100644 --- a/src/ecs/systems/terrain-system.cpp +++ b/src/ecs/systems/terrain-system.cpp @@ -105,7 +105,7 @@ model* terrain_system::generate_terrain_model(geom::mesh* terrain_mesh) // Create model group model_group* model_group = terrain_model->add_group("terrain"); - model_group->set_material(resource_manager->load("grassland-terrain.mtl")); + model_group->set_material(resource_manager->load("forest-terrain.mtl")); model_group->set_drawing_mode(gl::drawing_mode::triangles); model_group->set_start_index(0); model_group->set_index_count(terrain_mesh->get_faces().size() * 3); diff --git a/src/game/states/play-state.cpp b/src/game/states/play-state.cpp index b653f6c..1a38e4d 100644 --- a/src/game/states/play-state.cpp +++ b/src/game/states/play-state.cpp @@ -96,7 +96,7 @@ void play_state_enter(game_context* ctx) sky_pass->set_zenith_color({0.0f, 0.0f, 0.0f}); sky_pass->set_time_of_day(0.0f); sky_pass->set_julian_day(0.0f); - sky_pass->set_observer_location(ctx->biome->location[0], ctx->biome->location[1], 0.0f); + sky_pass->set_observer_location(0.0f, 0.0f, 0.0f); sky_pass->set_moon_angular_radius(math::radians(1.0f)); sky_pass->set_sun_angular_radius(math::radians(1.0f)); @@ -104,13 +104,13 @@ void play_state_enter(game_context* ctx) scene::ambient_light* ambient = new scene::ambient_light(); ambient->set_color({1, 1, 1}); - ambient->set_intensity(0.1f); + ambient->set_intensity(10.0f); ambient->update_tweens(); ctx->overworld_scene->add_object(ambient); scene::directional_light* sun = new scene::directional_light(); sun->set_color({1, 1, 1}); - sun->set_intensity(4.0f); + sun->set_intensity(750.0f); sun->look_at({0, 1, 1}, {0, 0, 0}, {0, 1, 0}); sun->update_tweens(); ctx->overworld_scene->add_object(sun); diff --git a/src/resources/material-loader.cpp b/src/resources/material-loader.cpp index 16a816a..da04dbe 100644 --- a/src/resources/material-loader.cpp +++ b/src/resources/material-loader.cpp @@ -17,355 +17,294 @@ * along with Antkeeper source code. If not, see . */ -#include "renderer/material.hpp" #include "resource-loader.hpp" #include "resource-manager.hpp" -#include "gl/shader-variable-type.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "gl/texture-2d.hpp" -#include "utility/fundamental-types.hpp" -#include "string-table.hpp" -#include -#include -#include - -static bool load_bool_property(material* material, const string_table_row& row, int vector_size, int array_size) -{ - if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) - { - return false; - } - - std::size_t size = array_size * vector_size; - bool* values = new bool[size]; - for (std::size_t i = 0; i < size; ++i) - { - values[i] = (std::stoi(row[4 + i]) != 0); - } - - if (vector_size == 1) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, values, array_size); - } - else if (vector_size == 2) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 3) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 4) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - - delete[] values; - - return true; -} - -static bool load_int_property(material* material, const string_table_row& row, int vector_size, int array_size) +#include "renderer/material.hpp" +#include "renderer/material-flags.hpp" +#include +#include +#include +#include +#include + +template +static bool read_value(T* value, const nlohmann::json& json, const std::string& name) { - if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) - { - return false; - } - - std::size_t size = array_size * vector_size; - int* values = new int[size]; - for (std::size_t i = 0; i < size; ++i) - { - values[i] = std::stoi(row[4 + i]); - } - - if (vector_size == 1) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, values, array_size); - } - else if (vector_size == 2) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 3) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 4) + if (auto element = json.find(name); element != json.end()) { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); + *value = element.value().get(); + return true; } - - delete[] values; - - return true; + + return false; } -static bool load_uint_property(material* material, const string_table_row& row, int vector_size, int array_size) +static bool load_texture_2d_property(resource_manager* resource_manager, material* material, const std::string& name, const nlohmann::json& json) { - if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) - { - return false; - } - - std::size_t size = array_size * vector_size; - unsigned int* values = new unsigned int[size]; - for (std::size_t i = 0; i < size; ++i) - { - values[i] = static_cast(std::stoul(row[4 + i])); - } - - if (vector_size == 1) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, values, array_size); - } - else if (vector_size == 2) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 3) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + material_property* property = material->add_property(name, array_size); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + std::string filename = element.get(); + const gl::texture_2d* texture = resource_manager->load(filename); + property->set_value(i++, texture); + } } - else if (vector_size == 4) + else { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); + // Create property + material_property* property = material->add_property(name); + + // Load texture + std::string filename = json.get(); + const gl::texture_2d* texture = resource_manager->load(filename); + property->set_value(texture); } - - delete[] values; - + return true; } -static bool load_float_property(material* material, const string_table_row& row, int vector_size, int array_size) +static bool load_texture_cube_property(resource_manager* resource_manager, material* material, const std::string& name, const nlohmann::json& json) { - if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) - { - return false; - } - - std::size_t size = array_size * vector_size; - float* values = new float[size]; - for (std::size_t i = 0; i < size; ++i) - { - values[i] = static_cast(std::stod(row[4 + i])); - } - - if (vector_size == 1) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, values, array_size); - } - else if (vector_size == 2) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 3) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (vector_size == 4) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - - delete[] values; - - return true; + return false; } -static bool load_float_matrix_property(material* material, const string_table_row& row, int matrix_columns, int matrix_rows, int array_size) +template +static bool load_scalar_property(material* material, const std::string& name, const nlohmann::json& json) { - int matrix_size = matrix_columns * matrix_rows; - - if (row.size() - 4 != matrix_size * array_size) - { - return false; - } - - std::size_t size = array_size * matrix_size; - float* values = new float[size]; - for (std::size_t i = 0; i < size; ++i) - { - values[i] = static_cast(std::stod(row[4 + i])); + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + material_property* property = material->add_property(name, array_size); + + // Set property values + std::size_t i = 0; + for (const auto& element: json) + property->set_value(i++, element.get()); + } + else + { + // Create property + material_property* property = material->add_property(name); + + // Set property value + property->set_value(json.get()); } - - if (matrix_size == 2*2) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (matrix_size == 3*3) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - else if (matrix_size == 4*4) - { - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, reinterpret_cast(values), array_size); - } - - delete[] values; - + return true; } -static bool load_texture_2d_property(material* material, const string_table_row& row, resource_manager* resource_manager, int array_size) +template +static bool load_vector_property(material* material, const std::string& name, std::size_t vector_size, const nlohmann::json& json) { - if (row.size() - 4 != array_size * 1) - { - return false; + // If JSON element is an array of arrays + if (json.is_array() && json.begin().value().is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + material_property* property = material->add_property(name, array_size); + + // For each vector in the array + std::size_t i = 0; + for (const auto& vector_element: json) + { + // Read vector elements + T value; + std::size_t j = 0; + for (const auto& value_element: vector_element) + value[j++] = value_element.get(); + + // Set property values + property->set_value(i++, value); + } } - - const gl::texture_2d** values = new const gl::texture_2d*[array_size]; - for (std::size_t i = 0; i < array_size; ++i) + else { - values[i] = resource_manager->load(row[4 + i]); + // Create property + material_property* property = material->add_property(name); + + // Read vector elements + T value; + std::size_t i = 0; + for (const auto& value_element: json) + value[i++] = value_element.get(); + + // Set property values + property->set_value(value); } - material_property* property = material->add_property(row[1], array_size); - property->set_values(0, values, array_size); - - delete[] values; - return true; } -static bool load_texture_cube_property(material* material, const string_table_row& row, resource_manager* resource_manager, int array_size) -{ - return false; -} - -static bool load_material_property(material* material, const string_table_row& row, resource_manager* resource_manager) -{ - // Ensure row has at least five columns - if (row.size() < 5) - { - return false; - } - - const std::string& name = row[1]; - if (name.empty()) - { - return false; - } - - const std::string& type = row[2]; - if (type.empty()) - { - return false; - } - - int vector_size = 1; - if (std::isdigit(type.back())) - { - vector_size = std::stoi(type.substr(type.size() - 1, 1)); - } - - int matrix_columns = 0; - int matrix_rows = 0; - if (type[type.size() - 2] == 'x' && std::isdigit(type[type.size() - 3]) && std::isdigit(type.back())) - { - matrix_columns = std::stoi(type.substr(type.size() - 3, 1)); - matrix_rows = std::stoi(type.substr(type.size() - 1, 1)); - } - - int array_size = std::stoi(row[3]); - if (array_size <= 0) - { - return false; - } - - if (type == "bool" || type == "bool2" || type == "bool3" || type == "bool4") - { - return load_bool_property(material, row, vector_size, array_size); - } - else if (type == "int" || type == "int2" || type == "int3" || type == "int4") - { - return load_int_property(material, row, vector_size, array_size); - } - else if (type == "uint" || type == "uint2" || type == "uint3" || type == "uint4") - { - return load_uint_property(material, row, vector_size, array_size); - } - else if (type == "float" || type == "float2" || type == "float3" || type == "float4") - { - return load_float_property(material, row, vector_size, array_size); - } - else if (type == "float2x2" || type == "float3x3" || type == "float4x4") - { - return load_float_matrix_property(material, row, matrix_columns, matrix_rows, array_size); - } - else if (type == "texture_2d") - { - return load_texture_2d_property(material, row, resource_manager, array_size); - } - else if (type == "texture_cube") - { - return load_texture_cube_property(material, row, resource_manager, array_size); - } - - return false; -} - template <> material* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { - // Load string table from input stream - string_table* table = resource_loader::load(resource_manager, file); - - // Ensure table is not empty. - if (!table || table->empty()) + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + nlohmann::json json = nlohmann::json::parse(buffer); + + // Allocate material + material* material = new ::material(); + + // Read shader filename + std::string shader_filename; + if (read_value(&shader_filename, json, "shader")) { - delete table; - return nullptr; + // Load shader program + gl::shader_program* program = resource_manager->load(shader_filename); + material->set_shader_program(program); } - - // Allocate material - ::material* material = new ::material(); - - // Parse table rows - for (const string_table_row& row: *table) + + // Init material flags + std::uint32_t flags = 0; + + // Read blend mode + std::string blend_mode; + read_value(&blend_mode, json, "blend_mode"); + if (blend_mode == "alpha_blend") + flags |= MATERIAL_FLAG_TRANSLUCENT; + else + flags |= MATERIAL_FLAG_OPAQUE; + + // Read shadow mode + std::string shadow_mode; + read_value(&shadow_mode, json, "shadow_mode"); + if (shadow_mode == "none") + flags |= MATERIAL_FLAG_NOT_SHADOW_CASTER; + else + flags |= MATERIAL_FLAG_SHADOW_CASTER; + + // Read cull mode + std::string cull_mode; + read_value(&cull_mode, json, "cull_mode"); + if (cull_mode == "none") + flags |= MATERIAL_FLAG_FRONT_AND_BACK_FACES; + else if (cull_mode == "front") + flags |= MATERIAL_FLAG_BACK_FACES; + else + flags |= MATERIAL_FLAG_FRONT_FACES; + + // Set material flags + material->set_flags(flags); + + // Read material properties + if (auto properties_element = json.find("properties"); properties_element != json.end()) { - // Skip empty rows and comments - if (row.empty() || row[0].empty() || row[0][0] == '#') + for (const auto& property_element: properties_element.value()) { - continue; - } - - if (row[0] == "shader" && row.size() == 2) - { - gl::shader_program* program = resource_manager->load(row[1]); - material->set_shader_program(program); - } - else if (row[0] == "flags" && row.size() == 2) - { - std::uint32_t flags = std::stoi(row[1]); - material->set_flags(flags); - } - else if (row[0] == "property") - { - load_material_property(material, row, resource_manager); + // Read property name + std::string name; + if (!read_value(&name, property_element, "name")) + // Ignore nameless properties + continue; + + // Read property type + std::string type; + if (!read_value(&type, property_element, "type")) + // Ignore typeless properties + continue; + + // Find value element + auto value_element = property_element.find("value"); + if (value_element == property_element.end()) + // Ignore valueless properties + continue; + + // If property type is a 2D texture + if (type == "texture_2d") + { + load_texture_2d_property(resource_manager, material, name, value_element.value()); + } + // If property type is a cubic texture + else if (type == "texture_cube") + { + load_texture_cube_property(resource_manager, material, name, value_element.value()); + } + // If property type is a matrix + else if (type[type.size() - 2] == 'x' && + std::isdigit(type[type.size() - 3]) && + std::isdigit(type.back())) + { + std::size_t columns = std::stoul(type.substr(type.size() - 3, 1)); + std::size_t rows = std::stoul(type.substr(type.size() - 1, 1)); + //load_matrix_property(material, name, columns, rows, value_element.value()); + } + // If property type is a vector + else if (std::isdigit(type.back())) + { + std::size_t size = std::stoul(type.substr(type.size() - 1, 1)); + + if (type.find("float") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + else if (type.find("uint") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + else if (type.find("int") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + else if (type.find("bool") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + } + // If property type is a scalar + else + { + if (type.find("float") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + else if (type.find("uint") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + else if (type.find("int") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + else if (type.find("bool") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + } } } return material; } -