#include "VolumeTexture3D.hpp" #include "Debug.hpp" #include #include #include #include TSE::VolumeTexture3D::VolumeTexture3D(const string &path) { namespace fs = std::filesystem; Size = Vector3(0, 0, 0); Bpp = 0; std::error_code ec; const fs::path folderPath = fs::absolute(path, ec); if(ec || !fs::exists(folderPath, ec)) { TSE_WARNING("Can't create VolumeTexture3D because the given path is inaccessible: \n" + path); return; } if(ec || !fs::is_directory(folderPath, ec)) { TSE_WARNING("Can't create VolumeTexture3D because the given path is not a folder: \n" + folderPath.string()); return; } std::vector pngFiles; for(fs::directory_iterator it(folderPath, ec); !ec && it != fs::directory_iterator(); it.increment(ec)) { if(!it->is_regular_file(ec)) continue; const fs::path &filePath = it->path(); auto extension = filePath.extension().string(); std::transform(extension.begin(), extension.end(), extension.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if(extension == ".png") pngFiles.push_back(filePath); } if(ec) { TSE_WARNING("Can't read VolumeTexture3D folder: \n" + folderPath.string()); return; } if(pngFiles.empty()) { TSE_WARNING("Can't create VolumeTexture3D because the folder contains no png files: \n" + folderPath.string()); return; } std::sort(pngFiles.begin(), pngFiles.end()); std::vector loadedBitmaps; loadedBitmaps.reserve(pngFiles.size()); uint width = 0; uint height = 0; for(const fs::path &filePath : pngFiles) { FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(filePath.string().c_str(), 0); if(fif == FREE_IMAGE_FORMAT::FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(filePath.string().c_str()); if(fif == FREE_IMAGE_FORMAT::FIF_UNKNOWN || !FreeImage_FIFSupportsReading(fif)) { TSE_WARNING("Failed to load png for VolumeTexture3D: \n" + filePath.string()); for(FIBITMAP* loadedBmp : loadedBitmaps) FreeImage_Unload(loadedBmp); return; } FIBITMAP* currentBmp = FreeImage_Load(fif, filePath.string().c_str()); if(currentBmp == nullptr) { TSE_WARNING("Failed to load png for VolumeTexture3D: \n" + filePath.string()); for(FIBITMAP* loadedBmp : loadedBitmaps) FreeImage_Unload(loadedBmp); return; } if(FreeImage_GetBPP(currentBmp) != 32) { FIBITMAP* convertedBmp = FreeImage_ConvertTo32Bits(currentBmp); FreeImage_Unload(currentBmp); currentBmp = convertedBmp; } if(currentBmp == nullptr) { TSE_WARNING("Failed to convert png for VolumeTexture3D: \n" + filePath.string()); for(FIBITMAP* loadedBmp : loadedBitmaps) FreeImage_Unload(loadedBmp); return; } const uint currentWidth = FreeImage_GetWidth(currentBmp); const uint currentHeight = FreeImage_GetHeight(currentBmp); if(loadedBitmaps.empty()) { width = currentWidth; height = currentHeight; } else if(currentWidth != width || currentHeight != height) { TSE_WARNING("Can't create VolumeTexture3D because all pngs must have the same dimensions: \n" + filePath.string()); FreeImage_Unload(currentBmp); for(FIBITMAP* loadedBmp : loadedBitmaps) FreeImage_Unload(loadedBmp); return; } loadedBitmaps.push_back(currentBmp); } Bpp = 32; chanels = 4; Size = Vector3(width, height, static_cast(loadedBitmaps.size())); bmp = new FIBITMAP*[loadedBitmaps.size()]; imagePtr = new byte*[loadedBitmaps.size()]; for(size_t i = 0; i < loadedBitmaps.size(); ++i) { bmp[i] = loadedBitmaps[i]; imagePtr[i] = FreeImage_GetBits(bmp[i]); } regist(); } TSE::VolumeTexture3D::VolumeTexture3D(const int &width, const int &height, const int &depth, int bpp) { switch (bpp) { case 32: chanels = 4; break; case 24: chanels = 3; case 16: chanels = 1; case 8: chanels = 4; } Bpp = bpp; Size = Vector3(width, height, depth); bmp = new FIBITMAP*[depth]; imagePtr = new byte*[depth]; for(int i = 0; i < Depth(); i++) { bmp[i] = FreeImage_Allocate(width, height, bpp); imagePtr[i] = FreeImage_GetBits(bmp[i]); } regist(); } TSE::VolumeTexture3D::VolumeTexture3D(const Vector3 &size, int bpp) : VolumeTexture3D(size.x, size.y, size.z, bpp) { } TSE::VolumeTexture3D::~VolumeTexture3D() { if(bmp != nullptr) { for(int i = 0; i < Depth(); i++) { if(bmp[i] != nullptr) { FreeImage_Unload(bmp[i]); bmp[i] = nullptr; } } delete [] bmp; delete [] imagePtr; } if(TextureID != 0) { PlatformDestroy(); TextureID = 0; } } TSE::uint TSE::VolumeTexture3D::bpp() const { return Bpp; } TSE::Vector2 TSE::VolumeTexture3D::size() const { return Vector2(Size.x, Size.y); } float TSE::VolumeTexture3D::Width() const { return Size.x; } float TSE::VolumeTexture3D::Height() const { return Size.y; } float TSE::VolumeTexture3D::Depth() const { return Size.z; } TSE::byte TSE::VolumeTexture3D::Chanels() const { return chanels; } TSE::byte *TSE::VolumeTexture3D::GetImagePtr(const int depth) const { return imagePtr[depth]; } void TSE::VolumeTexture3D::SetPixel(const Vector3 &pos, const Color &c) { SetPixel(pos.x, pos.y, pos.z, c); } void TSE::VolumeTexture3D::GetPixel(const Vector3 &pos, Color &c) const { GetPixel(pos.x, pos.y, pos.z, c); } void TSE::VolumeTexture3D::SetPixelNoApply(const Vector3 &pos, const Color &c) { SetPixelNoApply(pos.x, pos.y, pos.z, c); } void TSE::VolumeTexture3D::SetChanels(const byte &ch) { chanels = ch; } TSE::uint TSE::VolumeTexture3D::GetTextureId() const { return TextureID; } void TSE::VolumeTexture3D::SetTextureId(uint id) { TextureID = id; } void TSE::VolumeTexture3D::SetPixel(const int &x, const int &y, const int &z, const Color &c) { SetPixelNoApply(x,y,z,c); Apply(); } void TSE::VolumeTexture3D::GetPixel(const int &x, const int &y, const int &z, Color &c) const { if(x >= Width() || x < 0 || y >= Height() || y < 0|| z >= Depth() || z < 0) { TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ";" + std::to_string(z) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) + ";" + std::to_string(Depth()) ); return; } byte* pixel = getPixelPointer(x,y,z); byte b = *pixel++; byte g = *pixel++; byte r = *pixel++; byte a = *pixel++; if(bpp() == 8) c = Color(r,r,r); else if(bpp() == 24) c = Color(r,g,b,a); else if(bpp() == 32) c = Color(r,g,b); } void TSE::VolumeTexture3D::Fill(const Color &c) { for (int x = 0; x < Width(); x++) { for (int y = 0; y < Height(); y++) { for (int z = 0; z < Depth(); z++) { SetPixelNoApply(x,y,z,c); } } } Apply(); } void TSE::VolumeTexture3D::SetPixelNoApply(const int &x, const int &y, const int &z, const Color &c) { if(x >= Width() || x < 0 || y >= Height() || y < 0 || z >= Depth() || z < 0) { TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ";" + std::to_string(z) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) + ";" + std::to_string(Depth()) ); return; } byte* pixel = getPixelPointer(x,y,z); if(chanels == 4 && bpp() == 8) { byte r2bit = static_cast(c.r * 3.0f); byte g2bit = static_cast(c.g * 3.0f); byte b2bit = static_cast(c.b * 3.0f); byte a2bit = static_cast(c.a * 3.0f); byte result = (r2bit << 6) | (g2bit << 4) | (b2bit << 2) | a2bit; *pixel++ = result; return; } *pixel++ = c.B(); if(bpp() > 8) { *pixel++ = c.G(); *pixel++ = c.R(); if(bpp() > 24) *pixel++ = c.A(); } } void TSE::VolumeTexture3D::AddPixelNoApply(const int &x, const int &y, const int &z, const Color &c) { if(x >= Width() || x < 0 ||y >= Height() || y < 0 || z >= Depth() || z < 0) { TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ";" + std::to_string(z) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) + ";" + std::to_string(Depth()) ); return; } byte* pixel = getPixelPointer(x,y,z); if(chanels == 4 && bpp() == 8) //TODO add propper adding, becouse currently is only setting { byte r2bit = static_cast(c.r * 3.0f); byte g2bit = static_cast(c.g * 3.0f); byte b2bit = static_cast(c.b * 3.0f); byte a2bit = static_cast(c.a * 3.0f); byte result = (r2bit << 6) | (g2bit << 4) | (b2bit << 2) | a2bit; *pixel++ = result; return; } Color bc(pixel[2], pixel[1], pixel[0], pixel[3]); bc = c + bc; *pixel++ = bc.B(); if(bpp() > 8) { *pixel++ = bc.G(); *pixel++ = bc.R(); if(bpp() > 24) *pixel++ = bc.A(); } } void TSE::VolumeTexture3D::Recreate(const int &x, const int &y, const int &z, const int &bpp, const int &chanels) { if(bmp != nullptr) { for(int i = 0; i < Depth(); i++) { if(bmp[i] != nullptr) { FreeImage_Unload(bmp[i]); bmp[i] = nullptr; } } delete [] bmp; delete [] imagePtr; } if(TextureID != 0) { PlatformDestroy(); TextureID = 0; } this->chanels = chanels; Bpp = bpp; Size = Vector3(x, y, z); bmp = new FIBITMAP*[z]; imagePtr = new byte*[z]; for(int i = 0; i < Depth(); i++) { bmp[i] = FreeImage_Allocate(x, y, bpp); imagePtr[i] = FreeImage_GetBits(bmp[i]); } regist(); } void TSE::VolumeTexture3D::SavePNG(const std::string &path) const { //TODO: implement save all images as PNG in the given path, if the given path is inaccaseable, or not a folder, ust TSE_WARNING with an apropriate message } TSE::byte *TSE::VolumeTexture3D::getPixelPointer(const int &x, const int &y, const int &z) const { int alphaoffset = y * 2; if(bpp() > 24) alphaoffset = 0; int offset = ((y * Width() + x) * (bpp() / 8) + alphaoffset); return imagePtr[z] + offset; } void TSE::VolumeTexture3D::bind() const { helper->Bind3D(this); } void TSE::VolumeTexture3D::unbind() const { helper->UnBind3D(this); } void TSE::VolumeTexture3D::Apply() { helper->Apply3D(this); } void TSE::VolumeTexture3D::regist() { helper->Regist3D(this); } void TSE::VolumeTexture3D::PlatformDestroy() { helper->PlatromDestroy3D(this); }