LumixEngine/src/graphics/material.cpp

476 lines
12 KiB
C++
Raw Normal View History

2014-06-16 21:18:15 +02:00
#include "graphics/material.h"
2014-09-07 01:10:24 +02:00
#include "core/crc32.h"
2014-06-16 21:18:15 +02:00
#include "core/fs/file_system.h"
#include "core/fs/ifile.h"
#include "core/json_serializer.h"
#include "core/log.h"
#include "core/path_utils.h"
#include "core/profiler.h"
2014-06-16 21:18:15 +02:00
#include "core/resource_manager.h"
#include "core/resource_manager_base.h"
2014-06-21 19:26:27 +02:00
#include "core/timer.h"
2014-06-16 21:18:15 +02:00
#include "graphics/frame_buffer.h"
#include "graphics/pipeline.h"
#include "graphics/renderer.h"
#include "graphics/shader.h"
#include "graphics/texture.h"
namespace Lumix
{
2014-09-07 01:10:24 +02:00
static const uint32_t SHADOWMAP_HASH = crc32("shadowmap");
2014-06-16 21:18:15 +02:00
Material::~Material()
{
ASSERT(isEmpty());
}
2014-09-07 17:29:40 +02:00
void Material::apply(Renderer& renderer, PipelineInstance& pipeline) const
2014-06-16 21:18:15 +02:00
{
2014-09-07 01:10:24 +02:00
PROFILE_FUNCTION();
2014-06-16 21:18:15 +02:00
if(getState() == State::READY)
{
2014-10-06 23:58:33 +02:00
renderer.applyShader(*m_shader, m_shader_combination);
switch (m_depth_func)
{
case DepthFunc::LEQUAL:
glDepthFunc(GL_LEQUAL);
break;
default:
glDepthFunc(GL_LESS);
break;
}
2014-06-16 21:18:15 +02:00
if (m_is_backface_culling)
{
glEnable(GL_CULL_FACE);
}
else
{
glDisable(GL_CULL_FACE);
}
for (int i = 0, c = m_textures.size(); i < c; ++i)
{
2014-09-27 22:15:14 +02:00
m_textures[i].m_texture->apply(i);
2014-06-16 21:18:15 +02:00
}
renderer.enableAlphaToCoverage(m_is_alpha_to_coverage);
2014-06-16 21:18:15 +02:00
renderer.enableZTest(m_is_z_test);
for (int i = 0, c = m_uniforms.size(); i < c; ++i)
{
const Uniform& uniform = m_uniforms[i];
switch (uniform.m_type)
{
case Uniform::FLOAT:
renderer.setUniform(*m_shader, uniform.m_name, uniform.m_name_hash, uniform.m_float);
2014-06-16 21:18:15 +02:00
break;
case Uniform::INT:
renderer.setUniform(*m_shader, uniform.m_name, uniform.m_name_hash, uniform.m_int);
2014-06-16 21:18:15 +02:00
break;
case Uniform::MATRIX:
renderer.setUniform(*m_shader, uniform.m_name, uniform.m_name_hash, uniform.m_matrix);
2014-06-16 21:18:15 +02:00
break;
2014-06-21 19:26:27 +02:00
case Uniform::TIME:
renderer.setUniform(*m_shader, uniform.m_name, uniform.m_name_hash, pipeline.getScene()->getTimer()->getTimeSinceStart());
2014-06-21 19:26:27 +02:00
break;
2014-06-16 21:18:15 +02:00
default:
ASSERT(false);
break;
}
}
if (m_shader->isShadowmapRequired())
{
glActiveTexture(GL_TEXTURE0 + m_textures.size());
glBindTexture(GL_TEXTURE_2D, pipeline.getShadowmapFramebuffer()->getDepthTexture());
renderer.setUniform(*m_shader, "shadowmap", SHADOWMAP_HASH, m_textures.size());
2014-06-16 21:18:15 +02:00
}
}
}
2014-10-06 23:58:33 +02:00
void Material::updateShaderCombination()
{
static const int MAX_DEFINES_LENGTH = 1024;
char defines[MAX_DEFINES_LENGTH];
copyString(defines, MAX_DEFINES_LENGTH, m_is_alpha_cutout ? "#define ALPHA_CUTOUT\n" : "" );
catCString(defines, MAX_DEFINES_LENGTH, m_is_shadow_receiver ? "#define SHADOW_RECEIVER\n" : "" );
m_shader_combination = crc32(defines);
if(m_shader && m_shader->isReady())
{
m_shader->createCombination(defines);
}
}
2014-06-16 21:18:15 +02:00
void Material::doUnload(void)
{
2014-10-22 22:11:36 +02:00
setShader(NULL);
2014-06-16 21:18:15 +02:00
ResourceManagerBase* texture_manager = m_resource_manager.get(ResourceManager::TEXTURE);
for(int i = 0; i < m_textures.size(); i++)
{
2014-09-27 22:15:14 +02:00
removeDependency(*m_textures[i].m_texture);
texture_manager->unload(*m_textures[i].m_texture);
2014-06-16 21:18:15 +02:00
}
m_textures.clear();
m_size = 0;
onEmpty();
}
bool Material::save(JsonSerializer& serializer)
2014-06-16 21:18:15 +02:00
{
serializer.beginObject();
serializer.serialize("shader", m_shader->getPath().c_str());
for (int i = 0; i < m_textures.size(); ++i)
{
char path[LUMIX_MAX_PATH];
2014-09-27 22:15:14 +02:00
PathUtils::getFilename(path, LUMIX_MAX_PATH, m_textures[i].m_texture->getPath().c_str());
2014-09-28 17:29:39 +02:00
serializer.beginObject("texture");
serializer.serialize("source", path);
serializer.serialize("keep_data", m_textures[i].m_keep_data);
if (m_textures[i].m_uniform[0] != '\0')
{
serializer.serialize("uniform", m_textures[i].m_uniform);
}
serializer.endObject();
}
serializer.beginArray("uniforms");
for (int i = 0; i < m_uniforms.size(); ++i)
{
serializer.beginObject();
serializer.serialize("name", m_uniforms[i].m_name);
serializer.serialize("is_editable", m_uniforms[i].m_is_editable);
2014-09-28 17:29:39 +02:00
switch (m_uniforms[i].m_type)
{
case Uniform::FLOAT:
serializer.serialize("float_value", m_uniforms[i].m_float);
break;
case Uniform::TIME:
serializer.serialize("time", m_uniforms[i].m_float);
break;
case Uniform::INT:
serializer.serialize("int_value", m_uniforms[i].m_int);
break;
case Uniform::MATRIX:
serializer.beginArray("matrix_value");
for (int j = 0; j < 16; ++j)
{
serializer.serializeArrayItem(m_uniforms[i].m_matrix[j]);
}
serializer.endArray();
break;
default:
ASSERT(false);
break;
}
serializer.endObject();
2014-06-16 21:18:15 +02:00
}
2014-09-28 17:29:39 +02:00
serializer.endArray();
2014-09-03 22:15:20 +02:00
serializer.serialize("alpha_to_coverage", m_is_alpha_to_coverage);
serializer.serialize("backface_culling", m_is_backface_culling);
2014-10-06 23:58:33 +02:00
serializer.serialize("alpha_cutout", m_is_alpha_cutout);
serializer.serialize("shadow_receiver", m_is_shadow_receiver);
2014-09-03 22:15:20 +02:00
serializer.serialize("z_test", m_is_z_test);
2014-06-16 21:18:15 +02:00
serializer.endObject();
return false;
}
void Material::deserializeUniforms(JsonSerializer& serializer)
2014-06-16 21:18:15 +02:00
{
serializer.deserializeArrayBegin();
while (!serializer.isArrayEnd())
{
Uniform& uniform = m_uniforms.pushEmpty();
serializer.nextArrayItem();
serializer.deserializeObjectBegin();
char label[256];
while (!serializer.isObjectEnd())
{
serializer.deserializeLabel(label, 255);
if (strcmp(label, "is_editable") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(uniform.m_is_editable, false);
}
else if (strcmp(label, "name") == 0)
2014-06-16 21:18:15 +02:00
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(uniform.m_name, Uniform::MAX_NAME_LENGTH, "");
2014-09-07 01:10:24 +02:00
uniform.m_name_hash = crc32(uniform.m_name);
2014-06-16 21:18:15 +02:00
}
else if (strcmp(label, "int_value") == 0)
{
uniform.m_type = Uniform::INT;
2014-11-15 14:33:55 +01:00
serializer.deserialize(uniform.m_int, 0);
2014-06-16 21:18:15 +02:00
}
else if (strcmp(label, "float_value") == 0)
{
uniform.m_type = Uniform::FLOAT;
2014-11-15 14:33:55 +01:00
serializer.deserialize(uniform.m_float, 0);
2014-06-16 21:18:15 +02:00
}
else if (strcmp(label, "matrix_value") == 0)
{
uniform.m_type = Uniform::MATRIX;
serializer.deserializeArrayBegin();
for (int i = 0; i < 16; ++i)
{
serializer.deserializeArrayItem(uniform.m_matrix[i], 0);
2014-06-16 21:18:15 +02:00
ASSERT(i == 15 || !serializer.isArrayEnd());
}
serializer.deserializeArrayEnd();
}
2014-06-21 19:26:27 +02:00
else if (strcmp(label, "time") == 0)
{
uniform.m_type = Uniform::TIME;
2014-11-15 14:33:55 +01:00
serializer.deserialize(uniform.m_float, 0);
2014-06-21 19:26:27 +02:00
}
2014-06-16 21:18:15 +02:00
else
{
ASSERT(false);
}
}
serializer.deserializeObjectEnd();
}
serializer.deserializeArrayEnd();
}
void Material::removeTexture(int i)
{
2014-09-27 22:15:14 +02:00
if (m_textures[i].m_texture)
2014-06-16 21:18:15 +02:00
{
2014-09-27 22:15:14 +02:00
removeDependency(*m_textures[i].m_texture);
m_resource_manager.get(ResourceManager::TEXTURE)->unload(*m_textures[i].m_texture);
2014-06-16 21:18:15 +02:00
}
m_textures.erase(i);
}
2014-09-27 22:15:14 +02:00
Texture* Material::getTextureByUniform(const char* uniform) const
{
for (int i = 0; i < m_textures.size(); ++i)
{
if (strcmp(m_textures[i].m_uniform, uniform) == 0)
{
return m_textures[i].m_texture;
}
}
return NULL;
}
2014-06-16 21:18:15 +02:00
void Material::setTexture(int i, Texture* texture)
{
2014-09-27 22:15:14 +02:00
if (m_textures[i].m_texture)
2014-06-16 21:18:15 +02:00
{
2014-09-27 22:15:14 +02:00
removeDependency(*m_textures[i].m_texture);
m_resource_manager.get(ResourceManager::TEXTURE)->unload(*m_textures[i].m_texture);
2014-06-16 21:18:15 +02:00
}
if (texture)
{
addDependency(*texture);
}
2014-09-27 22:15:14 +02:00
m_textures[i].m_texture = texture;
2014-06-16 21:18:15 +02:00
}
void Material::addTexture(Texture* texture)
{
if (texture)
{
addDependency(*texture);
}
2014-09-27 22:15:14 +02:00
m_textures.pushEmpty().m_texture = texture;
2014-06-16 21:18:15 +02:00
}
2014-10-06 23:58:33 +02:00
void Material::shaderLoaded(Resource::State, Resource::State)
{
updateShaderCombination();
}
2014-06-16 21:18:15 +02:00
void Material::setShader(Shader* shader)
{
if (m_shader)
{
removeDependency(*m_shader);
m_resource_manager.get(ResourceManager::SHADER)->unload(*m_shader);
}
m_shader = shader;
if (m_shader)
{
addDependency(*m_shader);
2014-11-28 18:46:13 +01:00
m_shader->onLoaded<Material, &Material::shaderLoaded>(this);
2014-06-16 21:18:15 +02:00
}
}
bool Material::deserializeTexture(JsonSerializer& serializer, const char* material_dir)
2014-09-27 22:15:14 +02:00
{
char path[LUMIX_MAX_PATH];
TextureInfo& info = m_textures.pushEmpty();
serializer.deserializeObjectBegin();
char label[256];
bool data_kept = false;
while (!serializer.isObjectEnd())
{
serializer.deserializeLabel(label, sizeof(label));
if (strcmp(label, "source") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(path, MAX_PATH, "");
2014-09-27 22:15:14 +02:00
if (path[0] != '\0')
{
2014-11-30 00:07:35 +01:00
char texture_path[LUMIX_MAX_PATH];
copyString(texture_path, sizeof(texture_path), material_dir);
catCString(texture_path, sizeof(texture_path), path);
info.m_texture = static_cast<Texture*>(m_resource_manager.get(ResourceManager::TEXTURE)->load(Path(texture_path)));
2014-09-27 22:15:14 +02:00
addDependency(*info.m_texture);
if (info.m_keep_data)
{
if (info.m_texture->isReady() && !info.m_texture->getData())
{
g_log_error.log("Renderer") << "Cannot keep data for texture " << m_path.c_str() << "because the texture has already been loaded.";
return false;
}
if (!data_kept)
{
info.m_texture->addDataReference();
data_kept = true;
}
}
}
}
else if (strcmp("uniform", label) == 0)
{
Uniform& uniform = m_uniforms.pushEmpty();
2014-11-15 14:33:55 +01:00
serializer.deserialize(uniform.m_name, Uniform::MAX_NAME_LENGTH, "");
2014-09-27 22:15:14 +02:00
copyString(info.m_uniform, sizeof(info.m_uniform), uniform.m_name);
uniform.m_name_hash = crc32(uniform.m_name);
uniform.m_type = Uniform::INT;
uniform.m_int = info.m_texture ? m_textures.size() - 1 : m_textures.size();
}
else if (strcmp("keep_data", label) == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(info.m_keep_data, false);
2014-09-27 22:15:14 +02:00
if (info.m_keep_data && info.m_texture)
{
if (info.m_texture->isReady() && !info.m_texture->getData())
{
g_log_error.log("Renderer") << "Cannot keep data for texture " << info.m_texture->getPath().c_str() << ", it's already loaded.";
return false;
}
if (!data_kept)
{
data_kept = true;
info.m_texture->addDataReference();
}
}
}
else
{
g_log_error.log("Renderer") << "Unknown data \"" << label << "\" in material " << m_path.c_str();
return false;
}
}
serializer.deserializeObjectEnd();
return true;
}
2014-06-16 21:18:15 +02:00
void Material::loaded(FS::IFile* file, bool success, FS::FileSystem& fs)
{
PROFILE_FUNCTION();
2014-06-16 21:18:15 +02:00
if(success)
{
JsonSerializer serializer(m_allocator, *file, JsonSerializer::READ, m_path.c_str());
2014-06-16 21:18:15 +02:00
serializer.deserializeObjectBegin();
2014-06-25 23:20:33 +02:00
char path[LUMIX_MAX_PATH];
2014-06-16 21:18:15 +02:00
char label[256];
2014-06-25 23:20:33 +02:00
char material_dir[LUMIX_MAX_PATH];
PathUtils::getDir(material_dir, LUMIX_MAX_PATH, m_path.c_str());
while (!serializer.isObjectEnd())
2014-06-16 21:18:15 +02:00
{
serializer.deserializeLabel(label, 255);
if (strcmp(label, "uniforms") == 0)
{
deserializeUniforms(serializer);
}
2014-09-27 22:15:14 +02:00
else if (strcmp(label, "texture") == 0)
2014-06-16 21:18:15 +02:00
{
2014-09-27 22:15:14 +02:00
if (!deserializeTexture(serializer, material_dir))
2014-06-16 21:18:15 +02:00
{
2014-09-27 22:15:14 +02:00
onFailure();
fs.close(file);
return;
2014-06-16 21:18:15 +02:00
}
2014-09-27 22:15:14 +02:00
2014-06-16 21:18:15 +02:00
}
2014-10-06 23:58:33 +02:00
else if (strcmp(label, "alpha_cutout") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(m_is_alpha_cutout, false);
2014-10-06 23:58:33 +02:00
}
else if (strcmp(label, "shadow_receiver") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(m_is_shadow_receiver, true);
2014-10-06 23:58:33 +02:00
}
else if (strcmp(label, "alpha_to_coverage") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(m_is_alpha_to_coverage, false);
}
2014-06-16 21:18:15 +02:00
else if (strcmp(label, "shader") == 0)
{
2014-11-30 00:07:35 +01:00
serializer.deserialize(path, LUMIX_MAX_PATH, "");
setShader(static_cast<Shader*>(m_resource_manager.get(ResourceManager::SHADER)->load(Path(path))));
2014-06-16 21:18:15 +02:00
}
else if (strcmp(label, "z_test") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(m_is_z_test, true);
2014-06-16 21:18:15 +02:00
}
else if (strcmp(label, "backface_culling") == 0)
{
2014-11-15 14:33:55 +01:00
serializer.deserialize(m_is_backface_culling, true);
2014-06-16 21:18:15 +02:00
}
else if (strcmp(label, "depth_func") == 0)
{
char tmp[30];
2014-11-15 14:33:55 +01:00
serializer.deserialize(tmp, 30, "lequal");
if (strcmp(tmp, "lequal") == 0)
{
m_depth_func = DepthFunc::LEQUAL;
}
else if (strcmp(tmp, "less") == 0)
{
m_depth_func = DepthFunc::LESS;
}
else
{
g_log_warning.log("Renderer") << "Unknown depth function " << tmp << " in material " << m_path.c_str();
}
}
2014-06-16 21:18:15 +02:00
else
{
2014-06-29 11:45:32 +02:00
g_log_warning.log("renderer") << "Unknown parameter " << label << " in material " << m_path.c_str();
2014-06-16 21:18:15 +02:00
}
}
serializer.deserializeObjectEnd();
if (!m_shader)
{
2014-06-29 11:45:32 +02:00
g_log_error.log("renderer") << "Material " << m_path.c_str() << " without a shader";
2014-06-16 21:18:15 +02:00
onFailure();
fs.close(file);
return;
}
m_size = file->size();
decrementDepCount();
}
else
{
2014-06-29 11:45:32 +02:00
g_log_info.log("renderer") << "Error loading material " << m_path.c_str();
2014-06-16 21:18:15 +02:00
onFailure();
}
fs.close(file);
}
} // ~namespace Lumix