X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftile.cpp;h=17ec51614fe1265eaf8f2cf72762c875d6060004;hb=37b7f094e3ea502339794f64e8bad22444c6fb54;hp=6e4fde011acf411ec8ba7021a8cd472679199231;hpb=b850f0f03829cbb024d22672de0af29cab001d86;p=oweals%2Fminetest.git diff --git a/src/tile.cpp b/src/tile.cpp index 6e4fde011..17ec51614 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -32,6 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/thread.h" #include "util/numeric.h" +#ifdef __ANDROID__ +#include +#endif + /* A cache from texture name to texture path */ @@ -58,7 +62,7 @@ static bool replace_ext(std::string &path, const char *ext) last_dot_i = i; break; } - + if(path[i] == '\\' || path[i] == '/') break; } @@ -97,7 +101,7 @@ std::string getImagePath(std::string path) return path; } while((++ext) != NULL); - + return ""; } @@ -120,7 +124,7 @@ std::string getTexturePath(const std::string &filename) bool incache = g_texturename_to_path_cache.get(filename, &fullpath); if(incache) return fullpath; - + /* Check from texture_path */ @@ -131,18 +135,6 @@ std::string getTexturePath(const std::string &filename) // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); } - - /* - Check from $user/textures/all - */ - if(fullpath == "") - { - std::string texture_path = porting::path_user + DIR_DELIM - + "textures" + DIR_DELIM + "all"; - std::string testpath = texture_path + DIR_DELIM + filename; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } /* Check from default data directory @@ -155,14 +147,19 @@ std::string getTexturePath(const std::string &filename) // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); } - + // Add to cache (also an empty result is cached) g_texturename_to_path_cache.set(filename, fullpath); - + // Finally return it return fullpath; } +void clearTextureNameCache() +{ + g_texturename_to_path_cache.clear(); +} + /* Stores internal information about a texture. */ @@ -171,16 +168,13 @@ struct TextureInfo { std::string name; video::ITexture *texture; - video::IImage *img; // The source image TextureInfo( const std::string &name_, - video::ITexture *texture_=NULL, - video::IImage *img_=NULL + video::ITexture *texture_=NULL ): name(name_), - texture(texture_), - img(img_) + texture(texture_) { } }; @@ -309,14 +303,14 @@ public: getTextureId("stone.png^mineral_coal.png^crack0"). */ - + /* Gets a texture id from cache or - if main thread, from getTextureIdDirect - if other thread, adds to request queue and waits for main thread */ u32 getTextureId(const std::string &name); - + /* Example names: "stone.png" @@ -370,38 +364,38 @@ public: // Processes queued texture requests from other threads. // Shall be called from the main thread. void processQueue(); - + // Insert an image into the cache without touching the filesystem. // Shall be called from the main thread. void insertSourceImage(const std::string &name, video::IImage *img); - + // Rebuild images and textures from the current set of source images // Shall be called from the main thread. void rebuildImagesAndTextures(); - + // Render a mesh to a texture. // Returns NULL if render-to-texture failed. // Shall be called from the main thread. video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms); - + // Generates an image from a full string like - // "stone.png^mineral_coal.png^[crack0". + // "stone.png^mineral_coal.png^[crack:1:0". // Shall be called from the main thread. video::IImage* generateImageFromScratch(std::string name); - // Generate image based on a string like "stone.png" or "[crack0". + // Generate image based on a string like "stone.png" or "[crack:1:0". // if baseimg is NULL, it is created. Otherwise stuff is made on it. // Shall be called from the main thread. bool generateImage(std::string part_of_name, video::IImage *& baseimg); private: - + // The id of the thread that is allowed to use irrlicht directly threadid_t m_main_thread; // The irrlicht device IrrlichtDevice *m_device; - + // Cache of source images // This should be only accessed from the main thread SourceImageCache m_sourcecache; @@ -416,7 +410,7 @@ private: std::map m_name_to_id; // The two former containers are behind this mutex JMutex m_textureinfo_cache_mutex; - + // Queued texture fetches (to be processed by the main thread) RequestQueue m_get_texture_queue; @@ -439,15 +433,13 @@ TextureSource::TextureSource(IrrlichtDevice *device): m_device(device) { assert(m_device); - - m_textureinfo_cache_mutex.Init(); - + m_main_thread = get_current_thread_id(); - + // Add a NULL TextureInfo as the first index, named "" m_textureinfo_cache.push_back(TextureInfo("")); m_name_to_id[""] = 0; - + // Cache some settings // Note: Since this is only done once, the game must be restarted // for these settings to take effect @@ -469,10 +461,6 @@ TextureSource::~TextureSource() //cleanup texture if (iter->texture) driver->removeTexture(iter->texture); - - //cleanup source image - if (iter->img) - iter->img->drop(); } m_textureinfo_cache.clear(); @@ -506,7 +494,7 @@ u32 TextureSource::getTextureId(const std::string &name) return n->second; } } - + /* Get texture */ @@ -519,45 +507,53 @@ u32 TextureSource::getTextureId(const std::string &name) infostream<<"getTextureId(): Queued: name=\""< result_queue; - + static ResultQueue result_queue; + // Throw a request in m_get_texture_queue.add(name, 0, 0, &result_queue); - - infostream<<"Waiting for texture from main thread, name=\"" - < + while(true) { + // Wait result for a second + GetResult result = result_queue.pop_front(1000); - - // Check that at least something worked OK - assert(result.key == name); - return result.item; + if (result.key == name) { + return result.item; + } + } } catch(ItemNotFoundException &e) { - infostream<<"Waiting for texture timed out."<=0; i--) @@ -647,9 +643,9 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) < dim = ti->img->getDimension(); - - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + core::dimension2d dim = ti->texture->getSize(); - ti->img->copyTo( - baseimg, // target - v2s32(0,0), // position in target - core::rect(v2s32(0,0), dim) // from - ); + baseimg = driver->createImage(ti->texture,v2s32(0,0), dim); /*infostream<<"getTextureIdDirect(): Loaded \"" <addTexture(name.c_str(), baseimg); + baseimg->drop(); } - + /* Add texture to caches (add NULL textures too) */ JMutexAutoLock lock(m_textureinfo_cache_mutex); - + u32 id = m_textureinfo_cache.size(); - TextureInfo ti(name, t, baseimg); + TextureInfo ti(name, t); m_textureinfo_cache.push_back(ti); m_name_to_id[name] = id; - /*infostream<<"getTextureIdDirect(): " - <<"Returning id="< @@ -786,22 +778,16 @@ void TextureSource::processQueue() <<"name=\""< - result; - result.key = request.key; - result.callers = request.callers; - result.item = getTextureIdDirect(request.key); - - request.dest->push_back(result); + m_get_texture_queue.pushResult(request,getTextureIdDirect(request.key)); } } void TextureSource::insertSourceImage(const std::string &name, video::IImage *img) { //infostream<<"TextureSource::insertSourceImage(): name="<getVideoDriver()); m_source_image_existence.set(name, true); } @@ -811,19 +797,26 @@ void TextureSource::rebuildImagesAndTextures() JMutexAutoLock lock(m_textureinfo_cache_mutex); video::IVideoDriver* driver = m_device->getVideoDriver(); + assert(driver != 0); // Recreate textures for(u32 i=0; iname); +#ifdef __ANDROID__ + img = Align2Npot2(img,driver); + assert(img->getDimension().Height == npot2(img->getDimension().Height)); + assert(img->getDimension().Width == npot2(img->getDimension().Width)); +#endif // Create texture from resulting image video::ITexture *t = NULL; - if(img) + if(img) { t = driver->addTexture(ti->name.c_str(), img); + img->drop(); + } video::ITexture *t_old = ti->texture; // Replace texture ti->texture = t; - ti->img = img; if (t_old != 0) m_texture_trash.push_back(t_old); @@ -836,6 +829,126 @@ video::ITexture* TextureSource::generateTextureFromMesh( video::IVideoDriver *driver = m_device->getVideoDriver(); assert(driver); +#ifdef __ANDROID__ + const GLubyte* renderstr = glGetString(GL_RENDERER); + std::string renderer((char*) renderstr); + + // use no render to texture hack + if ( + (renderer.find("Adreno") != std::string::npos) || + (renderer.find("Mali") != std::string::npos) || + (renderer.find("Immersion") != std::string::npos) || + (renderer.find("Tegra") != std::string::npos) || + g_settings->getBool("inventory_image_hack") + ) { + // Get a scene manager + scene::ISceneManager *smgr_main = m_device->getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + const float scaling = 0.2; + + scene::IMeshSceneNode* meshnode = + smgr->addMeshSceneNode(params.mesh, NULL, + -1, v3f(0,0,0), v3f(0,0,0), + v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); + meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + params.camera_position, params.camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(params.camera_projection_matrix, false); + + smgr->setAmbientLight(params.ambient_light); + smgr->addLightSceneNode(0, + params.light_position, + params.light_color, + params.light_radius*scaling); + + core::dimension2d screen = driver->getScreenSize(); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + driver->clearZBuffer(); + smgr->drawAll(); + + core::dimension2d partsize(screen.Width * scaling,screen.Height * scaling); + + irr::video::IImage* rawImage = + driver->createImage(irr::video::ECF_A8R8G8B8, partsize); + + u8* pixels = static_cast(rawImage->lock()); + if (!pixels) + { + rawImage->drop(); + return NULL; + } + + core::rect source( + screen.Width /2 - (screen.Width * (scaling / 2)), + screen.Height/2 - (screen.Height * (scaling / 2)), + screen.Width /2 + (screen.Width * (scaling / 2)), + screen.Height/2 + (screen.Height * (scaling / 2)) + ); + + glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y, + partsize.Width, partsize.Height, GL_RGBA, + GL_UNSIGNED_BYTE, pixels); + + driver->endScene(); + + // Drop scene manager + smgr->drop(); + + unsigned int pixelcount = partsize.Width*partsize.Height; + + u8* runptr = pixels; + for (unsigned int i=0; i < pixelcount; i++) { + + u8 B = *runptr; + u8 G = *(runptr+1); + u8 R = *(runptr+2); + u8 A = *(runptr+3); + + //BGRA -> RGBA + *runptr = R; + runptr ++; + *runptr = G; + runptr ++; + *runptr = B; + runptr ++; + *runptr = A; + runptr ++; + } + + video::IImage* inventory_image = + driver->createImage(irr::video::ECF_A8R8G8B8, params.dim); + + rawImage->copyToScaling(inventory_image); + rawImage->drop(); + + video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image); + inventory_image->drop(); + + if (rtt == NULL) { + errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl; + return NULL; + } + + driver->makeColorKeyTexture(rtt, v2s32(0,0)); + + if(params.delete_texture_on_shutdown) + m_texture_trash.push_back(rtt); + + return rtt; + } +#endif + if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) { static bool warned = false; @@ -860,7 +973,12 @@ video::ITexture* TextureSource::generateTextureFromMesh( } // Set render target - driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0)); + if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) { + driver->removeTexture(rtt); + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"failed to set render target"<getSceneManager(); @@ -868,7 +986,9 @@ video::ITexture* TextureSource::generateTextureFromMesh( scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); assert(smgr); - scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + scene::IMeshSceneNode* meshnode = + smgr->addMeshSceneNode(params.mesh, NULL, + -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); meshnode->setMaterialFlag(video::EMF_LIGHTING, true); meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); @@ -891,11 +1011,6 @@ video::ITexture* TextureSource::generateTextureFromMesh( smgr->drawAll(); driver->endScene(); - // NOTE: The scene nodes should not be dropped, otherwise - // smgr->drop() segfaults - /*cube->drop(); - camera->drop(); - light->drop();*/ // Drop scene manager smgr->drop(); @@ -938,14 +1053,14 @@ video::IImage* TextureSource::generateImageFromScratch(std::string name) base_image_name = name.substr(0, last_separator_position); baseimg = generateImageFromScratch(base_image_name); } - + /* Parse out the last part of the name of the image and act according to it */ std::string last_part_of_name = name.substr(last_separator_position+1); - + // Generate image according to part of name if(!generateImage(last_part_of_name, baseimg)) { @@ -954,10 +1069,61 @@ video::IImage* TextureSource::generateImageFromScratch(std::string name) < +/** + * Check and align image to npot2 if required by hardware + * @param image image to check for npot2 alignment + * @param driver driver to use for image operations + * @return image or copy of image aligned to npot2 + */ +video::IImage * Align2Npot2(video::IImage * image, + video::IVideoDriver* driver) +{ + if(image == NULL) { + return image; + } + + core::dimension2d dim = image->getDimension(); + + std::string extensions = (char*) glGetString(GL_EXTENSIONS); + if (extensions.find("GL_OES_texture_npot") != std::string::npos) { + return image; + } + + unsigned int height = npot2(dim.Height); + unsigned int width = npot2(dim.Width); + + if ((dim.Height == height) && + (dim.Width == width)) { + return image; + } + + if (dim.Height > height) { + height *= 2; + } + + if (dim.Width > width) { + width *= 2; + } + + video::IImage *targetimage = + driver->createImage(video::ECF_A8R8G8B8, + core::dimension2d(width, height)); + + if (targetimage != NULL) { + image->copyToScaling(targetimage); + } + image->drop(); + return targetimage; +} + +#endif + bool TextureSource::generateImage(std::string part_of_name, video::IImage *& baseimg) { video::IVideoDriver* driver = m_device->getVideoDriver(); @@ -967,14 +1133,22 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas if(part_of_name.size() == 0 || part_of_name[0] != '[') { video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); - - if(image == NULL) - { - if(part_of_name != ""){ - errorstream<<"generateImage(): Could not load image \"" +#ifdef __ANDROID__ + image = Align2Npot2(image,driver); +#endif + if (image == NULL) { + if (part_of_name != "") { + if (part_of_name.find("_normal.png") == std::string::npos){ + errorstream<<"generateImage(): Could not load image \"" < dim = image->getDimension(); @@ -1037,10 +1211,12 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas /*infostream<<"generateImage(): generating special " <<"modification \""< dim_base = baseimg->getDimension(); - /* Load crack image. @@ -1077,60 +1243,12 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas */ video::IImage *img_crack = m_sourcecache.getOrLoad( "crack_anylength.png", m_device); - + if(img_crack && progression >= 0) { - // Dimension of original image - core::dimension2d dim_crack - = img_crack->getDimension(); - // Count of crack stages - s32 crack_count = dim_crack.Height / dim_crack.Width; - // Limit progression - if(progression > crack_count-1) - progression = crack_count-1; - // Dimension of a single crack stage - core::dimension2d dim_crack_cropped( - dim_crack.Width, - dim_crack.Width - ); - // Create cropped and scaled crack images - video::IImage *img_crack_cropped = driver->createImage( - video::ECF_A8R8G8B8, dim_crack_cropped); - video::IImage *img_crack_scaled = driver->createImage( - video::ECF_A8R8G8B8, dim_base); - - if(img_crack_cropped && img_crack_scaled) - { - // Crop crack image - v2s32 pos_crack(0, progression*dim_crack.Width); - img_crack->copyTo(img_crack_cropped, - v2s32(0,0), - core::rect(pos_crack, dim_crack_cropped)); - // Scale crack image by copying - img_crack_cropped->copyToScaling(img_crack_scaled); - // Copy or overlay crack image - if(use_overlay) - { - overlay(baseimg, img_crack_scaled); - } - else - { - /*img_crack_scaled->copyToWithAlpha( - baseimg, - v2s32(0,0), - core::rect(v2s32(0,0), dim_base), - video::SColor(255,255,255,255));*/ - blit_with_alpha(img_crack_scaled, baseimg, - v2s32(0,0), v2s32(0,0), dim_base); - } - } - - if(img_crack_scaled) - img_crack_scaled->drop(); - - if(img_crack_cropped) - img_crack_cropped->drop(); - + draw_crack(img_crack, baseimg, + use_overlay, frame_count, + progression, driver); img_crack->drop(); } } @@ -1202,7 +1320,7 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas "[noalpha" Make image completely opaque. Used for the leaves texture when in old leaves mode, so - that the transparent parts don't look completely black + that the transparent parts don't look completely black when simple alpha channel is used for rendering. */ else if(part_of_name.substr(0,8) == "[noalpha") @@ -1216,7 +1334,7 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas } core::dimension2d dim = baseimg->getDimension(); - + // Set alpha to full for(u32 y=0; y dim = baseimg->getDimension(); - + /*video::IImage *oldbaseimg = baseimg; baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); oldbaseimg->copyTo(baseimg); @@ -1340,7 +1458,16 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas video::IImage *img_right = generateImageFromScratch(imagename_right); assert(img_top && img_left && img_right); +#ifdef __ANDROID__ + assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height)); + assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width)); + + assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height)); + assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width)); + assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height)); + assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width)); +#endif // Create textures from images video::ITexture *texture_top = driver->addTexture( (imagename_top + "__temp__").c_str(), img_top); @@ -1354,7 +1481,7 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas img_top->drop(); img_left->drop(); img_right->drop(); - + /* Draw a cube mesh into a render target texture */ @@ -1384,9 +1511,9 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas params.light_position.set(10, 100, -50); params.light_color.set(1.0, 0.5, 0.5, 0.5); params.light_radius = 1000; - + video::ITexture *rtt = generateTextureFromMesh(params); - + // Drop mesh cube->drop(); @@ -1394,7 +1521,7 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas driver->removeTexture(texture_top); driver->removeTexture(texture_left); driver->removeTexture(texture_right); - + if(rtt == NULL) { baseimg = generateImageFromScratch(imagename_top); @@ -1469,7 +1596,7 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas <<"\", cancelling."<getDimension(); frame_size.Y /= frame_count; @@ -1506,37 +1633,6 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas return true; } -void overlay(video::IImage *image, video::IImage *overlay) -{ - /* - Copy overlay to image, taking alpha into account. - Where image is transparent, don't copy from overlay. - Images sizes must be identical. - */ - if(image == NULL || overlay == NULL) - return; - - core::dimension2d dim = image->getDimension(); - core::dimension2d dim_overlay = overlay->getDimension(); - assert(dim == dim_overlay); - - for(u32 y=0; ygetPixel(x,y); - video::SColor c2 = overlay->getPixel(x,y); - u32 a1 = c1.getAlpha(); - u32 a2 = c2.getAlpha(); - if(a1 == 255 && a2 != 0) - { - c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255); - c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255); - c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255); - } - image->setPixel(x,y,c1); - } -} - /* Draw an image on top of an another one, using the alpha channel of the source image @@ -1561,11 +1657,105 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst, } } +/* + Draw an image on top of an another one, using the alpha channel of the + source image; only modify fully opaque pixels in destinaion +*/ +static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size) +{ + for(u32 y0=0; y0getPixel(src_x, src_y); + video::SColor dst_c = dst->getPixel(dst_x, dst_y); + if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) + { + dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + dst->setPixel(dst_x, dst_y, dst_c); + } + } +} + +static void draw_crack(video::IImage *crack, video::IImage *dst, + bool use_overlay, s32 frame_count, s32 progression, + video::IVideoDriver *driver) +{ + // Dimension of destination image + core::dimension2d dim_dst = dst->getDimension(); + // Dimension of original image + core::dimension2d dim_crack = crack->getDimension(); + // Count of crack stages + s32 crack_count = dim_crack.Height / dim_crack.Width; + // Limit frame_count + if(frame_count > (s32) dim_dst.Height) + frame_count = dim_dst.Height; + if(frame_count < 1) + frame_count = 1; + // Limit progression + if(progression > crack_count-1) + progression = crack_count-1; + // Dimension of a single crack stage + core::dimension2d dim_crack_cropped( + dim_crack.Width, + dim_crack.Width + ); + // Dimension of the scaled crack stage, + // which is the same as the dimension of a single destination frame + core::dimension2d dim_crack_scaled( + dim_dst.Width, + dim_dst.Height / frame_count + ); + // Create cropped and scaled crack images + video::IImage *crack_cropped = driver->createImage( + video::ECF_A8R8G8B8, dim_crack_cropped); + video::IImage *crack_scaled = driver->createImage( + video::ECF_A8R8G8B8, dim_crack_scaled); + + if(crack_cropped && crack_scaled) + { + // Crop crack image + v2s32 pos_crack(0, progression*dim_crack.Width); + crack->copyTo(crack_cropped, + v2s32(0,0), + core::rect(pos_crack, dim_crack_cropped)); + // Scale crack image by copying + crack_cropped->copyToScaling(crack_scaled); + // Copy or overlay crack image onto each frame + for(s32 i = 0; i < frame_count; ++i) + { + v2s32 dst_pos(0, dim_crack_scaled.Height * i); + if(use_overlay) + { + blit_with_alpha_overlay(crack_scaled, dst, + v2s32(0,0), dst_pos, + dim_crack_scaled); + } + else + { + blit_with_alpha(crack_scaled, dst, + v2s32(0,0), dst_pos, + dim_crack_scaled); + } + } + } + + if(crack_scaled) + crack_scaled->drop(); + + if(crack_cropped) + crack_cropped->drop(); +} + void brighten(video::IImage *image) { if(image == NULL) return; - + core::dimension2d dim = image->getDimension(); for(u32 y=0; y srcdim = src->getDimension(); core::dimension2d dstdim = dst->getDimension();