From 135c19b4337e1ab45cff40a306eadc52a061ddef Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Mon, 13 Aug 2012 22:18:03 +0300 Subject: add atlas textures support. --- src/resources/atlasmanager.cpp | 302 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 src/resources/atlasmanager.cpp (limited to 'src/resources/atlasmanager.cpp') diff --git a/src/resources/atlasmanager.cpp b/src/resources/atlasmanager.cpp new file mode 100644 index 000000000..770e43412 --- /dev/null +++ b/src/resources/atlasmanager.cpp @@ -0,0 +1,302 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * 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 2 of the License, or + * 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, see . + */ + +#include "main.h" + +#ifdef USE_OPENGL + +#include "resources/atlasmanager.h" +#include "resources/openglimagehelper.h" + +#include "client.h" +#include "graphics.h" +#include "graphicsmanager.h" +#include "logger.h" + +#include "utils/mathutils.h" +#include "utils/physfsrwops.h" + +#include "resources/fboinfo.h" +#include "resources/imagehelper.h" +#include "resources/imagewriter.h" +#include "resources/resourcemanager.h" + +#include "debug.h" + +AtlasManager::AtlasManager() +{ +} + +AtlasResource *AtlasManager::loadTextureAtlas(const std::string &name, + const StringVect &files) +{ + std::vector atlases; + std::vector images; + AtlasResource *resource = new AtlasResource; + + loadImages(files, images); + int maxSize = OpenGLImageHelper::getTextureSize(); +// int maxSize = 1024; + + // sorting images on atlases. + simpleSort(name, atlases, images, maxSize); + + int k = 0; + for (std::vector::iterator it = atlases.begin(), + it_end = atlases.end(); it != it_end; ++ it) + { + TextureAtlas *atlas = *it; + if (!atlas) + continue; + + // create atlas base on sorted images + SDL_Surface *surface = createSDLAtlas(atlas); + + if (!surface) + continue; + + // debug save + ImageWriter::writePNG(surface, Client::getTempDirectory() + + "/atlas" + name + toString(k) + ".png"); + k ++; + + // covert SDL images to OpenGL + convertAtlas(atlas); + + // free SDL atlas surface + SDL_FreeSurface(surface); + + resource->atlases.push_back(atlas); + } + + return resource; +} + +void AtlasManager::loadImages(const StringVect &files, + std::vector &images) +{ + for (StringVectCIter it = files.begin(), it_end = files.end(); + it != it_end; ++ it) + { + const std::string str = *it; + SDL_RWops *rw = PHYSFSRWOPS_openRead(str.c_str()); + Image *image = sdlImageHelper->load(rw); + if (image) + { + image->mIdPath = str; + images.push_back(image); + } + } +} + +void AtlasManager::simpleSort(const std::string &name, + std::vector &atlases, + std::vector &images, int size) +{ + int x = 0; + int y = 0; + int tempHeight = 0; + TextureAtlas *atlas = new TextureAtlas(); + std::vector::iterator it = images.begin(); + const std::vector::const_iterator it_end = images.end(); + for (it = images.begin(); it != it_end; ++ it) + { + Image *img = *it; + if (img) + { + atlas->name = "atlas_" + name + "_" + img->getIdPath(); + break; + } + } + + for (it = images.begin(); it != it_end; ++ it) + { + Image *img = *it; + if (img) + { + AtlasItem *item = new AtlasItem(img); + item->name = img->getIdPath(); + // start next line + if (x + img->mBounds.w > size) + { + x = 0; + y += tempHeight; + tempHeight = 0; + } + + // cant put image with this height + if (y + img->mBounds.h > size) + { + x = 0; + y = 0; + atlases.push_back(atlas); + atlas = new TextureAtlas(); + atlas->name = "atlas_" + name + "_" + img->getIdPath(); + } + + if (img->mBounds.h > tempHeight) + tempHeight = img->mBounds.h; + + logger->log("image draw position: %d,%d (%d,%d)", + x, y, img->mBounds.w, img->mBounds.h); + item->x = x; + item->y = y; + atlas->items.push_back(item); + + // continue put textures in line + x += img->mBounds.w; + if (x > atlas->width) + atlas->width = x; + if (y + img->mBounds.h > atlas->height) + atlas->height = y + img->mBounds.h; + } + } + if (!atlas->items.empty()) + atlases.push_back(atlas); + else + delete atlas; +} + +SDL_Surface *AtlasManager::createSDLAtlas(TextureAtlas *atlas) +{ +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int rmask = 0xff000000; + const int gmask = 0x00ff0000; + const int bmask = 0x0000ff00; + const int amask = 0x000000ff; +#else + const int rmask = 0x000000ff; + const int gmask = 0x0000ff00; + const int bmask = 0x00ff0000; + const int amask = 0xff000000; +#endif + + // do not create atlas based on only one image + if (atlas->items.size() == 1) + return nullptr; + + // using only power of two sizes. + atlas->width = powerOfTwo(atlas->width); + atlas->height = powerOfTwo(atlas->height); + + // temp SDL surface for atlas + SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, + atlas->width, atlas->height, 32, rmask, gmask, bmask, amask); + + if (!surface) + return nullptr; + + Graphics *graphics = new Graphics(); + graphics->setTarget(surface); + graphics->_beginDraw(); + + // drawing SDL images to surface + for (std::vector::iterator it = atlas->items.begin(), + it_end = atlas->items.end(); + it != it_end; ++ it) + { + AtlasItem *item = *it; + Image *image = item->image; + + if (image) + { + SDL_SetAlpha(image->mSDLSurface, 0, SDL_ALPHA_OPAQUE); + graphics->drawImage(image, item->x, item->y); + } + } + + delete graphics; + atlas->surface = surface; + return surface; +} + +void AtlasManager::convertAtlas(TextureAtlas *atlas) +{ + // convert surface to OpemGL image + atlas->atlasImage = imageHelper->load(atlas->surface); + Image *const image = atlas->atlasImage; + + for (std::vector::iterator it = atlas->items.begin(), + it_end = atlas->items.end(); + it != it_end; ++ it) + { + AtlasItem *const item = *it; + // delete SDL Image + delete item->image; + // store OpenGL image + item->image = image->getSubImage(item->x, item->y, + item->width, item->height); + } +} + +void AtlasManager::injectToResources(AtlasResource *resource) +{ + ResourceManager *const resman = ResourceManager::getInstance(); + int k = 0; + for (std::vector::iterator it = resource->atlases.begin(), + it_end = resource->atlases.end(); it != it_end; ++ it, k ++) + { + // add each atlas image to resources + TextureAtlas *const atlas = *it; + resman->addResource(atlas->name, atlas->atlasImage); + if (atlas) + { + for (std::vector::iterator it2 = atlas->items.begin(), + it2_end = atlas->items.end(); + it2 != it2_end; ++ it2) + { + AtlasItem *const item = *it2; + // add each atlas sub image to resources + resman->addResource(item->name, item->image); + } + } + } +} + +AtlasResource::~AtlasResource() +{ + for (std::vector::iterator it = atlases.begin(), + it_end = atlases.end(); it != it_end; ++ it) + { + TextureAtlas *const atlas = *it; + if (atlas) + { + for (std::vector::iterator it2 = atlas->items.begin(), + it2_end = atlas->items.end(); + it2 != it2_end; ++ it2) + { + AtlasItem *const item = *it2; + if (item) + { + Image *const image = item->image; + if (image) + image->decRef(); + delete item; + } + } + Image *const image = atlas->atlasImage; + if (image) + image->decRef(); + delete atlas; + } + } +} + +#endif -- cgit v1.2.3-70-g09d2