#ifndef __EFFEKSEERRENDERER_COMMON_UTILS_H__
#define __EFFEKSEERRENDERER_COMMON_UTILS_H__

#include "EffekseerRenderer.Renderer.h"
#include "EffekseerRenderer.Renderer_Impl.h"
#include <Effekseer.h>
#include <Effekseer/Material/Effekseer.CompiledMaterial.h>
#include <Effekseer/Model/SplineGenerator.h>
#include <algorithm>
#include <array>
#include <assert.h>
#include <functional>
#include <math.h>
#include <string.h>
#include <type_traits>

namespace EffekseerRenderer
{

using VertexFloat3 = ::Effekseer::Vector3D;
using VertexColor = ::Effekseer::Color;

inline void SwapRGBAToBGRA(Effekseer::Color& color)
{
	auto temp = color;
	color.B = temp.R;
	color.R = temp.B;
}

inline Effekseer::Color PackVector3DF(const Effekseer::SIMD::Vec3f& v)
{
	Effekseer::Color ret;
	ret.R = static_cast<uint8_t>(Effekseer::Clamp(((v.GetX() + 1.0f) / 2.0f + 0.5f / 255.0f) * 255.0f, 255, 0));
	ret.G = static_cast<uint8_t>(Effekseer::Clamp(((v.GetY() + 1.0f) / 2.0f + 0.5f / 255.0f) * 255.0f, 255, 0));
	ret.B = static_cast<uint8_t>(Effekseer::Clamp(((v.GetZ() + 1.0f) / 2.0f + 0.5f / 255.0f) * 255.0f, 255, 0));
	ret.A = 255;
	return ret;
}

inline Effekseer::Vector3D UnpackVector3DF(const Effekseer::Color& v)
{
	Effekseer::Vector3D ret;
	ret.X = (static_cast<float>(v.R) / 255.0f * 2.0f - 1.0f);
	ret.Y = (static_cast<float>(v.G) / 255.0f * 2.0f - 1.0f);
	ret.Z = (static_cast<float>(v.B) / 255.0f * 2.0f - 1.0f);
	return ret;
}

struct DynamicVertex
{
	VertexFloat3 Pos;
	VertexColor Col;
	//! packed vector
	VertexColor Normal;
	//! packed vector
	VertexColor Tangent;

	union
	{
		//! UV1 (for template)
		float UV[2];
		float UV1[2];
	};

	float UV2[2];

	void SetFlipbookIndexAndNextRate(float value)
	{
	}

	void SetAlphaThreshold(float value)
	{
	}

	void SetColor(const VertexColor& color, bool flipRGB)
	{
		Col = color;

		if (flipRGB)
		{
			std::swap(Col.R, Col.B);
		}
	}

	void SetPackedNormal(const VertexColor& normal, bool flipRGB)
	{
		Normal = normal;

		if (flipRGB)
		{
			std::swap(Normal.R, Normal.B);
		}
	}

	void SetPackedTangent(const VertexColor& tangent, bool flipRGB)
	{
		Tangent = tangent;

		if (flipRGB)
		{
			std::swap(Tangent.R, Tangent.B);
		}
	}

	void SetUV2(float u, float v)
	{
		UV2[0] = u;
		UV2[1] = v;
	}
};

struct DynamicVertexWithCustomData
{
	DynamicVertex V;
	std::array<float, 4> CustomData1;
	std::array<float, 4> CustomData2;
};

struct LightingVertex
{
	VertexFloat3 Pos;
	VertexColor Col;
	//! packed vector
	VertexColor Normal;
	//! packed vector
	VertexColor Tangent;

	union
	{
		//! UV1 (for template)
		float UV[2];
		float UV1[2];
	};

	float UV2[2];

	void SetFlipbookIndexAndNextRate(float value)
	{
	}
	void SetAlphaThreshold(float value)
	{
	}

	void SetColor(const VertexColor& color, bool flipRGB)
	{
		Col = color;

		if (flipRGB)
		{
			std::swap(Col.R, Col.B);
		}
	}

	void SetPackedNormal(const VertexColor& normal, bool flipRGB)
	{
		Normal = normal;

		if (flipRGB)
		{
			std::swap(Normal.R, Normal.B);
		}
	}

	void SetPackedTangent(const VertexColor& tangent, bool flipRGB)
	{
		Tangent = tangent;

		if (flipRGB)
		{
			std::swap(Tangent.R, Tangent.B);
		}
	}

	void SetUV2(float u, float v)
	{
		UV2[0] = u;
		UV2[1] = v;
	}
};

struct SimpleVertex
{
	VertexFloat3 Pos;
	VertexColor Col;

	union
	{
		float UV[2];
		//! dummy for template
		float UV2[2];
	};

	void SetFlipbookIndexAndNextRate(float value)
	{
	}
	void SetAlphaThreshold(float value)
	{
	}

	void SetColor(const VertexColor& color, bool flipRGB)
	{
		Col = color;

		if (flipRGB)
		{
			std::swap(Col.R, Col.B);
		}
	}

	void SetPackedNormal(const VertexColor& normal, bool flipRGB)
	{
	}

	void SetPackedTangent(const VertexColor& tangent, bool flipRGB)
	{
	}

	void SetUV2(float u, float v)
	{
	}
};

struct AdvancedLightingVertex
{
	VertexFloat3 Pos;
	VertexColor Col;
	//! packed vector
	VertexColor Normal;
	//! packed vector
	VertexColor Tangent;

	union
	{
		//! UV1 (for template)
		float UV[2];
		float UV1[2];
	};

	float UV2[2];

	float AlphaUV[2];
	float UVDistortionUV[2];
	float BlendUV[2];
	float BlendAlphaUV[2];
	float BlendUVDistortionUV[2];
	float FlipbookIndexAndNextRate;
	float AlphaThreshold;

	void SetFlipbookIndexAndNextRate(float value)
	{
		FlipbookIndexAndNextRate = value;
	}
	void SetAlphaThreshold(float value)
	{
		AlphaThreshold = value;
	}

	void SetColor(const VertexColor& color, bool flipRGB)
	{
		Col = color;

		if (flipRGB)
		{
			std::swap(Col.R, Col.B);
		}
	}

	void SetPackedNormal(const VertexColor& normal, bool flipRGB)
	{
		Normal = normal;

		if (flipRGB)
		{
			std::swap(Normal.R, Normal.B);
		}
	}

	void SetPackedTangent(const VertexColor& tangent, bool flipRGB)
	{
		Tangent = tangent;

		if (flipRGB)
		{
			std::swap(Tangent.R, Tangent.B);
		}
	}

	void SetUV2(float u, float v)
	{
		UV2[0] = u;
		UV2[1] = v;
	}
};

struct AdvancedSimpleVertex
{
	VertexFloat3 Pos;
	VertexColor Col;

	union
	{
		float UV[2];
		//! dummy for template
		float UV1[2];
		//! dummy for template
		float UV2[2];
	};

	float AlphaUV[2];
	float UVDistortionUV[2];
	float BlendUV[2];
	float BlendAlphaUV[2];
	float BlendUVDistortionUV[2];
	float FlipbookIndexAndNextRate;
	float AlphaThreshold;

	void SetFlipbookIndexAndNextRate(float value)
	{
		FlipbookIndexAndNextRate = value;
	}
	void SetAlphaThreshold(float value)
	{
		AlphaThreshold = value;
	}

	void SetColor(const VertexColor& color, bool flipRGB)
	{
		Col = color;

		if (flipRGB)
		{
			std::swap(Col.R, Col.B);
		}
	}

	void SetPackedNormal(const VertexColor& normal, bool flipRGB)
	{
	}

	void SetPackedTangent(const VertexColor& tangent, bool flipRGB)
	{
	}

	void SetUV2(float u, float v)
	{
	}
};

template <typename U>
class ContainAdvancedData
{
public:
	using Value = int;
};

template <>
class ContainAdvancedData<SimpleVertex>
{
public:
	using Value = float;
};

template <>
class ContainAdvancedData<LightingVertex>
{
public:
	using Value = float;
};

template <>
class ContainAdvancedData<DynamicVertex>
{
public:
	using Value = float;
};

template <typename U>
using enable_if_contain_advanced_t = typename std::enable_if<std::is_same<typename ContainAdvancedData<U>::Value, int>::value, std::nullptr_t>::type;

template <typename U>
using enable_ifnot_contain_advanced_t = typename std::enable_if<std::is_same<typename ContainAdvancedData<U>::Value, float>::value, std::nullptr_t>::type;

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexAlphaUV(const U& v)
{
	return {v.AlphaUV[0], v.AlphaUV[1]};
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexAlphaUV(const U& v)
{
	return {0.0f, 0.0f};
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexUVDistortionUV(const U& v)
{
	return {v.UVDistortionUV[0], v.UVDistortionUV[1]};
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexUVDistortionUV(const U& v)
{
	return {0.0f, 0.0f};
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexBlendUV(const U& v)
{
	return {v.BlendUV[0], v.BlendUV[1]};
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexBlendUV(const U& v)
{
	return {0.0f, 0.0f};
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexBlendAlphaUV(const U& v)
{
	return {v.BlendAlphaUV[0], v.BlendAlphaUV[1]};
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexBlendAlphaUV(const U& v)
{
	return {0.0f, 0.0f};
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexBlendUVDistortionUV(const U& v)
{
	return {v.BlendUVDistortionUV[0], v.BlendUVDistortionUV[1]};
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
std::array<float, 2> GetVertexBlendUVDistortionUV(const U& v)
{
	return {0.0f, 0.0f};
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
float GetVertexFlipbookIndexAndNextRate(const U& v)
{
	return v.FlipbookIndexAndNextRate;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
float GetVertexFlipbookIndexAndNextRate(const U& v)
{
	return 0.0f;
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
float GetVertexAlphaThreshold(const U& v)
{
	return v.AlphaThreshold;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
float GetVertexAlphaThreshold(const U& v)
{
	return 0.0f;
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexAlphaUV(U& v, float value, int32_t ind)
{
	v.AlphaUV[ind] = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexAlphaUV(U& v, float value, int32_t ind)
{
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexUVDistortionUV(U& v, float value, int32_t ind)
{
	v.UVDistortionUV[ind] = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexUVDistortionUV(U& v, float value, int32_t ind)
{
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexBlendUV(U& v, float value, int32_t ind)
{
	v.BlendUV[ind] = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexBlendUV(U& v, float value, int32_t ind)
{
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexBlendAlphaUV(U& v, float value, int32_t ind)
{
	v.BlendAlphaUV[ind] = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexBlendAlphaUV(U& v, float value, int32_t ind)
{
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexBlendUVDistortionUV(U& v, float value, int32_t ind)
{
	v.BlendUVDistortionUV[ind] = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexBlendUVDistortionUV(U& v, float value, int32_t ind)
{
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexFlipbookIndexAndNextRate(U& v, float value)
{
	v.FlipbookIndexAndNextRate = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexFlipbookIndexAndNextRate(U& v, float value)
{
}

template <typename U, enable_if_contain_advanced_t<U> = nullptr>
void SetVertexAlphaThreshold(U& v, float value)
{
	v.AlphaThreshold = value;
}

template <typename U, enable_ifnot_contain_advanced_t<U> = nullptr>
void SetVertexAlphaThreshold(U& v, float value)
{
}

inline int32_t GetMaximumVertexSizeInAllTypes()
{
	size_t size = sizeof(DynamicVertexWithCustomData);
	size = (std::max)(size, sizeof(SimpleVertex));
	size = (std::max)(size, sizeof(LightingVertex));
	size = (std::max)(size, sizeof(AdvancedSimpleVertex));
	size = (std::max)(size, sizeof(AdvancedLightingVertex));

	return static_cast<int32_t>(size);
};

template <typename T>
inline bool VertexNormalRequired()
{
	return false;
}

template <>
inline bool VertexNormalRequired<DynamicVertex>()
{
	return true;
}

template <>
inline bool VertexNormalRequired<LightingVertex>()
{
	return true;
}

template <>
inline bool VertexNormalRequired<AdvancedLightingVertex>()
{
	return true;
}

template <typename T>
inline bool VertexUV2Required()
{
	return false;
}

template <>
inline bool VertexUV2Required<DynamicVertex>()
{
	return true;
}

/**
	@brief	a view class to access an array with a stride
*/
template <typename T>
struct StrideView
{
	int32_t stride_;
	uint8_t* pointer_;
	uint8_t* pointerOrigin_;

#ifndef NDEBUG
	int32_t offset_;
	int32_t elementCount_;
#endif

	StrideView(void* pointer, int32_t stride, int32_t elementCount)
		: stride_(stride)
		, pointer_(reinterpret_cast<uint8_t*>(pointer))
		, pointerOrigin_(reinterpret_cast<uint8_t*>(pointer))
#ifndef NDEBUG
		, offset_(0)
		, elementCount_(elementCount)
#endif
	{
	}

	T& operator[](int i) const
	{
#ifndef NDEBUG
		assert(i >= 0);
		assert(i + offset_ < elementCount_);
#endif
		return *reinterpret_cast<T*>((pointer_ + stride_ * i));
	}

	StrideView& operator+=(const int& rhs)
	{
#ifndef NDEBUG
		offset_ += rhs;
#endif
		pointer_ += stride_ * rhs;
		return *this;
	}

	void Reset()
	{
#ifndef NDEBUG
		offset_ = 0;
#endif
		pointer_ = pointerOrigin_;
	}
};

std::array<std::array<float, 4>, 13> ToUniform(const Effekseer::Gradient& gradient);

void CalcBillboard(::Effekseer::BillboardType billboardType,
				   Effekseer::SIMD::Mat43f& dst,
				   ::Effekseer::SIMD::Vec3f& s,
				   ::Effekseer::SIMD::Vec3f& R,
				   ::Effekseer::SIMD::Vec3f& F,
				   const ::Effekseer::SIMD::Mat43f& src,
				   const ::Effekseer::SIMD::Vec3f& frontDirection);

void ApplyDepthParameters(::Effekseer::SIMD::Mat43f& mat,
						  const ::Effekseer::SIMD::Vec3f& cameraFront,
						  const ::Effekseer::SIMD::Vec3f& cameraPos,
						  ::Effekseer::NodeRendererDepthParameter* depthParameter,
						  bool isRightHand);

void ApplyDepthParameters(::Effekseer::SIMD::Mat43f& mat,
						  const ::Effekseer::SIMD::Vec3f& cameraFront,
						  const ::Effekseer::SIMD::Vec3f& cameraPos,
						  ::Effekseer::SIMD::Vec3f& scaleValues,
						  ::Effekseer::NodeRendererDepthParameter* depthParameter,
						  bool isRightHand);

void ApplyDepthParameters(::Effekseer::SIMD::Mat43f& mat,
						  ::Effekseer::SIMD::Vec3f& translationValues,
						  ::Effekseer::SIMD::Vec3f& scaleValues,
						  const ::Effekseer::SIMD::Vec3f& cameraFront,
						  const ::Effekseer::SIMD::Vec3f& cameraPos,
						  ::Effekseer::NodeRendererDepthParameter* depthParameter,
						  bool isRightHand);

void ApplyDepthParameters(::Effekseer::SIMD::Mat44f& mat,
						  const ::Effekseer::SIMD::Vec3f& cameraFront,
						  const ::Effekseer::SIMD::Vec3f& cameraPos,
						  ::Effekseer::NodeRendererDepthParameter* depthParameter,
						  bool isRightHand);

void ApplyViewOffset(::Effekseer::SIMD::Mat43f& mat,
					 const ::Effekseer::SIMD::Mat44f& camera,
					 float distance);

void ApplyViewOffset(::Effekseer::SIMD::Mat44f& mat,
					 const ::Effekseer::SIMD::Mat44f& camera,
					 float distance);

struct ZFixedTransformBlock
{
	Effekseer::SIMD::Float4 m0;
	Effekseer::SIMD::Float4 m1;
	Effekseer::SIMD::Float4 center;

	ZFixedTransformBlock(const ::Effekseer::SIMD::Mat43f& mat, float z)
	{
		using namespace Effekseer::SIMD;

		m0 = mat.X;
		m1 = mat.Y;
		auto m2 = mat.Z;
		center = Float4::SetZero();
		Float4::Transpose(m0, m1, m2, center);
		center = center + m2 * z;
	}

	void Transform(Effekseer::SIMD::Vec3f& data)
	{
		using namespace Effekseer::SIMD;

		Float4 oPos = Float4::MulAddLane<0>(center, m0, data.s);
		data.s = Float4::MulAddLane<1>(oPos, m1, data.s);
	}
};

template <typename Vertex>
inline void TransformVertexes(Vertex& vertexes, int32_t count, const ::Effekseer::SIMD::Mat43f& mat)
{
	using namespace Effekseer::SIMD;

	Float4 m0 = mat.X;
	Float4 m1 = mat.Y;
	Float4 m2 = mat.Z;
	Float4 m3 = Float4::SetZero();
	Float4::Transpose(m0, m1, m2, m3);

	for (int i = 0; i < count; i++)
	{
		Float4 iPos = Float4::Load3(&vertexes[i].Pos);

		Float4 oPos = Float4::MulAddLane<0>(m3, m0, iPos);
		oPos = Float4::MulAddLane<1>(oPos, m1, iPos);
		oPos = Float4::MulAddLane<2>(oPos, m2, iPos);

		Float4::Store3(&vertexes[i].Pos, oPos);
	}
}

inline Effekseer::SIMD::Vec3f SafeNormalize(const Effekseer::SIMD::Vec3f& v)
{
	auto lengthSq = v.GetSquaredLength();
	auto e = 0.0001f;
	if (lengthSq < e * e)
	{
		return v;
	}

	return v * Effekseer::SIMD::Rsqrt(lengthSq);
}

struct MaterialShaderParameterGenerator
{
	int32_t VertexSize = 0;
	int32_t VertexShaderUniformBufferSize = 0;
	int32_t PixelShaderUniformBufferSize = 0;

	int32_t VertexCameraMatrixOffset = -1;
	int32_t VertexProjectionMatrixOffset = -1;
	int32_t VertexInversedFlagOffset = -1;
	int32_t VertexPredefinedOffset = -1;
	int32_t VertexCameraPositionOffset = -1;
	int32_t VertexUserUniformOffset = -1;

	int32_t PixelInversedFlagOffset = -1;
	int32_t PixelPredefinedOffset = -1;
	int32_t PixelCameraPositionOffset = -1;
	int32_t PixelReconstructionParam1Offset = -1;
	int32_t PixelReconstructionParam2Offset = -1;
	int32_t PixelLightDirectionOffset = -1;
	int32_t PixelLightColorOffset = -1;
	int32_t PixelLightAmbientColorOffset = -1;
	int32_t PixelCameraMatrixOffset = -1;
	int32_t PixelUserUniformOffset = -1;

	int32_t VertexModelMatrixOffset = -1;
	int32_t VertexModelUVOffset = -1;
	int32_t VertexModelColorOffset = -1;

	int32_t VertexModelCustomData1Offset = -1;
	int32_t VertexModelCustomData2Offset = -1;

	MaterialShaderParameterGenerator(const ::Effekseer::MaterialFile& materialFile, bool isModel, int32_t stage, int32_t instanceCount)
	{
		if (isModel)
		{
			VertexSize = sizeof(::Effekseer::Model::Vertex);
		}
		else if (materialFile.GetIsSimpleVertex())
		{
			VertexSize = sizeof(EffekseerRenderer::SimpleVertex);
		}
		else
		{
			VertexSize = sizeof(EffekseerRenderer::DynamicVertex) +
						 sizeof(float) * (materialFile.GetCustomData1Count() + materialFile.GetCustomData2Count());
		}

		if (isModel)
		{
			int32_t vsOffset = 0;
			VertexProjectionMatrixOffset = vsOffset;
			vsOffset += sizeof(Effekseer::SIMD::Mat44f);

			VertexModelMatrixOffset = vsOffset;
			vsOffset += sizeof(Effekseer::SIMD::Mat44f) * instanceCount;

			VertexModelUVOffset = vsOffset;
			vsOffset += sizeof(float) * 4 * instanceCount;

			VertexModelColorOffset = vsOffset;
			vsOffset += sizeof(float) * 4 * instanceCount;

			VertexInversedFlagOffset = vsOffset;
			vsOffset += sizeof(float) * 4;

			VertexPredefinedOffset = vsOffset;
			vsOffset += sizeof(float) * 4;

			VertexCameraPositionOffset = vsOffset;
			vsOffset += sizeof(float) * 4;

			if (materialFile.GetCustomData1Count() > 0)
			{
				VertexModelCustomData1Offset = vsOffset;
				vsOffset += sizeof(float) * 4 * instanceCount;
			}

			if (materialFile.GetCustomData2Count() > 0)
			{
				VertexModelCustomData2Offset = vsOffset;
				vsOffset += sizeof(float) * 4 * instanceCount;
			}

			VertexUserUniformOffset = vsOffset;
			vsOffset += sizeof(float) * 4 * materialFile.GetUniformCount();

			// TODO : remove magic number
			vsOffset += sizeof(float) * 4 * 13 * materialFile.Gradients.size();

			VertexShaderUniformBufferSize = vsOffset;
		}
		else
		{
			int32_t vsOffset = 0;
			VertexCameraMatrixOffset = vsOffset;
			vsOffset += sizeof(Effekseer::SIMD::Mat44f);

			VertexProjectionMatrixOffset = vsOffset;
			vsOffset += sizeof(Effekseer::SIMD::Mat44f);

			VertexInversedFlagOffset = vsOffset;
			vsOffset += sizeof(float) * 4;

			VertexPredefinedOffset = vsOffset;
			vsOffset += sizeof(float) * 4;

			VertexCameraPositionOffset = vsOffset;
			vsOffset += sizeof(float) * 4;

			VertexUserUniformOffset = vsOffset;
			vsOffset += sizeof(float) * 4 * materialFile.GetUniformCount();

			// TODO : remove magic number
			vsOffset += sizeof(float) * 4 * 13 * materialFile.Gradients.size();

			VertexShaderUniformBufferSize = vsOffset;
		}

		int32_t psOffset = 0;

		PixelInversedFlagOffset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelPredefinedOffset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelCameraPositionOffset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelReconstructionParam1Offset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelReconstructionParam2Offset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelLightDirectionOffset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelLightColorOffset = psOffset;
		psOffset += sizeof(float) * 4;

		PixelLightAmbientColorOffset = psOffset;
		psOffset += sizeof(float) * 4;

		if (materialFile.GetHasRefraction() && stage == 1)
		{
			PixelCameraMatrixOffset = psOffset;
			psOffset += sizeof(Effekseer::SIMD::Mat44f);
		}

		PixelUserUniformOffset = psOffset;
		psOffset += sizeof(float) * 4 * materialFile.GetUniformCount();

		// TODO : remove magic number
		psOffset += sizeof(float) * 4 * 13 * materialFile.Gradients.size();

		PixelShaderUniformBufferSize = psOffset;
	}
};

enum class RendererShaderType
{
	Unlit,
	Lit,
	BackDistortion,
	AdvancedUnlit,
	AdvancedLit,
	AdvancedBackDistortion,
	Material,
};

struct ShaderParameterCollector
{
	RendererShaderType ShaderType{};

	Effekseer::MaterialRenderData* MaterialRenderDataPtr = nullptr;
	Effekseer::MaterialRef MaterialDataPtr = nullptr;

	int32_t TextureCount = 0;
	std::array<::Effekseer::Backend::TextureRef, Effekseer::TextureSlotMax> Textures;
	std::array<::Effekseer::TextureFilterType, Effekseer::TextureSlotMax> TextureFilterTypes;
	std::array<::Effekseer::TextureWrapType, Effekseer::TextureSlotMax> TextureWrapTypes;

	bool IsDepthRequired = false;
	bool IsBackgroundRequiredOnFirstPass = false;
	bool HasMultiPass = false;
	int32_t BackgroundIndex = -1;
	int32_t DepthIndex = -1;

	bool DoRequireAdvancedRenderer() const
	{
		return ShaderType == RendererShaderType::AdvancedUnlit ||
			   ShaderType == RendererShaderType::AdvancedLit ||
			   ShaderType == RendererShaderType::AdvancedBackDistortion;
	}

	bool operator!=(const ShaderParameterCollector& state) const
	{
		if (ShaderType != state.ShaderType)
			return true;

		if (MaterialRenderDataPtr != state.MaterialRenderDataPtr)
			return true;

		if (MaterialDataPtr != state.MaterialDataPtr)
			return true;

		if (IsBackgroundRequiredOnFirstPass != state.IsBackgroundRequiredOnFirstPass)
			return true;

		if (HasMultiPass != state.HasMultiPass)
			return true;

		if (BackgroundIndex != state.BackgroundIndex)
			return true;

		if (TextureCount != state.TextureCount)
			return true;

		for (int32_t i = 0; i < TextureCount; i++)
		{
			if (Textures[i] != state.Textures[i])
				return true;

			if (TextureFilterTypes[i] != state.TextureFilterTypes[i])
				return true;

			if (TextureWrapTypes[i] != state.TextureWrapTypes[i])
				return true;
		}

		return false;
	}

	void Collect(Renderer* renderer, Effekseer::Effect* effect, Effekseer::NodeRendererBasicParameter* param, bool edgeFalloff, bool isSoftParticleEnabled)
	{
		::Effekseer::Backend::TextureRef TexturePtr = nullptr;
		::Effekseer::Backend::TextureRef NormalTexturePtr = nullptr;
		::Effekseer::Backend::TextureRef AlphaTexturePtr = nullptr;
		::Effekseer::Backend::TextureRef UVDistortionTexturePtr = nullptr;
		::Effekseer::Backend::TextureRef BlendTexturePtr = nullptr;
		::Effekseer::Backend::TextureRef BlendAlphaTexturePtr = nullptr;
		::Effekseer::Backend::TextureRef BlendUVDistortionTexturePtr = nullptr;

		Textures.fill(nullptr);
		TextureFilterTypes.fill(::Effekseer::TextureFilterType::Linear);
		TextureWrapTypes.fill(::Effekseer::TextureWrapType::Repeat);

		BackgroundIndex = -1;
		IsBackgroundRequiredOnFirstPass = false;

		DepthIndex = -1;
		IsDepthRequired = isSoftParticleEnabled;
		MaterialRenderDataPtr = nullptr;

		auto isMaterial = param->MaterialType == ::Effekseer::RendererMaterialType::File && param->MaterialRenderDataPtr != nullptr && renderer->GetRenderMode() == Effekseer::RenderMode::Normal;

		if (isMaterial)
		{
			MaterialDataPtr = effect->GetMaterial(param->MaterialRenderDataPtr->MaterialIndex);

			if (MaterialDataPtr == nullptr)
			{
				isMaterial = false;
			}

			if (isMaterial && MaterialDataPtr->IsSimpleVertex)
			{
				isMaterial = false;
			}

			// Validate parameters
			if (isMaterial && (MaterialDataPtr->TextureCount != param->MaterialRenderDataPtr->MaterialTextures.size() ||
							   MaterialDataPtr->UniformCount != param->MaterialRenderDataPtr->MaterialUniforms.size()))
			{
				isMaterial = false;
			}
		}

		auto isAdvanced = param->GetIsRenderedWithAdvancedRenderer() || edgeFalloff;

		if (isMaterial)
		{
			IsDepthRequired = true;
		}

		// TODO : refactor in 1.7
		const auto whiteMode = renderer->GetRenderMode() == Effekseer::RenderMode::Wireframe || renderer->GetExternalShaderSettings() != nullptr;
		if (whiteMode)
		{
			ShaderType = RendererShaderType::Unlit;
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::File && isMaterial)
		{
			MaterialRenderDataPtr = param->MaterialRenderDataPtr;
			if (MaterialRenderDataPtr != nullptr)
			{
				MaterialDataPtr = effect->GetMaterial(MaterialRenderDataPtr->MaterialIndex);
				if (MaterialDataPtr != nullptr)
				{
					ShaderType = RendererShaderType::Material;
					IsBackgroundRequiredOnFirstPass = MaterialDataPtr->IsRefractionRequired;

					if (IsBackgroundRequiredOnFirstPass)
					{
						HasMultiPass = true;
					}
				}
			}
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::Lighting && isAdvanced)
		{
			ShaderType = RendererShaderType::AdvancedLit;
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion && isAdvanced)
		{
			ShaderType = RendererShaderType::AdvancedBackDistortion;
			IsBackgroundRequiredOnFirstPass = true;
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::Default && isAdvanced)
		{
			ShaderType = RendererShaderType::AdvancedUnlit;
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::Lighting)
		{
			ShaderType = RendererShaderType::Lit;
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
		{
			ShaderType = RendererShaderType::BackDistortion;
			IsBackgroundRequiredOnFirstPass = true;
		}
		else if (param->MaterialType == ::Effekseer::RendererMaterialType::Default)
		{
			ShaderType = RendererShaderType::Unlit;
		}
		else
		{
			// Fallback
			ShaderType = RendererShaderType::Unlit;
		}

		// TODO : refactor in 1.7
		if (whiteMode)
		{
			TextureCount = 1;
			Textures[0] = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::White);
			TextureFilterTypes[0] = param->TextureFilters[0];
			TextureWrapTypes[0] = param->TextureWraps[0];

			if (IsDepthRequired)
			{
				DepthIndex = TextureCount;
				TextureCount += 1;
			}
		}
		else if (isMaterial)
		{
			TextureCount = static_cast<int32_t>(Effekseer::Min(MaterialRenderDataPtr->MaterialTextures.size(), ::Effekseer::UserTextureSlotMax));
			for (int32_t i = 0; i < TextureCount; i++)
			{
				if (MaterialRenderDataPtr->MaterialTextures[i].Type == 1)
				{
					if (MaterialRenderDataPtr->MaterialTextures[i].Index >= 0)
					{
						auto resource = effect->GetNormalImage(MaterialRenderDataPtr->MaterialTextures[i].Index);
						Textures[i] = (resource != nullptr) ? resource->GetBackend() : nullptr;
					}
					else
					{
						Textures[i] = nullptr;
					}
				}
				else
				{
					if (MaterialRenderDataPtr->MaterialTextures[i].Index >= 0)
					{
						auto resource = effect->GetColorImage(MaterialRenderDataPtr->MaterialTextures[i].Index);
						Textures[i] = (resource != nullptr) ? resource->GetBackend() : nullptr;
					}
					else
					{
						Textures[i] = nullptr;
					}
				}

				TextureFilterTypes[i] = Effekseer::TextureFilterType::Linear;
				TextureWrapTypes[i] = MaterialDataPtr->TextureWrapTypes[i];
			}

			if (IsBackgroundRequiredOnFirstPass)
			{
				// Store from external
				TextureFilterTypes[TextureCount] = Effekseer::TextureFilterType::Linear;
				TextureWrapTypes[TextureCount] = Effekseer::TextureWrapType::Clamp;
				BackgroundIndex = TextureCount;
			}
			TextureCount += 1;

			if (IsDepthRequired)
			{
				// Store from external
				TextureFilterTypes[TextureCount] = Effekseer::TextureFilterType::Linear;
				TextureWrapTypes[TextureCount] = Effekseer::TextureWrapType::Clamp;
				DepthIndex = TextureCount;
				TextureCount += 1;
			}
		}
		else
		{
			if (isAdvanced)
			{
				if (param->MaterialType == ::Effekseer::RendererMaterialType::Default)
				{
					TextureCount = 6;
				}
				else
				{
					TextureCount = 7;
				}

				if (IsDepthRequired)
				{
					DepthIndex = TextureCount;
					TextureCount += 1;
				}
			}
			else
			{
				if (param->MaterialType == ::Effekseer::RendererMaterialType::Default)
				{
					TextureCount = 1;
				}
				else
				{
					TextureCount = 2;
				}

				if (IsDepthRequired)
				{
					DepthIndex = TextureCount;
					TextureCount += 1;
				}
			}

			// color/distortion
			if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
			{
				auto resource = effect->GetDistortionImage(param->TextureIndexes[0]);
				TexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
			}
			else
			{
				auto resource = effect->GetColorImage(param->TextureIndexes[0]);
				TexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
			}

			if (TexturePtr == nullptr && renderer != nullptr)
			{
				TexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::White);
			}

			Textures[0] = TexturePtr;
			TextureFilterTypes[0] = param->TextureFilters[0];
			TextureWrapTypes[0] = param->TextureWraps[0];

			// normal/background
			if (param->MaterialType != ::Effekseer::RendererMaterialType::Default)
			{
				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					// Store from external
					IsBackgroundRequiredOnFirstPass = true;
					BackgroundIndex = 1;
				}
				else if (param->MaterialType == ::Effekseer::RendererMaterialType::Lighting)
				{
					auto resource = effect->GetNormalImage(param->TextureIndexes[1]);
					NormalTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;

					if (NormalTexturePtr == nullptr && renderer != nullptr)
					{
						NormalTexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::Normal);
					}

					Textures[1] = NormalTexturePtr;
				}

				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					TextureFilterTypes[1] = Effekseer::TextureFilterType::Linear;
					TextureWrapTypes[1] = Effekseer::TextureWrapType::Clamp;
				}
				else
				{
					TextureFilterTypes[1] = param->TextureFilters[1];
					TextureWrapTypes[1] = param->TextureWraps[1];
				}
			}

			if (isAdvanced)
			{
				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					auto resource = effect->GetDistortionImage(param->TextureIndexes[2]);
					AlphaTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}
				else
				{
					auto resource = effect->GetColorImage(param->TextureIndexes[2]);
					AlphaTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}

				if (AlphaTexturePtr == nullptr && renderer != nullptr)
				{
					AlphaTexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::White);
				}

				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					auto resource = effect->GetDistortionImage(param->TextureIndexes[3]);
					UVDistortionTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}
				else
				{
					auto resource = effect->GetColorImage(param->TextureIndexes[3]);
					UVDistortionTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}

				if (UVDistortionTexturePtr == nullptr && renderer != nullptr)
				{
					UVDistortionTexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::Normal);
				}

				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					auto resource = effect->GetDistortionImage(param->TextureIndexes[4]);
					BlendTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}
				else
				{
					auto resource = effect->GetColorImage(param->TextureIndexes[4]);
					BlendTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}

				if (BlendTexturePtr == nullptr && renderer != nullptr)
				{
					BlendTexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::White);
				}

				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					auto resource = effect->GetDistortionImage(param->TextureIndexes[5]);
					BlendAlphaTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}
				else
				{
					auto resource = effect->GetColorImage(param->TextureIndexes[5]);
					BlendAlphaTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}

				if (BlendAlphaTexturePtr == nullptr && renderer != nullptr)
				{
					BlendAlphaTexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::White);
				}

				if (param->MaterialType == ::Effekseer::RendererMaterialType::BackDistortion)
				{
					auto resource = effect->GetDistortionImage(param->TextureIndexes[6]);
					BlendUVDistortionTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}
				else
				{
					auto resource = effect->GetColorImage(param->TextureIndexes[6]);
					BlendUVDistortionTexturePtr = (resource != nullptr) ? resource->GetBackend() : nullptr;
				}

				if (BlendUVDistortionTexturePtr == nullptr && renderer != nullptr)
				{
					BlendUVDistortionTexturePtr = renderer->GetImpl()->GetProxyTexture(EffekseerRenderer::ProxyTextureType::Normal);
				}

				int offset = 1;

				if (param->MaterialType != ::Effekseer::RendererMaterialType::Default)
				{
					offset += 1;
				}

				Textures[offset + 0] = AlphaTexturePtr;
				TextureFilterTypes[offset + 0] = param->TextureFilters[2];
				TextureWrapTypes[offset + 0] = param->TextureWraps[2];

				Textures[offset + 1] = UVDistortionTexturePtr;
				TextureFilterTypes[offset + 1] = param->TextureFilters[3];
				TextureWrapTypes[offset + 1] = param->TextureWraps[3];

				Textures[offset + 2] = BlendTexturePtr;
				TextureFilterTypes[offset + 2] = param->TextureFilters[4];
				TextureWrapTypes[offset + 2] = param->TextureWraps[4];

				Textures[offset + 3] = BlendAlphaTexturePtr;
				TextureFilterTypes[offset + 3] = param->TextureFilters[5];
				TextureWrapTypes[offset + 3] = param->TextureWraps[5];

				Textures[offset + 4] = BlendUVDistortionTexturePtr;
				TextureFilterTypes[offset + 4] = param->TextureFilters[6];
				TextureWrapTypes[offset + 4] = param->TextureWraps[6];
			}
		}
	}
};

struct SoftParticleParameter
{
	std::array<float, 4> softParticleParams;
	std::array<float, 4> reconstructionParam1;
	std::array<float, 4> reconstructionParam2;

	void SetParam(float distanceFar, float distanceNear, float distanceNearOffset, float magnification, float rescale1, float rescale2, float v33, float v34, float v43, float v44)
	{
		softParticleParams[0] = distanceFar * magnification;
		softParticleParams[1] = distanceNear * magnification;
		softParticleParams[2] = distanceNearOffset * magnification;
		softParticleParams[3] = distanceFar != 0.0f || distanceNear != 0.0f || distanceNearOffset != 0.0f ? 1.0f : 0.0f;

		reconstructionParam1[0] = rescale1;
		reconstructionParam1[1] = rescale2;

		reconstructionParam2[0] = v33;
		reconstructionParam2[1] = v34;
		reconstructionParam2[2] = v43;
		reconstructionParam2[3] = v44;
	}
};

struct FlipbookParameter
{
	union
	{
		float Buffer[4];

		struct
		{
			float EnableInterpolation;
			float InterpolationType;
		};
	};
};

struct UVDistortionParameter
{
	union
	{
		float Buffer[4];

		struct
		{
			float Intensity;
			float BlendIntensity;
			float UVInversed[2];
		};
	};
};

struct BlendTextureParameter
{
	union
	{
		float Buffer[4];

		struct
		{
			float BlendType;
		};
	};
};

struct EmmisiveParameter
{
	union
	{
		float Buffer[4];

		struct
		{
			float EmissiveScaling;
		};
	};
};

struct EdgeParameter
{
	std::array<float, 4> EdgeColor;

	union
	{
		float Buffer[4];

		struct
		{
			float Threshold;
			float ColorScaling;
		};
	};
};

struct FalloffParameter
{
	union
	{
		float Buffer[4];

		struct
		{
			float Enable;
			float ColorBlendType;
			float Pow;
		};
	};

	std::array<float, 4> BeginColor;
	std::array<float, 4> EndColor;
};

struct PixelConstantBuffer
{
	//! Lit only
	std::array<float, 4> LightDirection;
	std::array<float, 4> LightColor;
	std::array<float, 4> LightAmbientColor;

	FlipbookParameter FlipbookParam;
	UVDistortionParameter UVDistortionParam;
	BlendTextureParameter BlendTextureParam;

	//! model only
	float CameraFrontDirection[4];

	//! model only
	FalloffParameter FalloffParam;

	EmmisiveParameter EmmisiveParam;
	EdgeParameter EdgeParam;
	SoftParticleParameter SoftParticleParam;
	float UVInversedBack[4];
	std::array<float, 4> MiscFlags;

	void SetModelFlipbookParameter(float enableInterpolation, float interpolationType)
	{
		FlipbookParam.EnableInterpolation = enableInterpolation;
		FlipbookParam.InterpolationType = interpolationType;
	}

	void SetModelUVDistortionParameter(float intensity, float blendIntensity, const std::array<float, 2>& uvInversed)
	{
		UVDistortionParam.Intensity = intensity;
		UVDistortionParam.BlendIntensity = blendIntensity;
		UVDistortionParam.UVInversed[0] = uvInversed[0];
		UVDistortionParam.UVInversed[1] = uvInversed[1];
	}

	void SetModelBlendTextureParameter(float blendType)
	{
		BlendTextureParam.BlendType = blendType;
	}

	void SetCameraFrontDirection(float x, float y, float z)
	{
		CameraFrontDirection[0] = x;
		CameraFrontDirection[1] = y;
		CameraFrontDirection[2] = z;
		CameraFrontDirection[3] = 0.0f;
	}

	void SetFalloffParameter(float enable, float colorBlendType, float pow, const std::array<float, 4>& beginColor, const std::array<float, 4>& endColor)
	{
		FalloffParam.Enable = enable;
		FalloffParam.ColorBlendType = colorBlendType;
		FalloffParam.Pow = pow;

		for (size_t i = 0; i < 4; i++)
		{
			FalloffParam.BeginColor[i] = beginColor[i];
		}

		for (size_t i = 0; i < 4; i++)
		{
			FalloffParam.EndColor[i] = endColor[i];
		}
	}

	void SetEmissiveScaling(float emissiveScaling)
	{
		EmmisiveParam.EmissiveScaling = emissiveScaling;
	}

	void SetEdgeParameter(const std::array<float, 4>& edgeColor, float threshold, float colorScaling)
	{
		for (size_t i = 0; i < 4; i++)
		{
			EdgeParam.EdgeColor[i] = edgeColor[i];
		}
		EdgeParam.Threshold = threshold;
		EdgeParam.ColorScaling = colorScaling;
	}
};

struct PixelConstantBufferDistortion
{
	float DistortionIntencity[4];
	float UVInversedBack[4];

	//! unused in none advanced renderer
	FlipbookParameter FlipbookParam;
	UVDistortionParameter UVDistortionParam;
	BlendTextureParameter BlendTextureParam;
	SoftParticleParameter SoftParticleParam;
};

void CalculateAlignedTextureInformation(Effekseer::Backend::TextureFormatType format, const std::array<int, 2>& size, int32_t& sizePerWidth, int32_t& height);

//! only support OpenGL
Effekseer::Backend::VertexLayoutRef GetVertexLayout(Effekseer::Backend::GraphicsDeviceRef graphicsDevice, RendererShaderType type);

struct FlipbookVertexBuffer
{
	union
	{
		float Buffer[8];

		struct
		{
			float enableInterpolation;
			float loopType;
			float divideX;
			float divideY;
			float onesizeX;
			float onesizeY;
			float offsetX;
			float offsetY;
		};
	};
};

struct RendererStateFlipbook
{
	int32_t EnableInterpolation = 0;
	int32_t UVLoopType = 0;
	int32_t InterpolationType = 0;
	int32_t FlipbookDivideX = 0;
	int32_t FlipbookDivideY = 0;
	float OneSizeX = 0;
	float OneSizeY = 0;
	float OffsetX = 0;
	float OffsetY = 0;

	bool operator==(const RendererStateFlipbook& state) const
	{
		return !(*this != state);
	}

	bool operator!=(const RendererStateFlipbook& state) const
	{
		if (EnableInterpolation != state.EnableInterpolation)
			return true;
		if (UVLoopType != state.UVLoopType)
			return true;
		if (InterpolationType != state.InterpolationType)
			return true;
		if (FlipbookDivideX != state.FlipbookDivideX)
			return true;
		if (FlipbookDivideY != state.FlipbookDivideY)
			return true;
		if (OneSizeX != state.OneSizeX)
			return true;
		if (OneSizeY != state.OneSizeY)
			return true;
		if (OffsetX != state.OffsetX)
			return true;
		if (OffsetY != state.OffsetY)
			return true;
		return false;
	}
};

inline FlipbookVertexBuffer ToVertexBuffer(const RendererStateFlipbook& state)
{
	FlipbookVertexBuffer ret;
	ret.enableInterpolation = static_cast<float>(state.EnableInterpolation);
	ret.loopType = static_cast<float>(state.UVLoopType);
	ret.divideX = static_cast<float>(state.FlipbookDivideX);
	ret.divideY = static_cast<float>(state.FlipbookDivideY);
	ret.onesizeX = state.OneSizeX;
	ret.onesizeY = state.OneSizeY;
	ret.offsetX = state.OffsetX;
	ret.offsetY = state.OffsetY;
	return ret;
}

inline RendererStateFlipbook ToState(const Effekseer::NodeRendererFlipbookParameter& param)
{
	RendererStateFlipbook ret;
	ret.EnableInterpolation = param.EnableInterpolation;
	ret.UVLoopType = param.UVLoopType;
	ret.InterpolationType = param.InterpolationType;
	ret.FlipbookDivideX = param.FlipbookDivideX;
	ret.FlipbookDivideY = param.FlipbookDivideY;
	ret.OneSizeX = param.OneSize[0];
	ret.OneSizeY = param.OneSize[1];
	ret.OffsetX = param.Offset[0];
	ret.OffsetY = param.Offset[1];
	return ret;
}

} // namespace EffekseerRenderer
#endif // __EFFEKSEERRENDERER_COMMON_UTILS_H__