📄 texture.cpp
字号:
/*+-------------------------------------------------------------------
Ben Landon
CSCI E-235
Final Project
Texture.cpp
*/
#include "gl_common.h"
#include "Texture.hpp"
Texture::Texture (unsigned int width, unsigned int height)
: m_width(width), m_height(height)
{
glGenTextures(1, &m_texture_id);
}
Texture::~Texture()
{
glDeleteTextures(1, &m_texture_id);
}
void Texture::make_current (void) const
{
glBindTexture(GL_TEXTURE_2D, m_texture_id);
GLenum err = glGetError();
}
GrayscaleTexture::GrayscaleTexture (Map_of<float>& gray_map)
: Texture(gray_map.width(), gray_map.height())
{
// FIXME - Ensure the width and height
// these are powers of two.
unsigned int width = gray_map.width();
unsigned int height = gray_map.height();
m_grayscale_texels = new float[width * height];
unsigned int counter = 0;
for (int j = 0; j < (int)height; j++)
{
for (int i = 0; i < (int)width; i++)
{
m_grayscale_texels[counter++] = gray_map.get(i, j);
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, m_texture_id);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
throw TextureException();
int rc = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, width, height,
GL_LUMINANCE, GL_FLOAT, m_grayscale_texels);
if (rc != 0)
throw TextureException();
}
GrayscaleTexture::~GrayscaleTexture ()
{
if (m_grayscale_texels)
{
delete [] m_grayscale_texels;
m_grayscale_texels = NULL;
}
}
/*+-------------------------------------------------------------------
GrayscaleTexture::mipmap_texels
Get the mipmap texels cooresponding to the specified texture level.
*/
void GrayscaleTexture::mipmap_texels (int mipmap_level, float* texels) const
{
glBindTexture(GL_TEXTURE_2D, m_texture_id);
int rc = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, m_width, m_height,
GL_LUMINANCE, GL_FLOAT, m_grayscale_texels);
if (rc != 0)
throw TextureException();
glGetTexImage(GL_TEXTURE_2D,
mipmap_level,
GL_LUMINANCE,
GL_FLOAT,
texels);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
throw TextureException();
}
/*+-------------------------------------------------------------------
GrayscaleTexture::get_mipmap_dimensions
*/
void GrayscaleTexture::get_mipmap_dimensions (int level, int& width, int& height) const
{
width = m_width >> level;
height = m_height >> level;
}
/*+-------------------------------------------------------------------
GrayscaleTexture::mipmap_level
Compute the mipmap level that corresponds to the given width and height
A return value of -1 means that no mipmap level corresponds to this width
and height.
*/
int GrayscaleTexture::mipmap_level (unsigned int width, unsigned int height) const
{
if (width > m_width || height > m_height)
return -1;
if (width == m_width && height == m_height)
return 0;
unsigned int count = 1;
unsigned int computed_width = m_width;
unsigned int computed_height = m_height;
while (computed_height > 0 && computed_height > 0)
{
computed_height >>= 1;
computed_width >>= 1;
if ((width == computed_width) && (height == computed_height))
return count;
else
count++;
}
return -1;
}
/*+-------------------------------------------------------------------
SceneTexture::components_per_pixel
This is the number of components in a given pixel
for a SceneTexture. 4 means that there are four float
values per pixel in the SceneTexture pixel array, in
RGBA order.
*/
int SceneTexture::components_per_pixel = 4; // RGBA
/*+-------------------------------------------------------------------
SceneTexture constructor
*/
SceneTexture::SceneTexture (int width, int height)
: Texture(width, height)
{
m_pixels = new float[components_per_pixel * width * height];
if (!m_pixels)
throw TextureException();
this->clear();
}
/*+-------------------------------------------------------------------
SceneTexture destructor
*/
SceneTexture::~SceneTexture ()
{
if (m_pixels)
{
delete [] m_pixels;
m_pixels = NULL;
}
}
/*+-------------------------------------------------------------------
SceneTexture::copy_into
This method copies the data from this SceneTexture object
into the target SceneTexture. If there is texture data in
the target SceneTarget it is deleted.
The width and height of the target SceneTexture will be
set to the width and height of this SceneTexture object.
*/
void SceneTexture::copy_into (SceneTexture& target)
{
target.clear();
if (m_pixels)
{
size_t array_size = components_per_pixel * m_width * m_height;
target.m_pixels = new float[array_size];
if (!target.m_pixels)
throw TextureException();
memcpy(target.m_pixels, m_pixels, array_size * sizeof(float));
}
target.m_width = m_width;
target.m_height = m_height;
}
/*+-------------------------------------------------------------------
subtract_texel
Subtracts val from the texel at (u, v) and writes that value back to
the texture.
*/
void SceneTexture::subtract (const Color& color_to_subtract,
int u, int v,
bool reset_gl_texture)
{
assert(u >= 0 && u < (int)m_width);
assert(v >= 0 && v < (int)m_height);
Color texel;
this->get(texel, u, v);
float red = texel.red() - color_to_subtract.red();
float green = texel.green() - color_to_subtract.green();
float blue = texel.blue() - color_to_subtract.blue();
// Clamp to zero
red = (red < 0.0f ? 0.0f : red);
green = (green < 0.0f ? 0.0f : green);
blue = (blue < 0.0f ? 0.0f : blue);
texel.set(red, green, blue);
this->set(texel, u, v);
if (reset_gl_texture)
this->tell_opengl();
}
/*+-------------------------------------------------------------------
SceneTexture::add
Add a color to a texel at a given position. Clamp at [0, 1].
*/
void SceneTexture::add (const Color& color_to_add,
int u, int v,
bool reset_gl_texture)
{
float red_to_add = color_to_add.red();
float green_to_add = color_to_add.green();
float blue_to_add = color_to_add.blue();
Color existing_color;
this->get(existing_color, u, v);
float red = existing_color.r() + red_to_add;
float green = existing_color.g() + green_to_add;
float blue = existing_color.g() + blue_to_add;
// Clamp to zero
red = (red < 0.0f ? 0.0f : red);
green = (green < 0.0f ? 0.0f : green);
blue = (blue < 0.0f ? 0.0f : blue);
// Clamp to one
red = (red > 1.0f ? 1.0f : red);
green = (green > 1.0f ? 1.0f : green);
blue = (blue > 1.0f ? 1.0f : blue);
Color texel(red, green, blue);
this->set(texel, u, v);
if (reset_gl_texture)
this->tell_opengl();
}
/*+-------------------------------------------------------------------
SceneTexture::clear
Clears all of the texels to zero. This is currently done by memset,
which may not be the right thing to do. Maybe MMX or SSE can be used
eventually to optimize this.
*/
void SceneTexture::clear (void)
{
if (m_pixels)
{
for (int i = 0; i < (int)m_width; i++)
{
for (int j = 0; j < (int)m_height; j++)
{
this->set(black, i, j);
}
}
}
}
/*+-------------------------------------------------------------------
SceneTexture::set (method)
Sets the texel at (u, v) to the color specified.
This method does no bounds checking so it is up to
the calling code to ensure that that (u, v) are valid
texture coordinates for this SceneTexture object.
*/
void SceneTexture::set (const Color& color, int u, int v)
{
if (!m_pixels)
throw TextureException();
unsigned int offset = components_per_pixel * (v * m_width + u);
m_pixels[offset] = color.r();
m_pixels[offset + 1] = color.g();
m_pixels[offset + 2] = color.b();
m_pixels[offset + 3] = color.a();
}
/*+-------------------------------------------------------------------
SceneTexture::get_brightest_texel
This can be optimized with mipmaps eventually.
*/
void SceneTexture::get_brightest_texel (int& u, int& v, Color& c) const
{
int brightest_u = 0;
int brightest_v = 0;
Color brightest_color;
// Use the first texel as the brightest.
// This corresponds to (u, v) of (0.0)
brightest_color.set_from_array(m_pixels, 4);
for (int i = 0; i < (int)m_width; i++)
{
for (int j = 0; j < (int)m_height; j++)
{
Color temp_color;
unsigned int offset = get_pixel_offset(i, j);
temp_color.set_from_array(&m_pixels[offset],4);
float lum1 = temp_color.sum();
float lum2 = brightest_color.sum();
if (lum1 > lum2)
{
brightest_u = i;
brightest_v = j;
brightest_color = temp_color;
}
}
}
c = brightest_color;
u = brightest_u;
v = brightest_v;
}
/*+-------------------------------------------------------------------
SceneTexture::get_texel_sum
Sum the texels in this texture. This is
the sum over all texels or (R + G + B) * A
*/
float SceneTexture::get_texel_sum (void) const
{
float texture_sum = 0.0f;
Color temp_color;
for (int i = 0; i < (int)m_width; i++)
{
for (int j = 0; j < (int)m_height; j++)
{
this->get(temp_color, i, j);
texture_sum += temp_color.sum();
}
}
return texture_sum;
}
/*+-------------------------------------------------------------------
SceneTexture::get
*/
void SceneTexture::get (Color& color, int u, int v) const
{
assert(u >= 0 && u < (int)m_width);
assert(v >= 0 && v < (int)m_height);
assert(m_pixels != NULL);
unsigned int offset = get_pixel_offset(u, v);
color.set_from_array(&m_pixels[offset], 4);
}
/*+-------------------------------------------------------------------
SceneTexture::delete_texture_data (private method)
This method is used to discard existing texture data.
*/
void SceneTexture::delete_texture_data (void)
{
// FIXME - Should I worry about what happens if
// the texels are currently be used by OpenGL.
if (m_pixels)
{
delete [] m_pixels;
m_pixels = NULL;
m_width = 0;
m_height = 0;
}
}
// Tell OpenGL about the texels in this texture object.
void SceneTexture::tell_opengl (void)
{
glBindTexture(GL_TEXTURE_2D, m_texture_id);
int rc = gluBuild2DMipmaps(GL_TEXTURE_2D,
components_per_pixel,
m_width,
m_height,
GL_RGBA, GL_FLOAT,
m_pixels);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
const GLubyte* str = gluErrorString(err);
throw TextureException();
}
}
/*+-------------------------------------------------------------------
SceneTextures::set_texels
*/
void SceneTexture::set_texels (float* texels, unsigned int num_texels)
{
assert(texels != NULL);
memcpy(m_pixels, texels, num_texels * sizeof(float));
}
/*+-------------------------------------------------------------------
SceneTextures::normalize
*/
void SceneTexture::normalize (void)
{
// Go through the texture and renormalize such that the
// brightest texel has one in a particular channel.
float max_red = 0.0f;
float max_green = 0.0f;
float max_blue = 0.0f;
unsigned int max_texel_index = m_width * m_height;
assert(components_per_pixel >= 3);
for (unsigned index = 0;
index < max_texel_index;
index += components_per_pixel)
{
float red = m_pixels[index];
float green = m_pixels[index + 1];
float blue = m_pixels[index + 2];
if (red > max_red)
max_red = red;
if (green > max_green)
max_green = green;
if (blue > max_blue)
max_blue = blue;
}
float max_val = max(max_red, max(max_green, max_blue));
if (max_val > 1.0f)
{
for (unsigned index = 0; index < max_texel_index; index ++)
{
m_pixels[index] = m_pixels[index]/max_val;
}
}
}
void SceneTexture::clone_into (SceneTexture& clone_target)
{
size_t pixel_array_size = components_per_pixel * m_width * m_height;
if (clone_target.m_width != m_width ||
clone_target.m_height != m_height)
{
if (clone_target.m_pixels)
delete [] (clone_target.m_pixels);
clone_target.m_width = m_width;
clone_target.m_height = m_height;
clone_target.m_pixels = new float [pixel_array_size];
}
memcpy((clone_target.m_pixels), m_pixels, pixel_array_size);
clone_target.m_texture_id = m_texture_id;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -