mirror of
https://github.com/HowestDAE/dae16-VerhulstBram.git
synced 2025-12-16 03:51:47 +01:00
361 lines
10 KiB
C++
361 lines
10 KiB
C++
#include "base.h"
|
|
#include <iostream>
|
|
#include <string>
|
|
#include "Texture.h"
|
|
|
|
int Texture::m_TextureCounter{ 0 };
|
|
|
|
Texture::Texture( const std::string& imagePath )
|
|
:m_Id{ }
|
|
,m_Width{ 10.0f }
|
|
,m_Height{ 10.0f }
|
|
,m_CreationOk{ false }
|
|
{
|
|
m_TextureCounter++;
|
|
CreateFromImage( imagePath );
|
|
}
|
|
|
|
Texture::Texture( const std::string& text, TTF_Font *pFont, const Color4f& textColor )
|
|
:m_Id{}
|
|
,m_Width{ 10.0f }
|
|
,m_Height{ 10.0f }
|
|
,m_CreationOk{ false }
|
|
{
|
|
CreateFromString( text, pFont, textColor );
|
|
}
|
|
|
|
Texture::Texture( const std::string& text, const std::string& fontPath, int ptSize, const Color4f& textColor )
|
|
:m_Id{}
|
|
,m_Width{ 10.0f }
|
|
,m_Height{ 10.0f }
|
|
,m_CreationOk{ false }
|
|
{
|
|
CreateFromString( text, fontPath, ptSize, textColor );
|
|
}
|
|
Texture::Texture( Texture&& other ) noexcept
|
|
:m_Id{ other.m_Id }
|
|
,m_Width{ other.m_Width }
|
|
,m_Height{ other.m_Height }
|
|
,m_CreationOk{ other.m_CreationOk }
|
|
{
|
|
other.m_Id = 0;
|
|
other.m_CreationOk = false;
|
|
}
|
|
|
|
Texture& Texture::operator=( Texture&& other ) noexcept
|
|
{
|
|
if (this != &other)// no self assignment
|
|
{
|
|
|
|
m_Id = other.m_Id;
|
|
m_Width = other.m_Width;
|
|
m_Height = other.m_Height;
|
|
m_CreationOk = other.m_CreationOk;
|
|
other.m_Id = 0;
|
|
other.m_CreationOk = false;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Texture::~Texture()
|
|
{
|
|
glDeleteTextures( 1, &m_Id );
|
|
}
|
|
|
|
void Texture::CreateFromImage( const std::string& path )
|
|
{
|
|
m_CreationOk = true;
|
|
|
|
// Load image at specified path
|
|
SDL_Surface* pLoadedSurface = IMG_Load(path.c_str());
|
|
if (pLoadedSurface == nullptr)
|
|
{
|
|
std::cerr << "Texture::CreateFromImage, error when calling IMG_Load: " << SDL_GetError() << std::endl;
|
|
m_CreationOk = false;
|
|
return;
|
|
}
|
|
CreateFromSurface(pLoadedSurface);
|
|
|
|
// Free loaded surface
|
|
SDL_FreeSurface(pLoadedSurface);
|
|
|
|
}
|
|
|
|
void Texture::CreateFromString( const std::string& text, const std::string& fontPath, int ptSize, const Color4f& textColor )
|
|
{
|
|
m_CreationOk = true;
|
|
|
|
// Create font
|
|
TTF_Font *pFont{};
|
|
pFont = TTF_OpenFont( fontPath.c_str( ), ptSize );
|
|
if(pFont == nullptr )
|
|
{
|
|
std::cerr << "Texture::CreateFromString, error when calling TTF_OpenFont: " << TTF_GetError( ) << std::endl;
|
|
m_CreationOk = false;
|
|
return;
|
|
}
|
|
|
|
// Create texture using this font and close font afterwards
|
|
CreateFromString( text, pFont, textColor );
|
|
TTF_CloseFont( pFont );
|
|
}
|
|
|
|
void Texture::CreateFromString( const std::string& text, TTF_Font *pFont, const Color4f& color )
|
|
{
|
|
m_CreationOk = true;
|
|
if ( pFont == nullptr )
|
|
{
|
|
std::cerr << "Texture::CreateFromString, invalid TTF_Font pointer\n" ;
|
|
m_CreationOk = false;
|
|
return;
|
|
}
|
|
|
|
// Render text surface
|
|
SDL_Color textColor{};
|
|
textColor.r = Uint8( color.r * 255 );
|
|
textColor.g = Uint8( color.g * 255 );
|
|
textColor.b = Uint8( color.b * 255 );
|
|
textColor.a = Uint8( color.a * 255 );
|
|
|
|
SDL_Surface* pLoadedSurface = TTF_RenderText_Blended( pFont, text.c_str( ), textColor );
|
|
if ( pLoadedSurface == nullptr )
|
|
{
|
|
std::cerr << "Texture::CreateFromString, error when calling TTF_RenderText_Blended: " << TTF_GetError( ) << std::endl;
|
|
m_CreationOk = false;
|
|
return;
|
|
}
|
|
|
|
// Copy to video memory
|
|
CreateFromSurface( pLoadedSurface );
|
|
|
|
// Free loaded surface
|
|
SDL_FreeSurface( pLoadedSurface );
|
|
}
|
|
|
|
void Texture::CreateFromSurface( SDL_Surface* pSurface )
|
|
{
|
|
m_CreationOk = true;
|
|
|
|
//Get image dimensions
|
|
m_Width = float(pSurface->w);
|
|
m_Height =float( pSurface->h);
|
|
|
|
// Get pixel format information and translate to OpenGl format
|
|
GLenum pixelFormat{ GL_RGB };
|
|
switch ( pSurface->format->BytesPerPixel )
|
|
{
|
|
case 3:
|
|
if ( pSurface->format->Rmask == 0x000000ff )
|
|
{
|
|
pixelFormat = GL_RGB;
|
|
}
|
|
else
|
|
{
|
|
pixelFormat = GL_BGR;
|
|
}
|
|
break;
|
|
case 4:
|
|
if ( pSurface->format->Rmask == 0x000000ff )
|
|
{
|
|
pixelFormat = GL_RGBA;
|
|
}
|
|
else
|
|
{
|
|
pixelFormat = GL_BGRA;
|
|
}
|
|
break;
|
|
default:
|
|
std::cerr << "Texture::CreateFromSurface, unknow pixel format, BytesPerPixel: " << pSurface->format->BytesPerPixel << "\nUse 32 bit or 24 bit images.\n";
|
|
m_CreationOk = false;
|
|
return;
|
|
}
|
|
|
|
//Generate an array of textures. We only want one texture (one element array), so trick
|
|
//it by treating "texture" as array of length one.
|
|
glGenTextures(1, &m_Id);
|
|
|
|
//Select (bind) the texture we just generated as the current 2D texture OpenGL is using/modifying.
|
|
//All subsequent changes to OpenGL's texturing state for 2D textures will affect this texture.
|
|
glBindTexture(GL_TEXTURE_2D, m_Id);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, pSurface->pitch / pSurface->format->BytesPerPixel);
|
|
// check for errors. Can happen if a texture is created while a static pointer is being initialized, even before the call to the main function.
|
|
GLenum e = glGetError();
|
|
if (e != GL_NO_ERROR)
|
|
{
|
|
std::cerr << "Texture::CreateFromSurface, error binding textures, Error id = " << e << '\n';
|
|
std::cerr << "Can happen if a texture is created before performing the initialization code (e.g. a static Texture object).\n";
|
|
std::cerr << "There might be a white rectangle instead of the image.\n";
|
|
}
|
|
|
|
// Specify the texture's data.
|
|
// This function is a bit tricky, and it's hard to find helpful documentation.
|
|
// A summary:
|
|
// GL_TEXTURE_2D: The currently bound 2D texture (i.e. the one we just made)
|
|
// 0: The mipmap level. 0, since we want to update the base level mipmap image (i.e., the image itself,
|
|
// not cached smaller copies)
|
|
// GL_RGBA: Specifies the number of color components in the texture.
|
|
// This is how OpenGL will store the texture internally (kinda)--
|
|
// It's essentially the texture's type.
|
|
// surface->w: The width of the texture
|
|
// surface->h: The height of the texture
|
|
// 0: The border. Don't worry about this if you're just starting.
|
|
// pixelFormat: The format that the *data* is in--NOT the texture!
|
|
// GL_UNSIGNED_BYTE: The type the data is in. In SDL, the data is stored as an array of bytes, with each channel
|
|
// getting one byte. This is fairly typical--it means that the image can store, for each channel,
|
|
// any value that fits in one byte (so 0 through 255). These values are to be interpreted as
|
|
// *unsigned* values (since 0x00 should be dark and 0xFF should be bright).
|
|
// surface->pixels: The actual data. As above, SDL's array of bytes.
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, pSurface->w, pSurface->h, 0, pixelFormat, GL_UNSIGNED_BYTE, pSurface->pixels );
|
|
|
|
// Set the minification and magnification filters. In this case, when the texture is minified (i.e., the texture's pixels (texels) are
|
|
// *smaller* than the screen pixels you're seeing them on, linearly filter them (i.e. blend them together). This blends four texels for
|
|
// each sample--which is not very much. Mipmapping can give better results. Find a texturing tutorial that discusses these issues
|
|
// further. Conversely, when the texture is magnified (i.e., the texture's texels are *larger* than the screen pixels you're seeing
|
|
// them on), linearly filter them. Qualitatively, this causes "blown up" (overmagnified) textures to look blurry instead of blocky.
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
}
|
|
|
|
void Texture::Draw( const Vector2f& dstBottomLeft, const Rectf& srcRect, bool flip) const
|
|
{
|
|
const float epsilon{ 0.001f };
|
|
if ( !m_CreationOk )
|
|
{
|
|
if (!(srcRect.width > epsilon && srcRect.height > epsilon)) // No srcRect specified
|
|
{
|
|
DrawFilledRect(Rectf{ dstBottomLeft.x, dstBottomLeft.y, m_Width, m_Height });
|
|
}
|
|
else
|
|
{
|
|
DrawFilledRect(Rectf{ dstBottomLeft.x, dstBottomLeft.y, srcRect.width, srcRect.height });
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Rectf dstRect{ dstBottomLeft.x, dstBottomLeft.y, srcRect.width, srcRect.height };
|
|
if (!(srcRect.width > epsilon && srcRect.height > epsilon)) // No srcRect specified
|
|
{
|
|
dstRect.width = m_Width;
|
|
dstRect.height = m_Height;
|
|
}
|
|
Draw( dstRect, srcRect, flip );
|
|
}
|
|
}
|
|
|
|
void Texture::Draw( const Rectf& dstRect, const Rectf& srcRect, bool flip ) const
|
|
{
|
|
const float epsilon{ 0.001f };
|
|
if ( !m_CreationOk )
|
|
{
|
|
DrawFilledRect( dstRect );
|
|
return;
|
|
}
|
|
|
|
// Determine texture coordinates using srcRect and default destination width and height
|
|
float textLeft{};
|
|
float textRight{};
|
|
float textTop{};
|
|
float textBottom{};
|
|
|
|
float defaultDestWidth{};
|
|
float defaultDestHeight{};
|
|
if ( !( srcRect.width > epsilon && srcRect.height > epsilon) ) // No srcRect specified
|
|
{
|
|
// Use complete texture
|
|
textLeft = 0.0f;
|
|
textRight = 1.0f;
|
|
textTop = 0.0f;
|
|
textBottom = 1.0f;
|
|
|
|
defaultDestHeight = m_Height;
|
|
defaultDestWidth = m_Width;
|
|
}
|
|
else // srcRect specified
|
|
{
|
|
// Convert to the range [0.0, 1.0]
|
|
textLeft = srcRect.left / m_Width;
|
|
textRight = ( srcRect.left + srcRect.width ) / m_Width;
|
|
textTop = ( srcRect.bottom - srcRect.height ) / m_Height;
|
|
textBottom = srcRect.bottom / m_Height;
|
|
|
|
defaultDestHeight = srcRect.height;
|
|
defaultDestWidth = srcRect.width;
|
|
}
|
|
|
|
// Determine vertex coordinates
|
|
float vertexLeft{ dstRect.left };
|
|
float vertexBottom{ dstRect.bottom };
|
|
float vertexRight{};
|
|
float vertexTop{};
|
|
if ( !( dstRect.width > 0.001f && dstRect.height > 0.001f ) ) // If no size specified use default size
|
|
{
|
|
vertexRight = vertexLeft + defaultDestWidth;
|
|
vertexTop = vertexBottom + defaultDestHeight;
|
|
}
|
|
else
|
|
{
|
|
vertexRight = vertexLeft + dstRect.width;
|
|
vertexTop = vertexBottom + dstRect.height;
|
|
}
|
|
if(flip) {
|
|
std::swap(vertexLeft, vertexRight);
|
|
}
|
|
|
|
// Tell opengl which texture we will use
|
|
glBindTexture( GL_TEXTURE_2D, m_Id );
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
|
|
|
|
// Draw
|
|
glEnable( GL_TEXTURE_2D );
|
|
{
|
|
glBegin( GL_QUADS );
|
|
{
|
|
glTexCoord2f( textLeft, textBottom );
|
|
glVertex2f( vertexLeft, vertexBottom );
|
|
|
|
glTexCoord2f( textLeft, textTop );
|
|
glVertex2f( vertexLeft, vertexTop );
|
|
|
|
glTexCoord2f( textRight, textTop );
|
|
glVertex2f( vertexRight, vertexTop );
|
|
|
|
glTexCoord2f( textRight, textBottom );
|
|
glVertex2f( vertexRight, vertexBottom );
|
|
}
|
|
glEnd( );
|
|
}
|
|
glDisable( GL_TEXTURE_2D );
|
|
}
|
|
|
|
float Texture::GetWidth() const
|
|
{
|
|
return m_Width;
|
|
}
|
|
|
|
float Texture::GetHeight() const
|
|
{
|
|
return m_Height;
|
|
}
|
|
|
|
bool Texture::IsCreationOk( ) const
|
|
{
|
|
return m_CreationOk;
|
|
}
|
|
Rectf Texture::getSrcRect() {
|
|
return Rectf{0, 0, m_Width, m_Height};
|
|
}
|
|
|
|
void Texture::DrawFilledRect(const Rectf& rect) const
|
|
{
|
|
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
|
|
glBegin(GL_POLYGON);
|
|
{
|
|
glVertex2f(rect.left, rect.bottom);
|
|
glVertex2f(rect.left + rect.width, rect.bottom);
|
|
glVertex2f(rect.left + rect.width, rect.bottom + rect.height);
|
|
glVertex2f(rect.left , rect.bottom + rect.height);
|
|
}
|
|
glEnd();
|
|
|
|
}
|