
#include "Ttree.h"

// shader en fin

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

Ttree::Ttree()
{
	//
}

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

Ttree::~Ttree()
{
    //
}

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

// px py pz rx ry rz scale
void Ttree::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_rot.push_back(OVec3_f());
    m_scale.push_back(1);
    m_model.push_back(OMat4());
    m_model_multi.push_back(OMat4());

	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_rot[idx].x = ECO_strToFloat(strList[3]);
	m_rot[idx].y = ECO_strToFloat(strList[4]);
	m_rot[idx].z = ECO_strToFloat(strList[5]);
    m_scale[idx] = ECO_strToFloat(strList[6]);


    _setModel(idx);
}

// code path file 1 / pos.xyz rot.xyz scale /
void Ttree::create(std::string flux)
{
    std::vector<std::string> strList;
    size_t idx = 0;

    ECO_strCut(strList, flux, ' ');

    // ---

    m_pos.push_back(OVec3_f());
    m_rot.push_back(OVec3_f());
    m_scale.push_back(1);
    m_model.push_back(OMat4());
    m_model_multi.push_back(OMat4());

    // ---

	m_code = strList[0];
	m_path = strList[1];
	m_file = strList[2];
	m_cull_face = ECO_strToInt(strList[3]);
	// 4
	m_pos[idx].x = ECO_strToFloat(strList[5]);
	m_pos[idx].y = ECO_strToFloat(strList[6]);
	m_pos[idx].z = ECO_strToFloat(strList[7]);
	m_rot[idx].x = ECO_strToFloat(strList[8]);
	m_rot[idx].y = ECO_strToFloat(strList[9]);
	m_rot[idx].z = ECO_strToFloat(strList[10]);
    m_scale[idx] = ECO_strToFloat(strList[11]);

    // ---

    _composition();
    _setModel(idx);
}

// code path file 1 / pos.xyz rot.xyz scale /
void Ttree::create(std::fstream& flux)
{
    std::string sep;
    size_t idx = 0;

    m_pos.push_back(OVec3_f());
    m_rot.push_back(OVec3_f());
    m_scale.push_back(1);
    m_model.push_back(OMat4());
    m_model_multi.push_back(OMat4());

    flux >> m_code >> m_path >> m_file >> m_cull_face >> sep;
    flux >> m_pos[idx].x >> m_pos[idx].y >> m_pos[idx].z >> m_rot[idx].x >> m_rot[idx].y >> m_rot[idx].z >> m_scale[idx] >> sep;

    _composition();
    _setModel(idx);
}

void Ttree::_composition()
{
    dshTree.create();

    // ---

    OMesh_obj::P_setPath(PATH_MESH + m_path);

    if (!mesh.create(m_file))
    {
        ECO_error_set("echec chargement %s >Ttree::_composition", m_file.c_str());
        return;
    }

    // ---

    m_time = 0.0f;
}





void Ttree::draw(OMat4& vp)
{
    m_time += 0.05f; // 1/50 fps;

    dshTree.enable();
        dshTree.setVP(vp);
        dshTree.setTime(m_time);

    if (m_cull_face)
        glEnable(GL_CULL_FACE);
    else
        glDisable(GL_CULL_FACE);

    for (size_t part = 0; part < mesh.getPart_size(); part++)
    {
        dshTree.setTextures( mesh.getId_diffuseMap(part), mesh.getId_normalMap(part), mesh.getId_specularMap(part));
        mesh.draw_multi(part);
    }
    mesh.end_draw_part();
}




// ou faire un std::vector<Ovec3_f> m_bound_min
OVec3_f Ttree::getBound_min(size_t instance)
{
    OVec3_f bound_min;

    // le bound contient la ramure (foliage) !
    bound_min.set(-0.5f, -0.5f, 0.0f); // on ne prend que le tronc
    bound_min *= m_scale[instance];

    return bound_min;
}

OVec3_f Ttree::getBound_max(size_t instance)
{
    OVec3_f bound_max;

    bound_max.set(0.5f, 0.5f, 3.0f);    // on ne prend que le tronc
    //bound_max = mesh.getBound_max();   // le bound est tres grand > le petit palmier recouvre le grand
                                        // c'est dans ce cas qu'il faut utiliser un picking color pour pouvoir cliquer précisement
    bound_max *= m_scale[instance];

    //ECO_error_set("bmax %f %f %f", bound_max.x, bound_max.y, bound_max.z);

    return bound_max;
}

size_t Ttree::getInstance_nbr()
{
    return m_model.size();
}

OMat4& Ttree::getModel(size_t idx)
{
    return m_model[idx];
}
/*
size_t Ttree::getPart_size()
{
    return mesh.getPart_size();
}
*/




void Ttree::save(std::ofstream& flux)
{
    if (m_pos.size() == 0) return; // aucune instance

    flux << FLUX_TREE1 << " ";

    flux << m_code << " " << m_path << " " << m_file << " " << m_cull_face << " / ";
    flux << m_pos[0].x << " " << m_pos[0].y << " " << m_pos[0].z << " ";
    flux << m_rot[0].x << " " << m_rot[0].y << " " << m_rot[0].z << " ";
    flux << m_scale[0] << " /" << std::endl;
    // instances
    for (size_t idx = 1; idx < m_pos.size(); idx++)
    {
        flux << FLUX_TREE1_M << " ";
        flux << m_pos[idx].x << " " << m_pos[idx].y << " " << m_pos[idx].z << " ";
        flux << m_rot[idx].x << " " << m_rot[idx].y << " " << m_rot[idx].z << " ";
        flux << m_scale[idx] << " /" << std::endl;
    }
}

void Ttree::_setModel(size_t idx)
{
    m_model[idx].loadIdentity();
    m_model[idx].translate(m_pos[idx]);
    m_model[idx].rotate(m_rot[idx].z, 0,0,1); // ma verticale
    m_model[idx].rotate(m_rot[idx].y, 0,1,0);
    m_model[idx].rotate(m_rot[idx].x, 1,0,0);
    m_model[idx].scale(m_scale[idx]);

    m_model_multi[idx] = m_model[idx];
    m_model_multi[idx].transpose();

    mesh.setModelMulti(m_model_multi);
}

// --- shader ---


static const char* pVS = " \n\
#version 330 \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\
// in_model = 3 4 5 6 \n\
 \n\
uniform mat4 uVP; \n\
uniform float uTime; \n\
uniform float uWindSpeed = 20.0; \n\
uniform float uFlexibility = 0.005; \n\
 \n\
out vec2 var_uv; \n\
out vec3 var_normal; \n\
out vec3 var_model; \n\
//flat out int var_instanceID; \n\
 \n\
float random (vec2 st) \n\
{\n\
    return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);\n\
    //return mod((st.x*st.x)+(0.13-st.x*st.y), 0.01)/0.01; \n\
}\n\
 \n\
void main() \n\
{ \n\
    vec3 pos = in_vertex; \n\
    var_uv = in_uv; \n\
    var_model = (in_model * vec4(pos, 1.0)).xyz; \n\
    var_normal = (in_model * vec4(in_normal, 0.0)).xyz; \n\
    var_normal = normalize(var_normal); \n\
\n\
    // tous les arbres ne bougent pas de la même façon \n\
    float windflux = random(var_model.xy) * uWindSpeed*0.1; \n\
    // Calcul de la hauteur du sommet > seul le sommet bouge\n\
    float b = uWindSpeed * uFlexibility * pos.z * 0.2; \n\
    pos.x += cos((uTime+var_model.x) *0.5) * b; \n\
    pos.y += sin((uTime+var_model.y) *0.25) * b; \n\
 \n\
    gl_Position = uVP * in_model * vec4(pos, 1.0); \n\
}";


static const char* pFS = " \n\
#version 330 core \n\
 \n\
in vec2 var_uv; \n\
in vec3 var_normal; \n\
in vec3 var_model; \n\
//flat in int var_instanceID; \n\
 \n\
layout (location = 0) out vec3 positionMap; \n\
layout (location = 1) out vec4 colorMap; \n\
layout (location = 2) out vec3 normalMap; \n\
 \n\
uniform sampler2D uColorMap; \n\
uniform sampler2D uNormalMap; \n\
uniform sampler2D uSpecularMap; \n\
 \n\
vec3 calcBumpNormal() \n\
{ \n\
    vec3 Tangent0;  \n\
    vec3 c1 = cross(var_normal, vec3(0.0, 0.0, 1.0));  \n\
    vec3 c2 = cross(var_normal, vec3(0.0, 1.0, 0.0));  \n\
    if (length(c1) > length(c2))  \n\
        Tangent0 = c1; \n\
    else  \n\
        Tangent0 = c2; \n\
    \n\
    // normale bump \n\
    vec3 Normal = normalize(var_normal); \n\
    vec3 Tangent = normalize(Tangent0); \n\
    Tangent = normalize(Tangent - dot(Tangent, Normal) * Normal); \n\
    vec3 Bitangent = cross(Tangent, Normal); \n\
    vec3 BumpMapNormal = texture(uNormalMap, var_uv).xyz; \n\
    BumpMapNormal = (2.0 * BumpMapNormal) - vec3(1.0,1.0,1.0); \n\
    vec3 NewNormal; \n\
    mat3 TBN = mat3(Tangent, Bitangent, Normal); \n\
    NewNormal = TBN * BumpMapNormal; \n\
    return normalize(NewNormal); \n\
} \n\
 \n\
void main(){ \n\
    vec4 color = texture(uColorMap, var_uv); \n\
	float alpha = smoothstep(0.6, 0.95, color.a); \n\
    if (alpha < 0.05) discard; \n\
    \n\
    positionMap = var_model; \n\
    color.a = texture(uSpecularMap, var_uv).a; \n\
    colorMap = color; \n\
    normalMap = calcBumpNormal(); \n\
}";




Ttree_shader::Ttree_shader()
{
	//
}

bool Ttree_shader::create()
{
    if (!ODshader::init())
    {
        ECO_error_set("echec init Ttree_shader");
        return false;
    }
    if (!addShader(GL_VERTEX_SHADER, pVS))
    {
        ECO_error_set("echec add vertex Ttree_shader");
        return false;
    }
    if (!addShader(GL_FRAGMENT_SHADER, pFS))
    {
        ECO_error_set("echec add fragment Ttree_shader");
        return false;
    }
    if (!finalize())
    {
        ECO_error_set("echec finalize Ttree_shader");
        return false;
    }

    m_vp = getUniformLocation("uVP");
	m_colorMap = getUniformLocation("uColorMap");
    m_normalMap = getUniformLocation("uNormalMap");
    m_specularMap = getUniformLocation("uSpecularMap");
    m_time = getUniformLocation("uTime");

/*
	if (m_vp == INVALID_UNIFORM_LOCATION
    || m_colorMap == INVALID_UNIFORM_LOCATION
    || m_normalMap == INVALID_UNIFORM_LOCATION
    || m_specularMap == INVALID_UNIFORM_LOCATION
    )
    {
        ECO_error_set("Echec Ttree_shader");
        return false;
    }
*/
    return true;
}

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





void Ttree_shader::setTextures(uint32_t texture, uint32_t normal, uint32_t specular)
{
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, specular);
    glUniform1i(m_specularMap, 2);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, normal);
    glUniform1i(m_normalMap, 1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(m_colorMap, 0);

    //glActiveTexture(GL_TEXTURE0);
}

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



