Jump to content

WidgetButton with runtime generated image?


photo

Recommended Posts

Hi, I want to create a GUI where available point of views are presented each in a button. The button image has to be generated at runtime, but only on request (not every frame!).

The number of viewpoints is not fixed (depends on the scene). Ideally, the buttons should be round (mask?)

I tried to use this kind of code, but the texture don't show up (the button is invisible, but still clickable).

for (auto p: players) {
	ViewportPtr viewport = Viewport::create();
	viewport->setSkipFlags(Viewport::SKIP_VELOCITY_BUFFER | Viewport::SKIP_VISUALIZER);
	viewport->setRenderMode(Viewport::RENDER_DEPTH_GBUFFER_FINAL);
	viewport->setNodeLightUsage(Viewport::USAGE_AUX_LIGHT);

	TexturePtr texture = Texture::create();
	texture->create2D(128, 128, Unigine::Texture::FORMAT_RGBA8, Texture::SAMPLER_FILTER_LINEAR | Texture::SAMPLER_ANISOTROPY_16 | Texture::FORMAT_USAGE_RENDER);

	int streaming_mode = Render::getStreamingMode();
	Render::setStreamingMode(Render::STREAMING_FORCE); // force load texture
	viewport->renderTexture2D(p->getCamera(),texture);
	Render::setStreamingMode(streaming_mode); // restore streaming mode
			
	ImagePtr image = Image::create();
	texture->getImage(image);

	String filename = String::format("__%s.png", p->getName()); // unique name per camera
	image->save(filename); // the image on disk is correct!

	FileSystem::addVirtualFile(filename); // Not sure if necessary?
  
  	auto button = WidgetButton::create(gui, p->getName());
	button->setTexture(String("asset://")+filename); // ideally, I'd rather set directly the texture instead of saving a file
    // the button is here, but invisible? (displayed correctly if commented)
			
	onScreenCameras->addChild(button, Gui::ALIGN_EXPAND);
}

How should I do that? What am I doing wrong?

Link to comment

Hello.

For round buttons I would recommend using WidgetSprite. This widget allows you to set up a mask for finding intersections. This mask is converted to R8 format. We can also use this mask to create a round sprite.

grid_box.deleteLater();

grid_box = WidgetGridBox::create();
WindowManager::getMainWindow()->addChild(grid_box, Gui::ALIGN_CENTER);

ImagePtr mask = Image::create("button_mask.png");
mask->convertToFormat(Image::FORMAT_R8);
mask->resize(128, 128);

for (auto p : players)
{
	ViewportPtr viewport = Viewport::create();
	viewport->setSkipFlags(Viewport::SKIP_VELOCITY_BUFFER | Viewport::SKIP_VISUALIZER);
	viewport->setRenderMode(Viewport::RENDER_DEPTH_GBUFFER_FINAL);
	viewport->setNodeLightUsage(Viewport::USAGE_AUX_LIGHT);

	TexturePtr texture = Texture::create();
	texture->create2D(128, 128, Unigine::Texture::FORMAT_RGBA8, Texture::SAMPLER_FILTER_LINEAR | Texture::SAMPLER_ANISOTROPY_16 | Texture::FORMAT_USAGE_RENDER);

	int streaming_mode = Render::getStreamingMode();
	Render::setStreamingMode(Render::STREAMING_FORCE); // force load texture
	viewport->renderTexture2D(p->getCamera(), texture);
	Render::setStreamingMode(streaming_mode); // restore streaming mode

	ImagePtr image = Image::create();
	texture->getImage(image);

	// apply mask
	int width = image->getWidth();
	int height = image->getHeight();
	for (int i = 0; i < width; i++)
	{
		for (int j = 0; j < height; j++)
		{
			Image::Pixel image_pixel = image->get2D(i, j);
			Image::Pixel mask_pixel = mask->get2D(i, j);

			image_pixel.i.a = mask_pixel.i.r;
			image->set2D(i, j, image_pixel);
		}
	}

	WidgetSpritePtr sprite = WidgetSprite::create();
	sprite->setImage(image);
	sprite->addCallback(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::on_click));

	// set intersection mask
	sprite->setIntersectionImageEnabled(true);
	sprite->setIntersectionImage(mask);

	grid_box->addChild(sprite);
}

Here's what the result look like:

image.png

button_mask.png

  • Like 1
Link to comment

Oooh I see, so WidgetButton is not necessary. So, last bit missing would be the mouse-hover effect. My Idea would have been to overlay a bright circle around the image. But I can't find a way to overlay something in a grid. Any idea?

Link to comment

You can use multiple layers in the WidgetSprite for this. Place the background on the zero layer, and the button sprite on the first one. Then you need to turn the backroung on and off on mouse callbacks. For the test I just used the mask as a background.

WidgetSpritePtr sprite = WidgetSprite::create();
sprite->addLayer();
sprite->setLayerEnabled(0, false);

sprite->setLayerImage(0, mask);
sprite->setLayerImage(1, image);

sprite->addCallback(Gui::ENTER, MakeCallback(on_enter));
sprite->addCallback(Gui::LEAVE, MakeCallback(on_leave));


void on_enter(WidgetPtr w)
{
	WidgetSpritePtr sprite = checked_ptr_cast<WidgetSprite>(w);
	if (sprite == nullptr)
		return;

	sprite->setLayerEnabled(0, true);
}

void on_leave(WidgetPtr w)
{
	WidgetSpritePtr sprite = checked_ptr_cast<WidgetSprite>(w);
	if (sprite == nullptr)
		return;

	sprite->setLayerEnabled(0, false);
}

buttons.gif

  • Thanks 1
Link to comment
×
×
  • Create New...