488 lines
17 KiB
C++
488 lines
17 KiB
C++
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2014-2015 SuperTuxKart-Team
|
|
//
|
|
// This program 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.
|
|
//
|
|
// This program 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 this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#ifndef SERVER_ONLY
|
|
|
|
#include "graphics/stk_mesh.hpp"
|
|
|
|
#include "graphics/callbacks.hpp"
|
|
#include "graphics/central_settings.hpp"
|
|
#include "graphics/irr_driver.hpp"
|
|
#include "graphics/material_manager.hpp"
|
|
#include "graphics/shaders.hpp"
|
|
#include "graphics/stk_tex_manager.hpp"
|
|
|
|
#include <ISceneManager.h>
|
|
#include <IMaterialRenderer.h>
|
|
|
|
|
|
// ============================================================================
|
|
Material::ShaderType getMeshMaterialFromType(video::E_MATERIAL_TYPE material_type,
|
|
video::E_VERTEX_TYPE tp,
|
|
Material* material,
|
|
Material* layer2_material)
|
|
{
|
|
if (layer2_material != NULL &&
|
|
layer2_material->getShaderType() == Material::SHADERTYPE_SPLATTING)
|
|
return Material::SHADERTYPE_SPLATTING;
|
|
|
|
if (tp == video::EVT_SKINNED_MESH)
|
|
{
|
|
switch (material->getShaderType())
|
|
{
|
|
case Material::SHADERTYPE_SOLID:
|
|
if (material_type == Shaders::getShader(ES_NORMAL_MAP))
|
|
return Material::SHADERTYPE_NORMAL_MAP_SKINNED_MESH;
|
|
else
|
|
return Material::SHADERTYPE_SOLID_SKINNED_MESH;
|
|
case Material::SHADERTYPE_ALPHA_TEST:
|
|
return Material::SHADERTYPE_ALPHA_TEST_SKINNED_MESH;
|
|
case Material::SHADERTYPE_SOLID_UNLIT:
|
|
return Material::SHADERTYPE_SOLID_UNLIT_SKINNED_MESH;
|
|
default:
|
|
return Material::SHADERTYPE_SOLID_SKINNED_MESH;
|
|
}
|
|
}
|
|
|
|
switch (material->getShaderType())
|
|
{
|
|
default:
|
|
return material->getShaderType();
|
|
case Material::SHADERTYPE_SOLID:
|
|
if (material_type == Shaders::getShader(ES_NORMAL_MAP))
|
|
return Material::SHADERTYPE_NORMAL_MAP;
|
|
else if (tp == video::EVT_2TCOORDS)
|
|
return Material::SHADERTYPE_DETAIL_MAP;
|
|
return Material::SHADERTYPE_SOLID;
|
|
}
|
|
} // getMeshMaterialFromType
|
|
|
|
// ----------------------------------------------------------------------------
|
|
TransparentMaterial getTransparentMaterialFromType(video::E_MATERIAL_TYPE type,
|
|
video::E_VERTEX_TYPE tp,
|
|
f32 MaterialTypeParam,
|
|
Material* material)
|
|
{
|
|
if (tp == video::EVT_SKINNED_MESH)
|
|
return TM_TRANSLUCENT_SKN;
|
|
|
|
if (type == Shaders::getShader(ES_DISPLACE))
|
|
{
|
|
if (CVS->isDefferedEnabled())
|
|
return TM_DISPLACEMENT;
|
|
else
|
|
return TM_TRANSLUCENT_2TC;
|
|
}
|
|
if (material->getShaderType() == Material::SHADERTYPE_ADDITIVE)
|
|
return TM_ADDITIVE;
|
|
return TM_DEFAULT;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
video::E_VERTEX_TYPE getVTXTYPEFromStride(size_t stride)
|
|
{
|
|
if (stride == sizeof(video::S3DVertex))
|
|
return video::EVT_STANDARD;
|
|
else if (stride == sizeof(video::S3DVertex2TCoords))
|
|
return video::EVT_2TCOORDS;
|
|
assert(stride == sizeof(video::S3DVertexTangents));
|
|
return video::EVT_TANGENTS;
|
|
} // getVTXTYPEFromStride
|
|
|
|
// ----------------------------------------------------------------------------
|
|
GLuint createVAO(GLuint vbo, GLuint idx, video::E_VERTEX_TYPE type)
|
|
{
|
|
GLuint vao;
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
assert(vbo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
switch (type)
|
|
{
|
|
case video::EVT_STANDARD:
|
|
// Position
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), 0);
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)12);
|
|
// Color
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
|
getVertexPitchFromType(type), (GLvoid*)24);
|
|
// Texcoord
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)28);
|
|
break;
|
|
case video::EVT_2TCOORDS:
|
|
// Position
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), 0);
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)12);
|
|
// Color
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
|
getVertexPitchFromType(type), (GLvoid*)24);
|
|
// Texcoord
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)28);
|
|
// SecondTexcoord
|
|
glEnableVertexAttribArray(4);
|
|
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)36);
|
|
break;
|
|
case video::EVT_TANGENTS:
|
|
// Position
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), 0);
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)12);
|
|
// Color
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
|
getVertexPitchFromType(type), (GLvoid*)24);
|
|
// Texcoord
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)28);
|
|
// Tangent
|
|
glEnableVertexAttribArray(5);
|
|
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)36);
|
|
// Bitangent
|
|
glEnableVertexAttribArray(6);
|
|
glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE,
|
|
getVertexPitchFromType(type), (GLvoid*)48);
|
|
break;
|
|
case video::EVT_SKINNED_MESH:
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, getVertexPitchFromType(type), 0);
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, getVertexPitchFromType(type), (GLvoid*)12);
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, getVertexPitchFromType(type), (GLvoid*)24);
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, getVertexPitchFromType(type), (GLvoid*)28);
|
|
glEnableVertexAttribArray(4);
|
|
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, getVertexPitchFromType(type), (GLvoid*)44);
|
|
glEnableVertexAttribArray(5);
|
|
glVertexAttribIPointer(5, 4, GL_INT, getVertexPitchFromType(type), (GLvoid*)60);
|
|
glEnableVertexAttribArray(6);
|
|
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, getVertexPitchFromType(type), (GLvoid*)76);
|
|
break;
|
|
default:
|
|
assert(0 && "Wrong vertex type");
|
|
}
|
|
assert(idx);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idx);
|
|
return vao;
|
|
} // createVAO
|
|
|
|
// ----------------------------------------------------------------------------
|
|
GLMesh allocateMeshBuffer(scene::IMeshBuffer* mb, const std::string& debug_name,
|
|
RenderInfo* render_info)
|
|
{
|
|
GLMesh result = {};
|
|
result.m_material = NULL;
|
|
result.m_render_info = NULL;
|
|
if (!mb)
|
|
return result;
|
|
result.mb = mb;
|
|
if (render_info != NULL)
|
|
{
|
|
result.m_render_info = render_info;
|
|
result.m_material = material_manager->getMaterialFor(mb
|
|
->getMaterial().getTexture(0), mb);
|
|
}
|
|
#ifdef DEBUG
|
|
result.debug_name = debug_name;
|
|
#endif
|
|
|
|
result.IndexCount = mb->getIndexCount();
|
|
switch (mb->getIndexType())
|
|
{
|
|
case irr::video::EIT_16BIT:
|
|
{
|
|
result.IndexType = GL_UNSIGNED_SHORT;
|
|
break;
|
|
}
|
|
case irr::video::EIT_32BIT:
|
|
{
|
|
result.IndexType = GL_UNSIGNED_INT;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
assert(0 && "Wrong index size");
|
|
}
|
|
}
|
|
result.VAOType = mb->getVertexType();
|
|
result.Stride = getVertexPitchFromType(result.VAOType);
|
|
result.IndexCount = mb->getIndexCount();
|
|
switch (mb->getPrimitiveType())
|
|
{
|
|
case scene::EPT_POINTS:
|
|
result.PrimitiveType = GL_POINTS;
|
|
break;
|
|
case scene::EPT_TRIANGLE_STRIP:
|
|
result.PrimitiveType = GL_TRIANGLE_STRIP;
|
|
break;
|
|
case scene::EPT_TRIANGLE_FAN:
|
|
result.PrimitiveType = GL_TRIANGLE_FAN;
|
|
break;
|
|
case scene::EPT_LINES:
|
|
result.PrimitiveType = GL_LINES;
|
|
break;
|
|
case scene::EPT_TRIANGLES:
|
|
result.PrimitiveType = GL_TRIANGLES;
|
|
break;
|
|
case scene::EPT_POINT_SPRITES:
|
|
case scene::EPT_LINE_LOOP:
|
|
case scene::EPT_POLYGON:
|
|
case scene::EPT_LINE_STRIP:
|
|
case scene::EPT_QUAD_STRIP:
|
|
case scene::EPT_QUADS:
|
|
assert(0 && "Unsupported primitive type");
|
|
}
|
|
for (unsigned i = 0; i < 8; i++)
|
|
result.textures[i] = mb->getMaterial().getTexture(i);
|
|
result.texture_trans = core::vector2df(0.0f, 0.0f);
|
|
|
|
return result;
|
|
} // allocateMeshBuffer
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static size_t getUnsignedSize(unsigned tp)
|
|
{
|
|
switch (tp)
|
|
{
|
|
case GL_UNSIGNED_SHORT:
|
|
return sizeof(u16);
|
|
case GL_UNSIGNED_INT:
|
|
return sizeof(u32);
|
|
default:
|
|
assert(0 && "Unsupported index type");
|
|
return 0;
|
|
}
|
|
} // getUnsignedSize
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void fillLocalBuffer(GLMesh &mesh, scene::IMeshBuffer* mb)
|
|
{
|
|
glBindVertexArray(0);
|
|
glGenBuffers(1, &(mesh.vertex_buffer));
|
|
glGenBuffers(1, &(mesh.index_buffer));
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, mesh.vertex_buffer);
|
|
|
|
const void* vertices = mb->getVertices();
|
|
const u32 vertexCount = mb->getVertexCount();
|
|
// This can happen for certain debug structures, e.g. ShowCurve
|
|
if (vertexCount == 0)
|
|
return;
|
|
|
|
const c8* vbuf = static_cast<const c8*>(vertices);
|
|
glBufferData(GL_ARRAY_BUFFER, vertexCount * mesh.Stride, vbuf,
|
|
GL_STREAM_DRAW);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.index_buffer);
|
|
const void* indices = mb->getIndices();
|
|
mesh.IndexCount = mb->getIndexCount();
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
|
mesh.IndexCount * getUnsignedSize(mesh.IndexType),
|
|
indices, GL_STREAM_DRAW);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
} // fillLocalBuffer
|
|
|
|
// ----------------------------------------------------------------------------
|
|
core::matrix4 computeMVP(const core::matrix4 &model_matrix)
|
|
{
|
|
core::matrix4 ModelViewProjectionMatrix = irr_driver->getProjMatrix();
|
|
ModelViewProjectionMatrix *= irr_driver->getViewMatrix();
|
|
ModelViewProjectionMatrix *= model_matrix;
|
|
return ModelViewProjectionMatrix;
|
|
} // computeMVP
|
|
|
|
// ----------------------------------------------------------------------------
|
|
core::vector3df getWindDir()
|
|
{
|
|
const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f;
|
|
GrassShaderProvider *gsp =
|
|
(GrassShaderProvider *)Shaders::getCallback(ES_GRASS);
|
|
return (gsp->getSpeed() * time * vector3df(1., 0., 0.));
|
|
} // getWindDir
|
|
|
|
// ----------------------------------------------------------------------------
|
|
bool isObject(video::E_MATERIAL_TYPE type)
|
|
{
|
|
if (type == Shaders::getShader(ES_OBJECTPASS))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_OBJECTPASS_REF))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_OBJECTPASS_RIMLIT))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_NORMAL_MAP))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_SPHERE_MAP))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_SPLATTING))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_GRASS))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_GRASS_REF))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_DISPLACE))
|
|
return true;
|
|
if (type == Shaders::getShader(ES_OBJECT_UNLIT))
|
|
return true;
|
|
if (type == video::EMT_TRANSPARENT_ALPHA_CHANNEL)
|
|
return true;
|
|
if (type == video::EMT_ONETEXTURE_BLEND)
|
|
return true;
|
|
if (type == video::EMT_TRANSPARENT_ADD_COLOR)
|
|
return true;
|
|
if (type == video::EMT_SOLID)
|
|
return true;
|
|
if (type == video::EMT_LIGHTMAP_LIGHTING)
|
|
return true;
|
|
if (type == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
|
|
return true;
|
|
return false;
|
|
} // isObject
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void setTexture(GLMesh &mesh, unsigned i, bool is_srgb,
|
|
const std::string &mat_name)
|
|
{
|
|
if (!mesh.textures[i])
|
|
{
|
|
Log::error("STKMesh", "Missing texture %d for material %s", i,
|
|
mat_name.c_str());
|
|
// use unicolor texture to replace missing texture
|
|
mesh.textures[i] =
|
|
STKTexManager::getInstance()->getUnicolorTexture(video::SColor(255, 127, 127, 127));
|
|
}
|
|
#if !defined(USE_GLES2)
|
|
if (CVS->isAZDOEnabled())
|
|
{
|
|
if (!mesh.TextureHandles[i])
|
|
{
|
|
mesh.TextureHandles[i] = mesh.textures[i]->getHandle();
|
|
}
|
|
}
|
|
#endif
|
|
} // setTexture
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static std::string getShaderTypeName(Material::ShaderType mat)
|
|
{
|
|
switch (mat)
|
|
{
|
|
default:
|
|
case Material::SHADERTYPE_SOLID:
|
|
return "Solid";
|
|
case Material::SHADERTYPE_ALPHA_TEST:
|
|
return "Alpha Test";
|
|
case Material::SHADERTYPE_VEGETATION:
|
|
return "Grass";
|
|
case Material::SHADERTYPE_SPHERE_MAP:
|
|
return "Sphere Map";
|
|
case Material::SHADERTYPE_SOLID_UNLIT:
|
|
return "Unlit";
|
|
case Material::SHADERTYPE_DETAIL_MAP:
|
|
return "Detail";
|
|
case Material::SHADERTYPE_NORMAL_MAP:
|
|
return "Normal";
|
|
case Material::SHADERTYPE_SPLATTING:
|
|
return "Splatting";
|
|
}
|
|
} // getShaderTypeName
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void initTextures(GLMesh &mesh, Material::ShaderType mat)
|
|
{
|
|
switch (mat)
|
|
{
|
|
default:
|
|
case Material::SHADERTYPE_SOLID:
|
|
case Material::SHADERTYPE_ALPHA_TEST:
|
|
case Material::SHADERTYPE_SPHERE_MAP:
|
|
case Material::SHADERTYPE_SOLID_UNLIT:
|
|
case Material::SHADERTYPE_VEGETATION:
|
|
case Material::SHADERTYPE_SOLID_SKINNED_MESH:
|
|
case Material::SHADERTYPE_ALPHA_TEST_SKINNED_MESH:
|
|
case Material::SHADERTYPE_SOLID_UNLIT_SKINNED_MESH:
|
|
setTexture(mesh, 0, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 1, false, getShaderTypeName(mat));
|
|
setTexture(mesh, 2, false, getShaderTypeName(mat));
|
|
break;
|
|
case Material::SHADERTYPE_DETAIL_MAP:
|
|
case Material::SHADERTYPE_NORMAL_MAP:
|
|
case Material::SHADERTYPE_NORMAL_MAP_SKINNED_MESH:
|
|
setTexture(mesh, 0, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 1, false, getShaderTypeName(mat));
|
|
setTexture(mesh, 2, false, getShaderTypeName(mat));
|
|
setTexture(mesh, 3, false, getShaderTypeName(mat));
|
|
break;
|
|
case Material::SHADERTYPE_SPLATTING:
|
|
setTexture(mesh, 0, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 1, false, getShaderTypeName(mat));
|
|
setTexture(mesh, 2, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 3, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 4, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 5, true, getShaderTypeName(mat));
|
|
setTexture(mesh, 6, false, getShaderTypeName(mat));
|
|
setTexture(mesh, 7, false, getShaderTypeName(mat));
|
|
break;
|
|
}
|
|
} // initTextures
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void initTexturesTransparent(GLMesh &mesh)
|
|
{
|
|
if (!mesh.textures[0])
|
|
{
|
|
mesh.textures[0] = STKTexManager::getInstance()->getUnicolorTexture(video::SColor(255, 255, 255, 255));
|
|
}
|
|
#if !defined(USE_GLES2)
|
|
if (CVS->isAZDOEnabled())
|
|
{
|
|
if (!mesh.TextureHandles[0])
|
|
{
|
|
mesh.TextureHandles[0] = mesh.textures[0]->getHandle();
|
|
}
|
|
}
|
|
#endif
|
|
} // initTexturesTransparent
|
|
|
|
#endif // !SERVER_ONLY
|
|
|