2015-08-17 23:45:26 +02:00
|
|
|
#include "renderer/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"
|
2014-06-25 23:06:30 +02:00
|
|
|
#include "core/path_utils.h"
|
2014-07-27 00:27:01 +02:00
|
|
|
#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"
|
2015-08-17 23:45:26 +02:00
|
|
|
#include "renderer/frame_buffer.h"
|
2015-10-07 18:40:17 +02:00
|
|
|
#include "renderer/material_manager.h"
|
2015-08-17 23:45:26 +02:00
|
|
|
#include "renderer/pipeline.h"
|
2015-09-05 13:08:47 +02:00
|
|
|
#include "renderer/renderer.h"
|
2015-08-17 23:45:26 +02:00
|
|
|
#include "renderer/shader.h"
|
|
|
|
#include "renderer/texture.h"
|
2014-06-16 21:18:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
namespace Lumix
|
|
|
|
{
|
|
|
|
|
2014-09-07 01:10:24 +02:00
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
static const uint32 SHADOWMAP_HASH = crc32("shadowmap");
|
2015-10-07 18:40:17 +02:00
|
|
|
int Material::s_alpha_cutout_define_idx = -1;
|
|
|
|
int Material::s_shadow_receiver_define_idx = -1;
|
2014-09-07 01:10:24 +02:00
|
|
|
|
|
|
|
|
2015-09-04 14:00:28 +02:00
|
|
|
Material::Material(const Path& path, ResourceManager& resource_manager, IAllocator& allocator)
|
|
|
|
: Resource(path, resource_manager, allocator)
|
|
|
|
, m_shader(nullptr)
|
|
|
|
, m_uniforms(allocator)
|
|
|
|
, m_allocator(allocator)
|
|
|
|
, m_texture_count(0)
|
|
|
|
, m_render_states(0)
|
|
|
|
, m_specular(1, 1, 1)
|
|
|
|
, m_shininess(4)
|
|
|
|
, m_shader_instance(nullptr)
|
2015-09-05 20:54:26 +02:00
|
|
|
, m_shader_mask(0)
|
2015-09-04 14:00:28 +02:00
|
|
|
{
|
2015-10-07 18:40:17 +02:00
|
|
|
auto* manager = resource_manager.get(ResourceManager::MATERIAL);
|
|
|
|
auto* mat_manager = static_cast<MaterialManager*>(manager);
|
|
|
|
|
|
|
|
s_alpha_cutout_define_idx = mat_manager->getRenderer().getShaderDefineIdx("ALPHA_CUTOUT");
|
|
|
|
s_shadow_receiver_define_idx = mat_manager->getRenderer().getShaderDefineIdx("SHADOW_RECEIVER");
|
|
|
|
|
2015-09-04 14:00:28 +02:00
|
|
|
enableZTest(true);
|
|
|
|
enableBackfaceCulling(true);
|
|
|
|
enableShadowReceiving(true);
|
|
|
|
for (int i = 0; i < MAX_TEXTURE_COUNT; ++i)
|
|
|
|
{
|
|
|
|
m_textures[i] = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-16 21:18:15 +02:00
|
|
|
Material::~Material()
|
|
|
|
{
|
|
|
|
ASSERT(isEmpty());
|
|
|
|
}
|
|
|
|
|
2015-06-18 01:33:49 +02:00
|
|
|
|
2015-09-05 13:08:47 +02:00
|
|
|
void Material::setUserDefine(int define_idx)
|
2015-09-04 14:00:28 +02:00
|
|
|
{
|
|
|
|
if (!isReady()) return;
|
|
|
|
if (!m_shader) return;
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
uint32 old_mask = m_shader_mask;
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_mask |= m_shader->getDefineMask(define_idx);
|
2015-09-05 13:08:47 +02:00
|
|
|
|
2015-09-05 20:54:26 +02:00
|
|
|
if (old_mask != m_shader_mask)
|
2015-09-04 14:00:28 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
2015-09-04 14:00:28 +02:00
|
|
|
}
|
2015-09-05 13:08:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Material::unsetUserDefine(int define_idx)
|
|
|
|
{
|
|
|
|
if (!isReady()) return;
|
|
|
|
if (!m_shader) return;
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
uint32 old_mask = m_shader_mask;
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_mask &= ~m_shader->getDefineMask(define_idx);
|
2015-09-04 14:00:28 +02:00
|
|
|
|
2015-09-05 20:54:26 +02:00
|
|
|
if (old_mask != m_shader_mask)
|
2015-09-04 14:00:28 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
2015-09-04 14:00:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-05 20:54:26 +02:00
|
|
|
bool Material::isAlphaCutout() const
|
|
|
|
{
|
|
|
|
if (!isReady()) return false;
|
|
|
|
if (!m_shader) return false;
|
|
|
|
|
2015-10-07 18:40:17 +02:00
|
|
|
return (m_shader_mask & m_shader->getDefineMask(s_alpha_cutout_define_idx)) != 0;
|
2015-09-05 20:54:26 +02:00
|
|
|
}
|
2015-09-05 13:08:47 +02:00
|
|
|
|
2015-09-05 20:54:26 +02:00
|
|
|
|
2015-09-14 18:03:45 +02:00
|
|
|
bool Material::hasAlphaCutoutDefine() const
|
|
|
|
{
|
2015-10-07 18:40:17 +02:00
|
|
|
return m_shader->getDefineMask(s_alpha_cutout_define_idx) != 0;
|
2015-09-14 18:03:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-05 20:54:26 +02:00
|
|
|
void Material::enableAlphaCutout(bool enable)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-09-04 14:00:28 +02:00
|
|
|
if (!isReady()) return;
|
2015-09-05 20:54:26 +02:00
|
|
|
if (!m_shader) return;
|
2015-09-04 14:00:28 +02:00
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
uint32 mask = m_shader->getDefineMask(s_alpha_cutout_define_idx);
|
2015-09-05 20:54:26 +02:00
|
|
|
if (enable)
|
2015-09-04 14:00:28 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_mask |= mask;
|
2015-09-04 14:00:28 +02:00
|
|
|
}
|
2015-09-05 20:54:26 +02:00
|
|
|
else
|
2015-09-04 14:00:28 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_mask &= ~mask;
|
2015-09-04 14:00:28 +02:00
|
|
|
}
|
2015-09-05 20:54:26 +02:00
|
|
|
|
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Material::isShadowReceiver() const
|
|
|
|
{
|
|
|
|
if (!isReady()) return false;
|
|
|
|
if (!m_shader) return false;
|
|
|
|
|
2015-10-07 18:40:17 +02:00
|
|
|
return (m_shader_mask & m_shader->getDefineMask(s_shadow_receiver_define_idx)) != 0;
|
2015-09-05 20:54:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-14 18:03:45 +02:00
|
|
|
bool Material::hasShadowReceivingDefine() const
|
|
|
|
{
|
2015-10-07 18:40:17 +02:00
|
|
|
return m_shader->getDefineMask(s_shadow_receiver_define_idx) != 0;
|
2015-09-14 18:03:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-05 20:54:26 +02:00
|
|
|
void Material::enableShadowReceiving(bool enable)
|
|
|
|
{
|
|
|
|
if (!isReady()) return;
|
|
|
|
if (!m_shader) return;
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
uint32 mask = m_shader->getDefineMask(s_shadow_receiver_define_idx);
|
2015-09-05 20:54:26 +02:00
|
|
|
if (enable)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_mask |= mask;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_shader_mask &= ~mask;
|
2014-10-06 23:58:33 +02:00
|
|
|
}
|
2015-09-05 20:54:26 +02:00
|
|
|
|
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
2014-10-06 23:58:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-03 01:14:38 +02:00
|
|
|
void Material::unload(void)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-11-16 12:11:10 +01:00
|
|
|
clearUniforms();
|
2015-07-25 19:33:19 +02:00
|
|
|
setShader(nullptr);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-26 20:54:22 +01:00
|
|
|
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
|
|
|
{
|
2015-07-30 00:33:52 +02:00
|
|
|
char path[MAX_PATH_LENGTH];
|
2015-09-25 23:03:10 +02:00
|
|
|
int flags = 0;
|
2015-10-04 14:39:52 +02:00
|
|
|
int atlas_size = -1;
|
2015-05-31 15:48:35 +02:00
|
|
|
if (m_textures[i])
|
|
|
|
{
|
2015-09-25 23:03:10 +02:00
|
|
|
flags = m_textures[i]->getFlags();
|
2015-10-25 15:52:00 +01:00
|
|
|
path[0] = '/';
|
|
|
|
Lumix::copyString(path + 1, MAX_PATH_LENGTH - 1, m_textures[i]->getPath().c_str());
|
2015-10-04 14:39:52 +02:00
|
|
|
atlas_size = m_textures[i]->getAtlasSize();
|
2015-05-31 15:48:35 +02:00
|
|
|
}
|
|
|
|
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);
|
2015-10-04 14:39:52 +02:00
|
|
|
if (atlas_size > 0) serializer.serialize("atlas_size", atlas_size);
|
2015-09-25 23:03:10 +02:00
|
|
|
if (flags & BGFX_TEXTURE_U_CLAMP) serializer.serialize("u_clamp", true);
|
|
|
|
if (flags & BGFX_TEXTURE_V_CLAMP) serializer.serialize("v_clamp", true);
|
|
|
|
if (flags & BGFX_TEXTURE_W_CLAMP) serializer.serialize("w_clamp", true);
|
|
|
|
if (flags & BGFX_TEXTURE_MIN_POINT) serializer.serialize("min_filter", "point");
|
|
|
|
if (flags & BGFX_TEXTURE_MIN_ANISOTROPIC) serializer.serialize("min_filter", "anisotropic");
|
|
|
|
if (flags & BGFX_TEXTURE_MAG_POINT) serializer.serialize("mag_filter", "point");
|
|
|
|
if (flags & BGFX_TEXTURE_MAG_ANISOTROPIC) serializer.serialize("mag_filter", "anisotropic");
|
|
|
|
if (m_textures[i] && m_textures[i]->getData()) serializer.serialize("keep_data", true);
|
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();
|
2015-06-07 18:17:25 +02:00
|
|
|
serializer.serialize("backface_culling", isBackfaceCulling());
|
2015-09-05 20:54:26 +02:00
|
|
|
serializer.serialize("alpha_cutout", isAlphaCutout());
|
|
|
|
serializer.serialize("shadow_receiver", isShadowReceiver());
|
2015-07-10 08:12:12 +02:00
|
|
|
serializer.serialize("shininess", m_shininess);
|
|
|
|
serializer.beginArray("specular");
|
|
|
|
serializer.serializeArrayItem(m_specular.x);
|
|
|
|
serializer.serializeArrayItem(m_specular.y);
|
|
|
|
serializer.serializeArrayItem(m_specular.z);
|
|
|
|
serializer.endArray();
|
2015-06-07 18:17:25 +02:00
|
|
|
serializer.serialize("z_test", isZTest());
|
2014-06-16 21:18:15 +02:00
|
|
|
serializer.endObject();
|
2015-09-25 23:03:10 +02:00
|
|
|
return true;
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
|
2015-11-16 12:11:10 +01:00
|
|
|
|
|
|
|
void Material::clearUniforms()
|
|
|
|
{
|
|
|
|
for (auto& uniform : m_uniforms)
|
|
|
|
{
|
|
|
|
bgfx::destroyUniform(uniform.m_handle);
|
|
|
|
}
|
|
|
|
m_uniforms.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-26 20:54:22 +01:00
|
|
|
void Material::deserializeUniforms(JsonSerializer& serializer)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
|
|
|
serializer.deserializeArrayBegin();
|
2015-11-16 12:11:10 +01:00
|
|
|
clearUniforms();
|
2014-06-16 21:18:15 +02:00
|
|
|
while (!serializer.isArrayEnd())
|
|
|
|
{
|
|
|
|
Uniform& uniform = m_uniforms.pushEmpty();
|
|
|
|
serializer.nextArrayItem();
|
|
|
|
serializer.deserializeObjectBegin();
|
|
|
|
char label[256];
|
2015-06-07 18:17:25 +02:00
|
|
|
auto uniform_type = bgfx::UniformType::End;
|
2014-06-16 21:18:15 +02:00
|
|
|
while (!serializer.isObjectEnd())
|
|
|
|
{
|
|
|
|
serializer.deserializeLabel(label, 255);
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(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
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "int_value") == 0)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-06-07 18:17:25 +02:00
|
|
|
uniform_type = bgfx::UniformType::Int1;
|
2014-06-16 21:18:15 +02:00
|
|
|
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
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "float_value") == 0)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "matrix_value") == 0)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-06-07 18:17:25 +02:00
|
|
|
uniform_type = bgfx::UniformType::Mat4;
|
2014-06-16 21:18:15 +02:00
|
|
|
uniform.m_type = Uniform::MATRIX;
|
|
|
|
serializer.deserializeArrayBegin();
|
|
|
|
for (int i = 0; i < 16; ++i)
|
|
|
|
{
|
2014-11-15 20:58:55 +01:00
|
|
|
serializer.deserializeArrayItem(uniform.m_matrix[i], 0);
|
2014-06-16 21:18:15 +02:00
|
|
|
ASSERT(i == 15 || !serializer.isArrayEnd());
|
|
|
|
}
|
|
|
|
serializer.deserializeArrayEnd();
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "time") == 0)
|
2014-06-21 19:26:27 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2015-05-30 21:12:40 +02:00
|
|
|
g_log_warning.log("material") << "Unknown label \"" << label << "\"";
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-07 18:17:25 +02:00
|
|
|
uniform.m_handle = bgfx::createUniform(uniform.m_name, uniform_type);
|
|
|
|
|
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-10-25 15:52:00 +01:00
|
|
|
Texture* texture =
|
|
|
|
static_cast<Texture*>(m_resource_manager.get(ResourceManager::TEXTURE)->load(path));
|
2015-05-31 15:48:35 +02:00
|
|
|
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;
|
2015-10-07 18:40:17 +02:00
|
|
|
|
|
|
|
if (texture) addDependency(*texture);
|
2015-05-31 15:48:35 +02:00
|
|
|
m_textures[i] = texture;
|
2015-10-07 18:40:17 +02:00
|
|
|
if (i >= m_texture_count) m_texture_count = i + 1;
|
|
|
|
|
2015-05-28 23:30:50 +02:00
|
|
|
if (old_texture)
|
|
|
|
{
|
2015-10-07 18:11:57 +02:00
|
|
|
if (texture) texture->setAtlasSize(old_texture->getAtlasSize());
|
2015-05-28 23:30:50 +02:00
|
|
|
removeDependency(*old_texture);
|
|
|
|
m_resource_manager.get(ResourceManager::TEXTURE)->unload(*old_texture);
|
|
|
|
}
|
2015-09-05 20:54:26 +02:00
|
|
|
if (isReady() && m_shader)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
int define_idx = m_shader->getTextureSlot(i).m_define_idx;
|
|
|
|
if (define_idx >= 0 && m_textures[i])
|
|
|
|
{
|
|
|
|
m_shader_mask |= m_shader->getDefineMask(define_idx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_shader_mask &= ~m_shader->getDefineMask(define_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
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-10-03 01:14:38 +02:00
|
|
|
void Material::onBeforeReady()
|
2015-05-31 15:48:35 +02:00
|
|
|
{
|
2015-10-03 01:14:38 +02:00
|
|
|
if (!m_shader) return;
|
2015-10-07 18:40:17 +02:00
|
|
|
|
2015-10-03 01:14:38 +02:00
|
|
|
for (int i = 0; i < m_shader->getTextureSlotCount(); ++i)
|
2015-09-05 13:08:47 +02:00
|
|
|
{
|
2015-10-03 01:14:38 +02:00
|
|
|
if (m_shader->getTextureSlot(i).m_define_idx >= 0 && m_textures[i])
|
2015-09-05 20:54:26 +02:00
|
|
|
{
|
2015-10-03 01:14:38 +02:00
|
|
|
m_shader_mask |= m_shader->getDefineMask(
|
|
|
|
m_shader->getTextureSlot(i).m_define_idx);
|
2015-09-05 20:54:26 +02:00
|
|
|
}
|
2015-09-05 13:08:47 +02:00
|
|
|
}
|
2015-10-03 01:14:38 +02:00
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
2015-05-31 15:48:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-16 21:18:15 +02:00
|
|
|
void Material::setShader(Shader* shader)
|
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
bool is_alpha = isAlphaCutout();
|
|
|
|
bool is_receiver = isShadowReceiver();
|
2014-06-16 21:18:15 +02:00
|
|
|
if (m_shader)
|
2015-09-11 00:03:05 +02:00
|
|
|
{
|
|
|
|
Shader* shader = m_shader;
|
|
|
|
m_shader = nullptr;
|
|
|
|
removeDependency(*shader);
|
|
|
|
m_resource_manager.get(ResourceManager::SHADER)->unload(*shader);
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
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())
|
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
m_shader_mask = 0;
|
|
|
|
enableShadowReceiving(is_receiver);
|
|
|
|
enableAlphaCutout(is_alpha);
|
|
|
|
|
|
|
|
for (int i = 0; i < m_shader->getTextureSlotCount(); ++i)
|
|
|
|
{
|
|
|
|
if (m_shader->getTextureSlot(i).m_define_idx >= 0 && m_textures[i])
|
|
|
|
{
|
|
|
|
m_shader_mask |= m_shader->getDefineMask(
|
|
|
|
m_shader->getTextureSlot(i).m_define_idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_shader_instance = &m_shader->getInstance(m_shader_mask);
|
2015-05-31 15:48:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 01:33:49 +02:00
|
|
|
|
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-06-18 01:33:49 +02:00
|
|
|
|
2015-05-31 15:48:35 +02:00
|
|
|
Texture* Material::getTextureByUniform(const char* uniform) const
|
|
|
|
{
|
2015-08-07 02:40:57 +02:00
|
|
|
if (!m_shader)
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-05-31 15:48:35 +02:00
|
|
|
for (int i = 0, c = m_shader->getTextureSlotCount(); i < c; ++i)
|
|
|
|
{
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(m_shader->getTextureSlot(i).m_uniform, uniform) == 0)
|
2015-05-31 15:48:35 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-11-26 20:54:22 +01:00
|
|
|
bool Material::deserializeTexture(JsonSerializer& serializer, const char* material_dir)
|
2014-09-27 22:15:14 +02:00
|
|
|
{
|
2015-07-30 00:33:52 +02:00
|
|
|
char path[MAX_PATH_LENGTH];
|
2014-09-27 22:15:14 +02:00
|
|
|
serializer.deserializeObjectBegin();
|
|
|
|
char label[256];
|
2015-06-01 23:35:57 +02:00
|
|
|
bool keep_data = false;
|
2015-11-11 21:54:25 +01:00
|
|
|
uint32 flags = 0;
|
2015-10-04 13:37:29 +02:00
|
|
|
int atlas_size = -1;
|
|
|
|
|
2014-09-27 22:15:14 +02:00
|
|
|
while (!serializer.isObjectEnd())
|
|
|
|
{
|
|
|
|
serializer.deserializeLabel(label, sizeof(label));
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(label, "source") == 0)
|
2014-09-27 22:15:14 +02:00
|
|
|
{
|
2015-07-30 00:33:52 +02:00
|
|
|
serializer.deserialize(path, MAX_PATH_LENGTH, "");
|
2014-09-27 22:15:14 +02:00
|
|
|
if (path[0] != '\0')
|
|
|
|
{
|
2015-07-30 00:33:52 +02:00
|
|
|
char texture_path[MAX_PATH_LENGTH];
|
2015-10-25 15:52:00 +01:00
|
|
|
if (path[0] != '/' && path[0] != '\\')
|
|
|
|
{
|
|
|
|
copyString(texture_path, material_dir);
|
|
|
|
catString(texture_path, path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
copyString(texture_path, path);
|
|
|
|
}
|
2016-01-05 23:43:12 +01:00
|
|
|
auto* mng = m_resource_manager.get(ResourceManager::TEXTURE);
|
|
|
|
m_textures[m_texture_count] = static_cast<Texture*>(mng->load(Path(texture_path)));
|
2015-05-31 15:48:35 +02:00
|
|
|
addDependency(*m_textures[m_texture_count]);
|
2014-09-27 22:15:14 +02:00
|
|
|
}
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "atlas_size") == 0)
|
2015-10-04 13:37:29 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(atlas_size, -1);
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "min_filter") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(label, sizeof(label), "");
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(label, "point") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_MIN_POINT;
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "anisotropic") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_MIN_ANISOTROPIC;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-10-03 01:53:04 +02:00
|
|
|
g_log_error.log("Renderer") << "Unknown texture filter \"" << label
|
|
|
|
<< "\" in material " << getPath().c_str();
|
2015-08-20 00:24:04 +02:00
|
|
|
}
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "mag_filter") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(label, sizeof(label), "");
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(label, "point") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_MAG_POINT;
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "anisotropic") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_MAG_ANISOTROPIC;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-10-03 01:53:04 +02:00
|
|
|
g_log_error.log("Renderer") << "Unknown texture filter \"" << label
|
|
|
|
<< "\" in material " << getPath().c_str();
|
2015-08-20 00:24:04 +02:00
|
|
|
}
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "u_clamp") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
bool b;
|
|
|
|
serializer.deserialize(b, false);
|
|
|
|
if (b)
|
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_U_CLAMP;
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "v_clamp") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
bool b;
|
|
|
|
serializer.deserialize(b, false);
|
|
|
|
if (b)
|
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_V_CLAMP;
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "w_clamp") == 0)
|
2015-08-20 00:24:04 +02:00
|
|
|
{
|
|
|
|
bool b;
|
|
|
|
serializer.deserialize(b, false);
|
|
|
|
if (b)
|
|
|
|
{
|
|
|
|
flags |= BGFX_TEXTURE_W_CLAMP;
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "keep_data") == 0)
|
2015-06-01 23:35:57 +02:00
|
|
|
{
|
2015-10-04 13:37:29 +02:00
|
|
|
serializer.deserialize(keep_data, false);
|
2015-06-01 23:35:57 +02:00
|
|
|
}
|
2014-09-27 22:15:14 +02:00
|
|
|
else
|
|
|
|
{
|
2015-10-03 01:53:04 +02:00
|
|
|
g_log_warning.log("Renderer") << "Unknown data \"" << label << "\" in material "
|
|
|
|
<< getPath().c_str();
|
2014-09-27 22:15:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-08-20 00:24:04 +02:00
|
|
|
if (m_textures[m_texture_count])
|
2015-06-01 23:35:57 +02:00
|
|
|
{
|
2015-10-04 13:37:29 +02:00
|
|
|
m_textures[m_texture_count]->setAtlasSize(atlas_size);
|
2015-08-20 00:24:04 +02:00
|
|
|
m_textures[m_texture_count]->setFlags(flags);
|
|
|
|
|
|
|
|
if (keep_data)
|
|
|
|
{
|
|
|
|
m_textures[m_texture_count]->addDataReference();
|
|
|
|
}
|
2015-06-01 23:35:57 +02:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-07 18:17:25 +02:00
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
void Material::setRenderState(bool value, uint64 state, uint64 mask)
|
2015-06-07 18:17:25 +02:00
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
m_render_states |= state;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_render_states &= ~mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-03 01:14:38 +02:00
|
|
|
bool Material::load(FS::IFile& file)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-07-27 00:27:01 +02:00
|
|
|
PROFILE_FUNCTION();
|
2015-08-15 09:56:37 +02:00
|
|
|
|
2015-10-03 01:14:38 +02:00
|
|
|
m_render_states = BGFX_STATE_DEPTH_TEST_LEQUAL | BGFX_STATE_CULL_CW;
|
2015-08-15 09:56:37 +02:00
|
|
|
m_uniforms.clear();
|
2015-10-03 01:53:04 +02:00
|
|
|
JsonSerializer serializer(file, JsonSerializer::READ, getPath().c_str(), m_allocator);
|
2015-08-15 09:56:37 +02:00
|
|
|
serializer.deserializeObjectBegin();
|
|
|
|
char path[MAX_PATH_LENGTH];
|
|
|
|
char label[256];
|
|
|
|
char material_dir[MAX_PATH_LENGTH];
|
2015-10-03 01:53:04 +02:00
|
|
|
PathUtils::getDir(material_dir, MAX_PATH_LENGTH, getPath().c_str());
|
2015-08-15 09:56:37 +02:00
|
|
|
bool b_value;
|
|
|
|
while (!serializer.isObjectEnd())
|
|
|
|
{
|
|
|
|
serializer.deserializeLabel(label, 255);
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(label, "uniforms") == 0)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-08-15 09:56:37 +02:00
|
|
|
deserializeUniforms(serializer);
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "texture") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
|
|
|
if (!deserializeTexture(serializer, material_dir))
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-10-03 01:14:38 +02:00
|
|
|
return false;
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
2015-08-15 09:56:37 +02:00
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "alpha_cutout") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
bool b;
|
|
|
|
serializer.deserialize(b, false);
|
|
|
|
enableAlphaCutout(b);
|
2015-08-15 09:56:37 +02:00
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "alpha_blending") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
2015-11-08 11:44:20 +01:00
|
|
|
if (serializer.isNextBoolean())
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-11-08 11:44:20 +01:00
|
|
|
bool is_alpha_blending;
|
|
|
|
serializer.deserialize(is_alpha_blending, false);
|
|
|
|
if (is_alpha_blending)
|
|
|
|
{
|
|
|
|
m_render_states |= BGFX_STATE_BLEND_ADD;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_render_states &= ~BGFX_STATE_BLEND_MASK;
|
|
|
|
}
|
2015-09-12 14:51:13 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-08 11:44:20 +01:00
|
|
|
serializer.deserialize(label, 255, "alpha");
|
2015-11-12 17:54:02 +01:00
|
|
|
if (compareString(label, "alpha") == 0)
|
2015-11-08 11:44:20 +01:00
|
|
|
{
|
|
|
|
m_render_states |= BGFX_STATE_BLEND_ALPHA;
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "add") == 0)
|
2015-11-08 11:44:20 +01:00
|
|
|
{
|
|
|
|
m_render_states |= BGFX_STATE_BLEND_ADD;
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "disabled") == 0)
|
2015-11-08 11:44:20 +01:00
|
|
|
{
|
|
|
|
m_render_states &= ~BGFX_STATE_BLEND_MASK;
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
2015-08-15 09:56:37 +02:00
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "specular") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
|
|
|
serializer.deserializeArrayBegin();
|
|
|
|
serializer.deserializeArrayItem(m_specular.x, 1.0f);
|
|
|
|
serializer.deserializeArrayItem(m_specular.y, 1.0f);
|
|
|
|
serializer.deserializeArrayItem(m_specular.z, 1.0f);
|
|
|
|
serializer.deserializeArrayEnd();
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "shininess") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(m_shininess, 4.0f);
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "shadow_receiver") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
2015-09-05 20:54:26 +02:00
|
|
|
bool b;
|
|
|
|
serializer.deserialize(b, true);
|
|
|
|
enableShadowReceiving(b);
|
2015-08-15 09:56:37 +02:00
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "shader") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(path, MAX_PATH_LENGTH, "");
|
2016-01-05 23:43:12 +01:00
|
|
|
auto* manager = m_resource_manager.get(ResourceManager::SHADER);
|
|
|
|
setShader(static_cast<Shader*>(manager->load(Path(path))));
|
2015-08-15 09:56:37 +02:00
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "z_test") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(b_value, true);
|
|
|
|
enableZTest(b_value);
|
|
|
|
}
|
2015-11-12 17:54:02 +01:00
|
|
|
else if (compareString(label, "backface_culling") == 0)
|
2015-08-15 09:56:37 +02:00
|
|
|
{
|
|
|
|
serializer.deserialize(b_value, true);
|
|
|
|
enableBackfaceCulling(b_value);
|
|
|
|
}
|
|
|
|
else
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-10-03 01:53:04 +02:00
|
|
|
g_log_warning.log("renderer") << "Unknown parameter " << label << " in material "
|
|
|
|
<< getPath().c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
}
|
2015-08-15 09:56:37 +02:00
|
|
|
serializer.deserializeObjectEnd();
|
|
|
|
|
|
|
|
if (!m_shader)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2015-10-03 01:53:04 +02:00
|
|
|
g_log_error.log("renderer") << "Material " << getPath().c_str() << " without a shader";
|
2015-10-03 01:14:38 +02:00
|
|
|
return false;
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
2015-08-15 09:56:37 +02:00
|
|
|
|
|
|
|
m_size = file.size();
|
2015-10-03 01:14:38 +02:00
|
|
|
return true;
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // ~namespace Lumix
|