CSC3223_Graphics_For_Games_Software_Rasteriser / SoftwareRasteriser / Texture.cpp
Texture.cpp
Raw
#include "Texture.h"

Texture::Texture(void)
{
  width = 0;
  height = 0;

  texels = NULL;

  CreateMipMaps();
}

Texture::~Texture(void)
{
  delete[] texels;
}

Texture* Texture::TextureFromTGA(const string &filename) {
  Texture* t = new Texture();
  std::ifstream file;

  std::cout << "Loading TGA from(" << filename << ")" << std::endl;
  file.open(filename.c_str(), std::ios::binary);
  if (!file.is_open()) {
    std::cout << "TextureFromTGA file error" << std::endl;
    return t;
  }

  unsigned char TGAheader[18];

  std::cout << "sizeof(TGAheader) is " << sizeof(TGAheader) << std::endl;

  file.read((char *)TGAheader, sizeof(TGAheader));

  t->width = (TGAheader[12] + (TGAheader[13] << 8));
  t->height = (TGAheader[14] + (TGAheader[15] << 8));

  int size = t->width * t->height * (TGAheader[16] / 8);

  t->texels = new Colour[t->width * t->height];

  file.read((char *)t->texels, size);
  file.close();

  return t;
}

const Colour& Texture::NearestTexSample(const Vector3& coords, int miplevel) {
  miplevel = min(miplevel, mipLevels.size() - 1);
  miplevel = (mipLevels.size() - 1) - miplevel;

  const int texWidth = width >> miplevel;
  const int texHeight = height >> miplevel;

  int x = (int) (coords.x * (texWidth - 1));
  int y = (int) (coords.y * (texHeight - 1));

  return ColourAtPoint(x, y, miplevel);
}

const Colour& Texture::BilinearTexSample(const Vector3& coords, int miplevel) {
  const int texWidth = width;
  const int texHeight = height;

  const int x = (int) (coords.x * texWidth);
  const int y = (int) (coords.y * texHeight);

  const Colour &tl = ColourAtPoint(x, y);
  const Colour &tr = ColourAtPoint(x+1, y);
  const Colour &bl = ColourAtPoint(x, y+1);
  const Colour &br = ColourAtPoint(x+1, y+1);

  const float fracX = (coords.x * texWidth) - x;
  const float fracY = (coords.y * texHeight) - y;

  Colour top = Colour::Lerp(tl, tr, fracX);
  Colour bottom = Colour::Lerp(bl, br, fracX);

  return Colour::Lerp(top, bottom, fracY);
}

void Texture::CreateMipMaps() {
  int tempWidth = width;
  int tempHeight = height;

  mipLevels.push_back(texels);

  int numLevels = 0;
  while (tempWidth > 1 && tempHeight > 1) {
    tempWidth = tempWidth >> 1;
    tempHeight = tempHeight >> 1;

    Colour * newLevel = new Colour[tempWidth * tempHeight];
    GenerateMipLevel(mipLevels.back(), newLevel, numLevels);

    numLevels++;
    mipLevels.push_back(newLevel);
  }
}

void Texture::GenerateMipLevel(Colour* source, Colour* dest, int mipLevel) {
  int sourceWidth = width >> mipLevel;
  int sourceHeight = height >> mipLevel;

  int destWidth = width >> (mipLevel + 1);
  int destHeight = height >> (mipLevel + 1);

  int outY = 0;

  for (int y = 0; y < sourceHeight; y += 2) {
    int outX = 0;
    for (int x = 0; x < sourceWidth; x += 2) {
      Colour out;

      out += source[(y * sourceHeight) + x] * 0.25f;
      out += source[(y * sourceHeight) + x+1] * 0.25f;
      out += source[((y+1) * sourceHeight) + x] * 0.25f;
      out += source[((y+1) * sourceHeight) + x+1] * 0.25f;

      dest[outY * destHeight + outX] = out;
      outX++;
    }
    outY++;
  }
}