/* * 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_RENDER_MATERIAL_PROPERTY_HPP #define ANTKEEPER_RENDER_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-1d.hpp" #include "gl/texture-2d.hpp" #include "gl/texture-3d.hpp" #include "gl/texture-cube.hpp" #include namespace render { 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_1d; } 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_3d; } 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; } } // namespace render #endif // ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP