#include "tile.h"
+#include <algorithm>
#include <ICameraSceneNode.h>
#include "util/string.h"
#include "util/container.h"
// 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);
-
video::ITexture* getNormalTexture(const std::string &name);
video::SColor getTextureAverageColor(const std::string &name);
video::ITexture *getShaderFlagsTexture(bool normamap_present);
}
}
-video::ITexture* TextureSource::generateTextureFromMesh(
- const TextureFromMeshParams ¶ms)
+inline static void applyShadeFactor(video::SColor &color, u32 factor)
{
- video::IVideoDriver *driver = RenderingEngine::get_video_driver();
- sanity_check(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 = RenderingEngine::get_raw_device()->getSceneManager();
- sanity_check(smgr_main);
- scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
- sanity_check(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<u32> screen = driver->getScreenSize();
-
- // Render scene
- driver->beginScene(true, true, video::SColor(0,0,0,0));
- driver->clearZBuffer();
- smgr->drawAll();
-
- core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
-
- irr::video::IImage* rawImage =
- driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
-
- u8* pixels = static_cast<u8*>(rawImage->lock());
- if (!pixels)
- {
- rawImage->drop();
- return NULL;
- }
-
- core::rect<s32> 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();
+ u32 f = core::clamp<u32>(factor, 0, 256);
+ color.setRed(color.getRed() * f / 256);
+ color.setGreen(color.getGreen() * f / 256);
+ color.setBlue(color.getBlue() * f / 256);
+}
- guiScalingCache(io::path(params.rtt_texture_name.c_str()), driver, inventory_image);
+static video::IImage *createInventoryCubeImage(
+ video::IImage *top, video::IImage *left, video::IImage *right)
+{
+ core::dimension2du size_top = top->getDimension();
+ core::dimension2du size_left = left->getDimension();
+ core::dimension2du size_right = right->getDimension();
+
+ u32 size = npot2(std::max({
+ size_top.Width, size_top.Height,
+ size_left.Width, size_left.Height,
+ size_right.Width, size_right.Height,
+ }));
+
+ // It must be divisible by 4, to let everything work correctly.
+ // But it is a power of 2, so being at least 4 is the same.
+ // And the resulting texture should't be too large as well.
+ size = core::clamp<u32>(size, 4, 64);
+
+ // With such parameters, the cube fits exactly, touching each image line
+ // from `0` to `cube_size - 1`. (Note that division is exact here).
+ u32 cube_size = 9 * size;
+ u32 offset = size / 2;
- video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
- inventory_image->drop();
+ video::IVideoDriver *driver = RenderingEngine::get_video_driver();
- if (rtt == NULL) {
- errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl;
- return NULL;
+ auto lock_image = [size, driver] (video::IImage *&image) -> const u32 * {
+ image->grab();
+ core::dimension2du dim = image->getDimension();
+ video::ECOLOR_FORMAT format = image->getColorFormat();
+ if (dim.Width != size || dim.Height != size || format != video::ECF_A8R8G8B8) {
+ video::IImage *scaled = driver->createImage(video::ECF_A8R8G8B8, {size, size});
+ image->copyToScaling(scaled);
+ image->drop();
+ image = scaled;
}
+ sanity_check(image->getPitch() == 4 * size);
+ return reinterpret_cast<u32 *>(image->lock());
+ };
+ auto free_image = [] (video::IImage *image) -> void {
+ image->unlock();
+ image->drop();
+ };
- 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)) {
- static bool warned = false;
- if (!warned)
- {
- errorstream<<"TextureSource::generateTextureFromMesh(): "
- <<"EVDF_RENDER_TO_TARGET not supported."<<std::endl;
- warned = true;
+ video::IImage *result = driver->createImage(video::ECF_A8R8G8B8, {cube_size, cube_size});
+ sanity_check(result->getPitch() == 4 * cube_size);
+ result->fill(video::SColor(0x00000000u));
+ u32 *target = reinterpret_cast<u32 *>(result->lock());
+
+ // Draws single cube face
+ // `shade_factor` is face brightness, in range [0.0, 1.0]
+ // (xu, xv, x1; yu, yv, y1) form coordinate transformation matrix
+ // `offsets` list pixels to be drawn for single source pixel
+ auto draw_image = [=] (video::IImage *image, float shade_factor,
+ s16 xu, s16 xv, s16 x1,
+ s16 yu, s16 yv, s16 y1,
+ std::initializer_list<v2s16> offsets) -> void {
+ u32 brightness = core::clamp<u32>(256 * shade_factor, 0, 256);
+ const u32 *source = lock_image(image);
+ for (u16 v = 0; v < size; v++) {
+ for (u16 u = 0; u < size; u++) {
+ video::SColor pixel(*source);
+ applyShadeFactor(pixel, brightness);
+ s16 x = xu * u + xv * v + x1;
+ s16 y = yu * u + yv * v + y1;
+ for (const auto &off : offsets)
+ target[(y + off.Y) * cube_size + (x + off.X) + offset] = pixel.color;
+ source++;
+ }
}
- return NULL;
- }
-
- // Create render target texture
- video::ITexture *rtt = driver->addRenderTargetTexture(
- params.dim, params.rtt_texture_name.c_str(),
- video::ECF_A8R8G8B8);
- if (rtt == NULL)
- {
- errorstream<<"TextureSource::generateTextureFromMesh(): "
- <<"addRenderTargetTexture returned NULL."<<std::endl;
- return NULL;
- }
-
- // Set render target
- if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) {
- driver->removeTexture(rtt);
- errorstream<<"TextureSource::generateTextureFromMesh(): "
- <<"failed to set render target"<<std::endl;
- return NULL;
- }
+ free_image(image);
+ };
- // Get a scene manager
- scene::ISceneManager *smgr_main = RenderingEngine::get_scene_manager();
- assert(smgr_main);
- 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);
- 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);
-
- // Render scene
- driver->beginScene(true, true, video::SColor(0,0,0,0));
- smgr->drawAll();
- driver->endScene();
-
- // Drop scene manager
- smgr->drop();
-
- // Unset render target
- driver->setRenderTarget(0, false, true, video::SColor(0,0,0,0));
-
- if (params.delete_texture_on_shutdown)
- m_texture_trash.push_back(rtt);
-
- return rtt;
+ draw_image(top, 1.000000f,
+ 4, -4, 4 * (size - 1),
+ 2, 2, 0,
+ {
+ {2, 0}, {3, 0}, {4, 0}, {5, 0},
+ {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1},
+ {2, 2}, {3, 2}, {4, 2}, {5, 2},
+ });
+
+ draw_image(left, 0.836660f,
+ 4, 0, 0,
+ 2, 5, 2 * size,
+ {
+ {0, 0}, {1, 0},
+ {0, 1}, {1, 1}, {2, 1}, {3, 1},
+ {0, 2}, {1, 2}, {2, 2}, {3, 2},
+ {0, 3}, {1, 3}, {2, 3}, {3, 3},
+ {0, 4}, {1, 4}, {2, 4}, {3, 4},
+ {2, 5}, {3, 5},
+ });
+
+ draw_image(right, 0.670820f,
+ 4, 0, 4 * size,
+ -2, 5, 4 * size - 2,
+ {
+ {2, 0}, {3, 0},
+ {0, 1}, {1, 1}, {2, 1}, {3, 1},
+ {0, 2}, {1, 2}, {2, 2}, {3, 2},
+ {0, 3}, {1, 3}, {2, 3}, {3, 3},
+ {0, 4}, {1, 4}, {2, 4}, {3, 4},
+ {0, 5}, {1, 5},
+ });
+
+ result->unlock();
+ return result;
}
video::IImage* TextureSource::generateImage(const std::string &name)
return true;
}
-#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);
- video::ITexture *texture_left = driver->addTexture(
- (imagename_left + "__temp__").c_str(), img_left);
- video::ITexture *texture_right = driver->addTexture(
- (imagename_right + "__temp__").c_str(), img_right);
- FATAL_ERROR_IF(!(texture_top && texture_left && texture_right), "");
+ baseimg = createInventoryCubeImage(img_top, img_left, img_right);
- // Drop images
+ // Face images are not needed anymore
img_top->drop();
img_left->drop();
img_right->drop();
- /*
- Draw a cube mesh into a render target texture
- */
- scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
- setMeshColor(cube, video::SColor(255, 255, 255, 255));
- cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
- cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
- cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
- cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
- cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
- cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
-
- TextureFromMeshParams params;
- params.mesh = cube;
- params.dim.set(64, 64);
- params.rtt_texture_name = part_of_name + "_RTT";
- // We will delete the rtt texture ourselves
- params.delete_texture_on_shutdown = false;
- params.camera_position.set(0, 1.0, -1.5);
- params.camera_position.rotateXZBy(45);
- params.camera_lookat.set(0, 0, 0);
- // Set orthogonal projection
- params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
- 1.65, 1.65, 0, 100);
-
- params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
- 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();
-
- // Free textures
- driver->removeTexture(texture_top);
- driver->removeTexture(texture_left);
- driver->removeTexture(texture_right);
-
- if (rtt == NULL) {
- baseimg = generateImage(imagename_top);
- return true;
- }
-
- // Create image of render target
- video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim);
- FATAL_ERROR_IF(!image, "Could not create image of render target");
-
- // Cleanup texture
- driver->removeTexture(rtt);
-
- baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
-
- if (image) {
- image->copyTo(baseimg);
- image->drop();
- }
+ return true;
}
/*
[lowpart:percent:filename