/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see .
*/
#ifndef ANTKEEPER_MATERIAL_PROPERTY_HPP
#define ANTKEEPER_MATERIAL_PROPERTY_HPP
#include "animation/tween.hpp"
#include "gl/shader-variable-type.hpp"
#include "gl/shader-input.hpp"
#include "math/interpolation.hpp"
#include "utility/fundamental-types.hpp"
#include "gl/shader-program.hpp"
#include "gl/texture-2d.hpp"
#include "gl/texture-cube.hpp"
#include
class material;
/**
* Abstract base class for material properties.
*/
class material_property_base
{
public:
/**
* Connects the material property to a shader input.
*
* @param input Shader input to which the material property should be connected.
* @return `true` if the property was connected to the input successfully, `false` otherwise.
*/
bool connect(const gl::shader_input* input);
/**
* Disconnects the material property from its shader input.
*/
void disconnect();
/**
* Sets state 0 = state 1.
*/
virtual void update_tweens() = 0;
/**
* Uploads the material property to its shader program.
*
* @param a Interpolation factor. Should be on `[0.0, 1.0]`.
* @return `true` if the property was uploaded successfully, `false` otherwise.
*/
virtual bool upload(double a) const = 0;
/**
* Returns the type of data which the property contains.
*/
virtual gl::shader_variable_type get_data_type() const = 0;
/**
* Returns `true` if the material property is connected to a shader input, `false` otherwise.
*/
bool is_connected() const;
/**
* Creates a copy of this material property.
*/
virtual material_property_base* clone() const = 0;
protected:
material_property_base();
const gl::shader_input* input;
};
inline bool material_property_base::is_connected() const
{
return (input != nullptr);
}
/**
* A property of a material which can be uploaded to a shader program via a shader input.
*
* @tparam T Property data type.
*/
template
class material_property: public material_property_base
{
public:
typedef tween tween_type;
typedef typename tween::interpolator_type interpolator_type;
/// Default tween interpolator function for this material property type.
static T default_interpolator(const T& x, const T& y, double a);
/**
* Creates a material property.
*
* @param element_count Number of elements in the property array.
*/
material_property(std::size_t element_count);
/**
* Destroys a material property.
*/
virtual ~material_property();
material_property(const material_property&) = delete;
material_property& operator=(const material_property&) = delete;
/// @copydoc material_property_base::update_tweens()
virtual void update_tweens();
/// @copydoc material_property_base::upload() const
virtual bool upload(double a) const;
/**
* Sets the value of this property.
*
* @param value Value to set.
*/
void set_value(const T& value);
/**
* Sets the value of a single element in this array property.
*
* @param index Index of an array element.
* @param value Value to set.
*/
void set_value(std::size_t index, const T& value);
/**
* Sets the values of a range of elements in this array property.
*
* @param index Index of the first array element to set.
* @param values Pointer to an array of values to set.
* @param count Number of elements to set.
*/
void set_values(std::size_t index, const T* values, std::size_t count);
/**
* Sets the tween interpolator function.
*
* @param interpolator Tween interpolator function.
*/
void set_tween_interpolator(interpolator_type interpolator);
/// Returns the value of the first element in this property.
const T& get_value() const;
/**
* Returns the value of the first element in this property.
*
* @param index Index of an array element.
* @return Value of the element at the specified index.
*/
const T& get_value(std::size_t index) const;
/// @copydoc material_property_base::get_data_type() const
virtual gl::shader_variable_type get_data_type() const;
/// @copydoc material_property_base::clone() const
virtual material_property_base* clone() const;
private:
std::size_t element_count;
tween* values;
};
template
inline T material_property::default_interpolator(const T& x, const T& y, double a)
{
return y;
}
template <>
inline float material_property::default_interpolator(const float& x, const float& y, double a)
{
return math::lerp(x, y, static_cast(a));
}
template <>
inline float2 material_property::default_interpolator(const float2& x, const float2& y, double a)
{
return math::lerp(x, y, static_cast(a));
}
template <>
inline float3 material_property::default_interpolator(const float3& x, const float3& y, double a)
{
return math::lerp(x, y, static_cast(a));
}
template <>
inline float4 material_property::default_interpolator(const float4& x, const float4& y, double a)
{
return math::lerp(x, y, static_cast(a));
}
template
material_property::material_property(std::size_t element_count):
element_count(element_count),
values(nullptr)
{
values = new tween[element_count];
set_tween_interpolator(default_interpolator);
}
template
material_property::~material_property()
{
delete[] values;
}
template
void material_property::update_tweens()
{
for (std::size_t i = 0; i < element_count; ++i)
{
values[i].update();
}
}
template
bool material_property::upload(double a) const
{
if (!is_connected())
{
return false;
}
if (element_count > 1)
{
for (std::size_t i = 0; i < element_count; ++i)
{
if (!input->upload(i, values[i].interpolate(a)))
return false;
}
return true;
}
else
{
return input->upload(values[0].interpolate(a));
}
}
template
void material_property::set_value(const T& value)
{
values[0][1] = value;
}
template
void material_property::set_value(std::size_t index, const T& value)
{
values[index][1] = value;
}
template
void material_property::set_values(std::size_t index, const T* values, std::size_t count)
{
for (std::size_t i = 0; i < count; ++i)
{
this->values[index + i][1] = values[i];
}
}
template
void material_property::set_tween_interpolator(interpolator_type interpolator)
{
for (std::size_t i = 0; i < element_count; ++i)
{
this->values[i].set_interpolator(interpolator);
}
}
template
inline const T& material_property::get_value() const
{
return values[0][1];
}
template
inline const T& material_property::get_value(std::size_t index) const
{
return values[index][1];
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::bool1;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::bool2;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::bool3;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::bool4;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::int1;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::int2;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::int3;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::int4;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::uint1;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::uint2;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::uint3;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::uint4;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float1;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float2;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float3;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float4;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float2x2;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float3x3;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::float4x4;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::texture_2d;
}
template <>
inline gl::shader_variable_type material_property::get_data_type() const
{
return gl::shader_variable_type::texture_cube;
}
template
material_property_base* material_property::clone() const
{
material_property* property = new material_property(element_count);
for (std::size_t i = 0; i < element_count; ++i)
{
property->values[i][0] = values[i][0];
property->values[i][1] = values[i][1];
}
property->input = input;
return property;
}
#endif // ANTKEEPER_MATERIAL_PROPERTY_HPP