
#include "Tfire1.h"

// shader en fin

// -----------------------
// --- le constructeur ---
// -----------------------

Tfire1::Tfire1()
{
	//
}

// ----------------------
// --- le destructeur ---
// ----------------------

Tfire1::~Tfire1()
{
    if (m_vao_buffer[0] != 0) glDeleteBuffers(m_nbr_vao_buffer, m_vao_buffer);
    if (m_vao != 0) glDeleteVertexArrays(1, &m_vao);
    m_vao = 0;
}

// --------------------
// --- les méthodes ---
// --------------------

// pos.xyz, scale horizontal vertical
void Tfire1::addModel(float px, float py, float pz, float sw, float sh)
{
    size_t idx = m_pos.size();

    m_pos.push_back(OVec3_f());
    m_scale.push_back(OVec3_f());

	m_pos[idx].set(px,py,pz);
	m_scale[idx].set(1.0f, sw, sh);

	// il y a trois plans par position
    _setModel(m_pos[idx], m_scale[idx]);
}

void Tfire1::addModel(std::string flux)
{
    std::vector<std::string> strList;
    size_t idx = m_pos.size();

    ECO_strCut(strList, flux, ' ');

    m_pos.push_back(OVec3_f());
    m_scale.push_back(OVec3_f());

	m_pos[idx].x = ECO_strToFloat(strList[0]);
	m_pos[idx].y = ECO_strToFloat(strList[1]);
	m_pos[idx].z = ECO_strToFloat(strList[2]);
    m_scale[idx].x = 1.0f;
	m_scale[idx].y = ECO_strToFloat(strList[3]);
	m_scale[idx].z = ECO_strToFloat(strList[4]);

    _setModel(m_pos[idx], m_scale[idx]);
}

void Tfire1::addModel(std::ifstream& flux)
{
    std::string sep;
    size_t idx = m_pos.size();

    m_pos.push_back(OVec3_f());
    m_scale.push_back(OVec3_f());

    flux >> m_pos[idx].x >> m_pos[idx].y >> m_pos[idx].z;
    flux >> m_scale[idx].y >> m_scale[idx].z >> sep;
    m_scale[idx].x = 1.0f;

    _setModel(m_pos[idx], m_scale[idx]);
}




// fire image/ / px py pz w h / fire.png noise.png alpha.png /
void Tfire1::create(std::string flux)
{
    std::vector<std::string> strList;

    // ---

    shFire.create();

    // ---

    float w = 0.5f, h = 1.0f;

    // posé au sol /  face à l'est
    m_vertex[0].set(0.0f, -w, 0.0f);
    m_vertex[1].set(0.0f,  w, 0.0f);
    m_vertex[2].set(0.0f,  w, h);
    m_vertex[3].set(0.0f, -w, h);

    m_uv[0].set(0, 0);
    m_uv[1].set(1, 0);
    m_uv[2].set(1, 1);
    m_uv[3].set(0, 1);

    m_indice[0] = 0;
    m_indice[1] = 1;
    m_indice[2] = 2;
    m_indice[3] = 3;

    // ---

    m_pos.clear();
    m_scale.clear();
    m_model_multi.clear();
    //
    m_pos.push_back(OVec3_f());
    m_scale.push_back(OVec3_f());

    // ---

    ECO_strCut(strList, flux, ' ');

	m_code = strList[0];
	m_path = strList[1];
	// 2
	m_pos[0].x = ECO_strToFloat(strList[3]);
	m_pos[0].y = ECO_strToFloat(strList[4]);
	m_pos[0].z = ECO_strToFloat(strList[5]);
	m_scale[0].x = 1.0f;
    m_scale[0].y = ECO_strToFloat(strList[6]);   // plan construit face à l'est
	m_scale[0].z = ECO_strToFloat(strList[7]);   // verticale
    // 8
    m_str_fire = strList[9];
    m_str_noise = strList[10];
    m_str_alpha = strList[11];
    // 12

    _vao();
    _setModel(m_pos[0], m_scale[0]);

    // ---

    OTextureList::P_setPath(m_path);
    textureList.add(m_str_fire);
    textureList.add(m_str_noise);
    textureList.add(m_str_alpha);

    m_tex_fire = textureList.getParam(m_str_fire);
    m_tex_noise = textureList.getParam(m_str_noise);
    m_tex_alpha = textureList.getParam(m_str_alpha);
    //ECO_error_set("alpha %s %d", m_str_alpha.c_str(), m_tex_alpha);

}

void Tfire1::draw(OMat4& vp)
{
    static float time = 0.0f;
    OMat4 model;

    time += 0.05f; // 1/fps

    glDisable(GL_CULL_FACE);
    //glDepthMask(GL_FALSE);

    // rappeller les effets de transparence
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    shFire.enable();
        shFire.setVP(vp);
        //shFire.setMulti();
        shFire.setTime(time);
        shFire.setTextures(m_tex_fire.id, m_tex_noise.id, m_tex_alpha.id);

        glBindVertexArray(m_vao);
        glDrawElementsInstanced(GL_QUADS, m_model_multi.size()*4, GL_UNSIGNED_INT, 0, m_model_multi.size());

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //glDepthMask(GL_TRUE);

}




void Tfire1::save(std::ofstream& flux)
{
    flux << FLUX_FIRE1 << " ";

    // fire image/ / px py pz w h / fire.png noise.png alpha.png /
    flux << m_code << " " << m_path << " / ";
    flux << m_pos[0].x << " " << m_pos[0].y << " " << m_pos[0].z << " ";
    flux << m_scale[0].y << " " << m_scale[0].z << " / ";
    flux << m_str_fire << " " << m_str_noise << " " << m_str_alpha << " /" << std::endl;

    // instances
    for (size_t idx = 1; idx < m_pos.size(); idx++)
    {
        flux << FLUX_FIRE1_MOD << " ";
        flux << m_pos[idx].x << " " << m_pos[idx].y << " " << m_pos[idx].z << " ";
        flux << m_scale[idx].y << " " << m_scale[idx].z << " /" << std::endl;
    }

}

void Tfire1::_setModel(OVec3_f& pos, OVec3_f& scale)
{
    size_t idx = m_model_multi.size();
    float rot = 0.0f;
    //ECO_error_set("scale %f %f %f", scale.x, scale.y, scale.z);

    for (int i = 0; i < 3; i++)
    {
        m_model_multi.push_back(OMat4());

        m_model_multi[idx].loadIdentity();
        m_model_multi[idx].translate(pos);
        m_model_multi[idx].rotate(rot, 0,0,1);
        m_model_multi[idx].scale(scale);

        m_model_multi[idx].transpose(); // mat horizontale

        idx++;
        rot += 60.0f;
    }

    // ---

    glBindVertexArray(m_vao);
    glBindBuffer(GL_ARRAY_BUFFER, m_vao_buffer[LOCATION_MODEL]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(OMat4) * m_model_multi.size(), &m_model_multi[0], GL_DYNAMIC_DRAW);

    //désactivation du VAO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void Tfire1::_vao()
{
    // >> on respecte l'ordre général de nos buffers (normal pas utile)
    int nbr = 4; // (4 points par plan)

    //génération de l'identifiant du VAO
    glGenVertexArrays(1, &m_vao);
    //activation du VAO
    glBindVertexArray(m_vao);
    // créations des buffers
    glGenBuffers(m_nbr_vao_buffer, m_vao_buffer);

    glBindBuffer(GL_ARRAY_BUFFER, m_vao_buffer[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(OVec3_f) * nbr, m_vertex, GL_STATIC_DRAW);
    glEnableVertexAttribArray(LOCATION_POSITION);
    glVertexAttribPointer(LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_vao_buffer[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(OVec3_f) * nbr, m_normal, GL_STATIC_DRAW);
    glEnableVertexAttribArray(LOCATION_NORMAL);
    glVertexAttribPointer(LOCATION_NORMAL, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_vao_buffer[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(OVec2_f) * nbr, m_uv, GL_STATIC_DRAW);
    glEnableVertexAttribArray(LOCATION_TEXCOORD);
    glVertexAttribPointer(LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_vao_buffer[LOCATION_MODEL]);
        for (int i = 0; i < 4 ; i++)
        {
            glEnableVertexAttribArray(LOCATION_MODEL + i);
            glVertexAttribPointer(LOCATION_MODEL + i, 4, GL_FLOAT, GL_FALSE, sizeof(OMat4), (const void*)(sizeof(float) * i * 4));
            glVertexAttribDivisor(LOCATION_MODEL + i, 1);
        }

    // ---

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vao_buffer[7]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * nbr, m_indice, GL_STATIC_DRAW);
}






// --- shader ---

static const char* pVS = " \n\
#version 330 core   \n\
 \n\
layout (location = 0) in vec3 in_vertex; \n\
layout (location = 1) in vec3 in_normal; \n\
layout (location = 2) in vec2 in_uv; \n\
layout (location = 3) in mat4 in_model; \n\
 \n\
out vec2 var_uv; \n\
out vec2 var_uv1; \n\
out vec2 var_uv2; \n\
out vec2 var_uv3; \n\
 \n\
uniform mat4 uVP; \n\
uniform float uTime; \n\
uniform vec3 uScrollSpeed = vec3(0.1, 0.5, 0.9); \n\
uniform vec3 uScale = vec3(1.0, 2.0, 3.0); \n\
 \n\
void main() \n\
{ \n\
    // toujours en multi > 1 feu = 3 plans \n\
    gl_Position = uVP * in_model * vec4(in_vertex, 1.0); \n\
 \n\
    var_uv = in_uv; \n\
 \n\
    var_uv1 = (in_uv * uScale.x); \n\
    var_uv1.y = var_uv1.y - (uTime * uScrollSpeed.x); \n\
 \n\
    var_uv2 = (in_uv * uScale.y); \n\
    var_uv2.y = var_uv2.y - (uTime * uScrollSpeed.y); \n\
 \n\
    var_uv3 = (in_uv * uScale.z); \n\
    var_uv3.y = var_uv3.y - (uTime * uScrollSpeed.z); \n\
}";



static const char* pFS = " \n\
#version 330 core \n\
 \n\
in vec2 var_uv; \n\
 \n\
in vec2 var_uv1; \n\
in vec2 var_uv2; \n\
in vec2 var_uv3; \n\
 \n\
out vec4 out_color; \n\
 \n\
uniform sampler2D uFireMap; \n\
uniform sampler2D uNoiseMap; \n\
uniform sampler2D uAlphaMap; \n\
uniform vec2 uDistortion1 = vec2(0.1, 0.2); \n\
uniform vec2 uDistortion2 = vec2(0.1, 0.3); \n\
uniform vec2 uDistortion3 = vec2(0.1, 0.1); \n\
uniform float uDistortionScale = 0.8; \n\
uniform float uDistortionBias = 0.5; \n\
 \n\
void main() \n\
{ \n\
    vec4 noise1; \n\
    vec4 noise2; \n\
    vec4 noise3; \n\
    vec4 finalNoise; \n\
    float perturb; \n\
    vec2 noiseCoords; \n\
    vec4 fireColor; \n\
    float alpha; \n\
 \n\
    noise1 = texture(uNoiseMap, var_uv1); \n\
    noise2 = texture(uNoiseMap, var_uv2); \n\
    noise3 = texture(uNoiseMap, var_uv3); \n\
 \n\
    noise1 = (noise1 - 0.5f) * 2.0f; \n\
    noise2 = (noise2 - 0.5f) * 2.0f; \n\
    noise3 = (noise3 - 0.5f) * 2.0f; \n\
 \n\
    noise1.xy = noise1.xy * uDistortion1.xy; \n\
    noise2.xy = noise2.xy * uDistortion2.xy; \n\
    noise3.xy = noise3.xy * uDistortion3.xy; \n\
 \n\
    finalNoise = noise1 + noise2 + noise3; \n\
 \n\
    perturb = ((var_uv.y) * uDistortionScale) + uDistortionBias; \n\
 \n\
    noiseCoords.x = (finalNoise.x * perturb) + var_uv.x; \n\
    noiseCoords.y = (finalNoise.y * perturb) + var_uv.y; \n\
    noiseCoords.y = clamp(noiseCoords.y, 0.0, 1.0); \n\
 \n\
    fireColor = texture(uFireMap, noiseCoords); \n\
 \n\
    alpha = texture(uAlphaMap, (var_uv + noiseCoords.xy) *0.5).r; \n\
    alpha = smoothstep(0.6, 0.95, alpha); \n\
    if (alpha < 0.05) discard; \n\
    out_color = fireColor; \n\
    out_color.a = alpha; \n\
}";




TShFire1::TShFire1()
{
	//
}

bool TShFire1::create()
{
    if (!OShader::init())
    {
        ECO_error_set("echec init > TShFire1");
        return false;
    }
    if (!addShader(GL_VERTEX_SHADER, pVS))
    {
        ECO_error_set("echec pVS > TShFire1");
        return false;
    }
    if (!addShader(GL_FRAGMENT_SHADER, pFS))
    {
        ECO_error_set("echec pFS > TShFire1");
        return false;
    }
    if (!finalize())
    {
        ECO_error_set("echec finalize > TShFire1");
        return false;
    }

    m_vp = getUniformLocation("uVP");
    m_time = getUniformLocation("uTime");
    m_fireMap = getUniformLocation("uFireMap");
    m_noiseMap = getUniformLocation("uNoiseMap");
    m_alphaMap = getUniformLocation("uAlphaMap");
/*
    if (m_vp == INVALID_UNIFORM_LOCATION
    || m_time == INVALID_UNIFORM_LOCATION
    || m_fireMap == INVALID_UNIFORM_LOCATION
    || m_noiseMap == INVALID_UNIFORM_LOCATION
    || m_alphaMap == INVALID_UNIFORM_LOCATION
    )
    {
        ECO_error_set("Echec TShFire1");
        return false;
    }
*/
    return true;
}



void TShFire1::setVP(const OMat4& vp)
{
    glUniformMatrix4fv(m_vp, 1, GL_TRUE, vp.getFv());
}

void TShFire1::setTextures(uint32_t fire, uint32_t noise, uint32_t alpha)
{
    glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, alpha);
    glUniform1i(m_alphaMap, 2);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, noise);
   	glUniform1i(m_noiseMap, 1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, fire);
   	glUniform1i(m_fireMap, 0);

    //glActiveTexture(GL_TEXTURE0);
}

// + 1/fps
void TShFire1::setTime(float value)
{
    glUniform1f(m_time, value);
}




