Jump to content

[SOLVED] Capture final image, including GUI


photo

Recommended Posts

Posted

Hi Morbid,

 

I am sending the output of a Unigine world to a broadcast system in SDI format using a BlackMagic Decklink card, so the solution needs to be quick. The method I have of using a callback on the Renderer is good and it gives me everything in the window but it seems to skip over the gui elements. We use WidgetSprites to place images and text on the screen.

 

I had a look through the forums and it seems that the question of getting the *final pixels in the window* is something that comes up a lot but is also something that Unigine do not have a solution to - is that right?

Posted

Hi angus,

Indeed, when Viewport::CALLBACK_END callback is triggered, GUI is not rendered yet.

video_grab command makes the engine to copy frame buffer contents just before swap() and to save it on disk.
That way it gets the effective final image from the render sequence including the GUI.

You can do exactly that yourself if you implement engine main loop and add copy logic before swap() call:

while (!engine->isDone())
{
    engine->update();
    engine->render();

    TexturePtr texture = Texture::create();
    texture->create2D(App::get()->getWidth(), App::get()->getHeight(), Texture::FORMAT_RGBA8, Texture::FILTER_POINT | Texture::USAGE_RENDER);
    texture->copy2D();

    engine->swap();
}

 

Please, check out the modified Screenshot sample that demonstrates this approach:

// Copyright (C) 2005-2018, UNIGINE. All rights reserved.

#include <UnigineApp.h>
#include <UnigineLogic.h>
#include <UnigineGame.h>
#include <UnigineConsole.h>
#include <UnigineViewport.h>
#include <UnigineCallback.h>
#include <UnigineRender.h>
#include <UnigineRenderState.h>
#include <UnigineWidgets.h>
#include <UnigineImage.h>
#include <UnigineWorld.h>

using namespace Unigine;
using namespace Unigine::Math;

//////////////////////////////////////////////////////////////////////////
// World logic class
//////////////////////////////////////////////////////////////////////////

class AppWorldLogic : public WorldLogic
{
public:
    AppWorldLogic() {}
    virtual ~AppWorldLogic() {}

    int init() override
    {
        player = PlayerDummy::create();
        player->release();

        Game::get()->setPlayer(player->getPlayer());

        GuiPtr gui = Gui::get();
        sprite = WidgetSprite::create(gui);
        gui->addChild(sprite->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_BACKGROUND);
        sprite->setPosition(0, 0);

        save_screenshot_button = WidgetButton::create(gui, "Save screenshot to disk");
        gui->addChild(save_screenshot_button->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_BACKGROUND);
        save_screenshot_button->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::on_button_clicked));

        save_screenshot_dialog = WidgetDialogFile::create(gui, "Save screenshot dialog");
        save_screenshot_dialog->setPath("./");
        save_screenshot_dialog->setFilter(".png.jpeg");
        save_screenshot_dialog->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::dialog_ok_clicked));
        save_screenshot_dialog->setHidden(1);
        gui->addChild(save_screenshot_dialog->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_CENTER);

        return 1;
    }

    int shutdown() override
    {
        sprite.clear();
        save_screenshot_button.clear();
        save_screenshot_dialog.clear();

        player.clear();

        screenshot.clear();

        return 1;
    }

    int destroy() override
    {
        screenshot.clear();
        return 1;
    }

    int update() override
    {
        if (!screenshot)
        {
            screenshot = Texture::create();
            sprite->setRender(screenshot, !Render::get()->isFlipped());
        }
        
        update_screenshot_size();

        update_player();

        update_sprite_size();
        update_button_position();

        return 1;
    }

    void beforeSwap()
    {
        screenshot->copy2D();
    }

private:
    void update_screenshot_size()
    {
        App *app = App::get();
        if (screenshot->getWidth() != app->getWidth() || screenshot->getHeight() != app->getHeight())
            screenshot->create2D(app->getWidth(), app->getHeight(), Texture::FORMAT_RGBA8, Texture::FILTER_POINT | Texture::USAGE_RENDER);
    }

    void update_player()
    {
        const float CAMERA_DISTANCE = 3.0f;
        const float CAMERA_HEIGHT = 2.5f;

        float time = Game::get()->getTime();

        Vec3 position = Vec3(CAMERA_DISTANCE * Math::sin(time), -CAMERA_DISTANCE * Math::cos(time), CAMERA_HEIGHT);
        vec3 direction = vec3(Vec3::ZERO - position);
        player->setPosition(position);
        player->setDirection(direction, vec3::UP);
    }

    void update_sprite_size()
    {
        App *app = App::get();
        sprite->setWidth(app->getWidth() / 3);
        sprite->setHeight(app->getHeight() / 3);
        sprite->arrange();
    }

    void update_button_position()
    {
        save_screenshot_button->setPosition(0, sprite->getHeight());
        save_screenshot_button->arrange();
    }

    void on_button_clicked()
    {
        save_screenshot_dialog->setHidden(0);
        save_screenshot_dialog->setPermanentFocus();
    }

    void dialog_ok_clicked()
    {
        save_screenshot_dialog->setHidden(1);

        ImagePtr screenshot_image = Image::create();
        screenshot->getImage(screenshot_image);
        if (!Render::get()->isFlipped())
            screenshot_image->flipY();
        screenshot_image->convertToFormat(Image::FORMAT_RGB8);

        screenshot_image->save(save_screenshot_dialog->getFile());
        save_screenshot_dialog->removeFocus();
    }

private:
    PlayerDummyPtr player;
    WidgetSpritePtr sprite;
    WidgetButtonPtr save_screenshot_button;
    WidgetDialogFilePtr save_screenshot_dialog;

    TexturePtr screenshot;
};

//////////////////////////////////////////////////////////////////////////
// System logic class
//////////////////////////////////////////////////////////////////////////

class AppSystemLogic : public SystemLogic
{
public:
    AppSystemLogic() {}
    virtual ~AppSystemLogic() {}

    int init() override
    {
        App::get()->setUpdate(1);
        World::get()->loadWorld("screenshot");
        return 1;
    }
};

//////////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////////

int main(int argc, char **argv)
{
    // init engine
    EnginePtr engine(UNIGINE_VERSION, argc, argv);

    AppSystemLogic system_logic;
    engine->addSystemLogic(&system_logic);

    AppWorldLogic world_logic;
    engine->addWorldLogic(&world_logic);

    while (!engine->isDone())
    {
        engine->update();
        engine->render();

        world_logic.beforeSwap();

        engine->swap();
    }

    return 0;
}

 

  • Like 1
  • silent changed the title to [SOLVED] Capture final image, including GUI
×
×
  • Create New...