LumixEngine/src/graphics/material.cpp

503 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);
2015-05-15 22:38:34 +02:00
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);
}
2015-05-31 15:48:35 +02:00
for (int i = 0, c = m_texture_count; i < c; ++i)
2014-06-16 21:18:15 +02:00
{
2015-05-31 15:48:35 +02:00
if (m_textures[i])
2015-05-28 23:30:50 +02:00
{
2015-05-31 15:48:35 +02:00
m_textures[i]->apply(i);
2015-05-28 23:30:50 +02:00
}
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:
2015-01-29 23:58:30 +01:00
renderer.setUniform(*m_shader, uniform.m_name, uniform.m_name_hash, pipeline.getScene()->getTime());
2014-06-21 19:26:27 +02:00
break;
2014-06-16 21:18:15 +02:00
default:
ASSERT(false);
break;
}
}
}
}
2014-10-06 23:58:33 +02:00
void Material::updateShaderCombination()
{
2015-05-31 15:48:35 +02:00
if (isReady())
2014-10-06 23:58:33 +02:00
{
2015-05-31 15:48:35 +02:00
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" : "");
if (m_shader && m_shader->isReady())
{
for (int i = 0; i < m_shader->getTextureSlotCount(); ++i)
{
if (m_shader->getTextureSlot(i).m_define[0] != '\0' && m_textures[i])
{
catCString(defines, MAX_DEFINES_LENGTH, "#define ");
catCString(defines, MAX_DEFINES_LENGTH, m_shader->getTextureSlot(i).m_define);
catCString(defines, MAX_DEFINES_LENGTH, "\n");
}
}
m_shader->createCombination(defines);
}
m_shader_combination = crc32(defines);
2014-10-06 23:58:33 +02:00
}
}
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);
2015-05-31 15:48:35 +02:00
for (int i = 0; i < m_texture_count; i++)
2014-06-16 21:18:15 +02:00
{
2015-05-31 15:48:35 +02:00
if (m_textures[i])
2015-02-28 13:05:01 +01:00
{
2015-05-31 15:48:35 +02:00
removeDependency(*m_textures[i]);
texture_manager->unload(*m_textures[i]);
2015-02-28 13:05:01 +01:00
}
2014-06-16 21:18:15 +02:00
}
2015-05-31 15:48:35 +02:00
m_texture_count = 0;
2014-06-16 21:18:15 +02:00
m_size = 0;
onEmpty();
}
bool Material::save(JsonSerializer& serializer)
2014-06-16 21:18:15 +02:00
{
serializer.beginObject();
2015-05-28 23:30:50 +02:00
serializer.serialize("shader", m_shader ? m_shader->getPath().c_str() : "");
2015-05-31 15:48:35 +02:00
for (int i = 0; i < m_texture_count; ++i)
2014-06-16 21:18:15 +02:00
{
char path[LUMIX_MAX_PATH];
2015-05-31 15:48:35 +02:00
if (m_textures[i])
{
PathUtils::getFilename(path, LUMIX_MAX_PATH, m_textures[i]->getPath().c_str());
}
else
2014-09-28 17:29:39 +02:00
{
2015-05-31 15:48:35 +02:00
path[0] = '\0';
2014-09-28 17:29:39 +02:00
}
2015-05-31 15:48:35 +02:00
serializer.beginObject("texture");
serializer.serialize("source", path);
2014-09-28 17:29:39 +02:00
serializer.endObject();
}
serializer.beginArray("uniforms");
for (int i = 0; i < m_uniforms.size(); ++i)
{
serializer.beginObject();
serializer.serialize("name", m_uniforms[i].m_name);
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();
2015-05-31 15:48:35 +02:00
m_uniforms.clear();
2014-06-16 21:18:15 +02:00
while (!serializer.isArrayEnd())
{
Uniform& uniform = m_uniforms.pushEmpty();
serializer.nextArrayItem();
serializer.deserializeObjectBegin();
char label[256];
while (!serializer.isObjectEnd())
{
serializer.deserializeLabel(label, 255);
2015-05-28 23:30:50 +02:00
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
{
g_log_warning.log("material") << "Unknown label \"" << label << "\"";
2014-06-16 21:18:15 +02:00
}
}
serializer.deserializeObjectEnd();
}
serializer.deserializeArrayEnd();
}
2015-05-28 23:30:50 +02:00
2015-05-31 15:48:35 +02:00
void Material::setTexturePath(int i, const Path& path)
2014-06-16 21:18:15 +02:00
{
2015-05-31 15:48:35 +02:00
if (path.length() == 0)
2014-06-16 21:18:15 +02:00
{
2015-05-31 15:48:35 +02:00
setTexture(i, nullptr);
2014-06-16 21:18:15 +02:00
}
2015-05-31 15:48:35 +02:00
else
2014-09-27 22:15:14 +02:00
{
2015-05-31 15:48:35 +02:00
Texture* texture = static_cast<Texture*>(m_resource_manager.get(ResourceManager::TEXTURE)->load(path));
setTexture(i, texture);
2014-09-27 22:15:14 +02:00
}
2014-12-12 00:48:06 +01:00
}
2014-06-16 21:18:15 +02:00
void Material::setTexture(int i, Texture* texture)
{
2015-06-01 23:35:57 +02:00
Texture* old_texture = i < m_texture_count ? m_textures[i] : nullptr;
2014-06-16 21:18:15 +02:00
if (texture)
{
addDependency(*texture);
}
2015-05-31 15:48:35 +02:00
m_textures[i] = texture;
if (i >= m_texture_count)
2015-06-01 23:35:57 +02:00
{
m_texture_count = i + 1;
}
2015-05-28 23:30:50 +02:00
if (old_texture)
{
removeDependency(*old_texture);
m_resource_manager.get(ResourceManager::TEXTURE)->unload(*old_texture);
}
2015-05-31 15:48:35 +02:00
if (isReady())
2014-06-16 21:18:15 +02:00
{
2015-05-31 15:48:35 +02:00
updateShaderCombination();
2014-06-16 21:18:15 +02:00
}
2014-10-06 23:58:33 +02:00
}
2014-12-12 00:48:06 +01:00
void Material::setShader(const Path& path)
{
Shader* shader = static_cast<Shader*>(m_resource_manager.get(ResourceManager::SHADER)->load(path));
setShader(shader);
}
2015-05-31 15:48:35 +02:00
void Material::onReady()
{
Resource::onReady();
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
2015-05-31 15:48:35 +02:00
if (m_shader->isReady())
{
updateShaderCombination();
}
}
}
2015-06-01 23:35:57 +02:00
const char* Material::getTextureUniform(int i)
{
if (i < m_shader->getTextureSlotCount())
{
return m_shader->getTextureSlot(i).m_uniform;
}
return "";
}
2015-05-31 15:48:35 +02:00
Texture* Material::getTextureByUniform(const char* uniform) const
{
for (int i = 0, c = m_shader->getTextureSlotCount(); i < c; ++i)
{
if (strcmp(m_shader->getTextureSlot(i).m_uniform, uniform) == 0)
{
return m_textures[i];
}
2014-06-16 21:18:15 +02:00
}
2015-05-31 15:48:35 +02:00
return nullptr;
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];
serializer.deserializeObjectBegin();
char label[256];
2015-06-01 23:35:57 +02:00
bool keep_data = false;
2014-09-27 22:15:14 +02:00
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);
2015-05-31 15:48:35 +02:00
m_textures[m_texture_count] = static_cast<Texture*>(m_resource_manager.get(ResourceManager::TEXTURE)->load(Path(texture_path)));
addDependency(*m_textures[m_texture_count]);
2014-09-27 22:15:14 +02:00
}
}
2015-06-01 23:35:57 +02:00
else if (strcmp(label, "keep_data") == 0)
{
keep_data = true;
}
2014-09-27 22:15:14 +02:00
else
{
2015-05-31 15:48:35 +02:00
g_log_warning.log("Renderer") << "Unknown data \"" << label << "\" in material " << m_path.c_str();
2014-09-27 22:15:14 +02:00
return false;
}
}
2015-06-01 23:35:57 +02:00
if (keep_data)
{
m_textures[m_texture_count]->addDataReference();
}
2014-09-27 22:15:14 +02:00
serializer.deserializeObjectEnd();
2015-05-31 15:48:35 +02:00
++m_texture_count;
2014-09-27 22:15:14 +02:00
return true;
}
2014-06-16 21:18:15 +02:00
void Material::loaded(FS::IFile* file, bool success, FS::FileSystem& fs)
{
2015-06-06 15:04:58 +02:00
auto fp = fopen("shaders/vs_bump.bin", "rb");
fseek(fp, 0, SEEK_END);
auto s = ftell(fp);
auto* mem = bgfx::alloc(s+1);
fseek(fp, 0, SEEK_SET);
fread(mem->data, s, 1, fp);
mem->data[s] = '\0';
auto vs = bgfx::createShader(mem);
fclose(fp);
fp = fopen("shaders/fs_bump.bin", "rb");
fseek(fp, 0, SEEK_END);
s = ftell(fp);
mem = bgfx::alloc(s + 1);
fseek(fp, 0, SEEK_SET);
fread(mem->data, s, 1, fp);
mem->data[s] = '\0';
auto ps = bgfx::createShader(mem);
fclose(fp);
m_program_id = bgfx::createProgram(vs, ps, true);
PROFILE_FUNCTION();
2014-06-16 21:18:15 +02:00
if(success)
{
2015-05-28 23:30:50 +02:00
m_uniforms.clear();
JsonSerializer serializer(*file, JsonSerializer::READ, m_path.c_str(), m_allocator);
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