bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
33.44k stars 3.26k forks source link

Support load .svg images #1139

Open wyhaya opened 3 years ago

wyhaya commented 3 years ago

What problem does this solve or what need does it fill?

Allow bevy to load svg vector images

Describe the solution would you like?

Using resvg crate, bevy can easily support svg images

Describe the alternative(s) you've considered?

None

Additional context

I added a SvgTextureLoader and then rendered the image using resvg and now it works fine.

let texture_handle = asset_server.load("branding/bevy_logo_light.svg");
Screen Shot

One troublesome question, since svg is vector, how do we determine the size of the image?

Before rendering the svg we have to determine the size

pub enum FitTo {
    /// Keep original size.
    Original,
    /// Scale to width.
    Width(u32),
    /// Scale to height.
    Height(u32),
    /// Zoom by factor.
    Zoom(f32),
}

Maybe we can?

// Default
asset_server.load("branding/bevy_logo_light.svg");
// Scale
asset_server.load("branding/bevy_logo_light.svg?10.0");
asset_server.load("branding/bevy_logo_light.svg#width=1000.");

Bad scenario: Re-render at each zoom?

alice-i-cecile commented 3 years ago

WRT the re-render at each zoom problem, perhaps we could build off of mip-maps (#1685) to pre-cache the SVG rendered at different scales.

If we had that, it feels like there are three possibly sensible options:

  1. Use the nearest size.
  2. Recompute if no exact match is found; likely caching the result.
  3. Quickly interpolate between nearby sizes.

I think 2 is my preferred solution in most cases.

inodentry commented 3 years ago

Just chiming in to say that this is not really what mipmaps are for. (the use case of using vector graphics like SVG in UI)

Mipmaps are for the GPU hardware to be able to automatically sample a texture at an appropriate resolution when UV-mapping onto 3d geometry (or for raster 2d sprites, when scaling down). Note that you don't really have much choice and control over which size to use when, as mipmaps are a GPU hardware feature and handled automatically. And your mipmap levels/sizes must strictly have specific sizes as expected by the GPU (1/2, 1/4, 1/8, etc., of the base texture).

Your (1) and (3) are things that the GPU hardware does automatically (you should try out the texture_filtering example from the mipmaps PR, which illustrates this ("linear with nearest mipmap" and "linear with linear mipmaps" (interpolation)). Choosing which one you want is just a parameter on the texture sampler. The GPU implements this functionality in hardware.

However, this wouldn't produce the best-looking result for using vector graphics like SVG in UI. Instead, you want to display it rendered natively at the correct size. In that sense, I agree with your (2) point, I just want to point out that it has nothing to do with mipmaps.

On the other hand, if you want to use the SVG to create a texture to be used in your scene / on a mesh, you should totally generate a mipmapped texture with the maximum size (base level) set to something reasonable of your liking. In that case, you should use mipmaps, and probably set the mipmap filtering to LINEAR (or in a 2d game, maybe NEAREST might work better than LINEAR, but anyway, those are just sampler settings).

Really, these are two different use cases: using a SVG in UI and using a SVG for textures in a game. So they call for different implementations. The former should be rendered natively at the correct size, similar to how we handle text. The latter makes sense as a mipmapped texture.

alice-i-cecile commented 3 years ago

Thanks @jamadazi; this is very useful expertise :) I agree with your conclusions here.

blaind commented 2 years ago

https://github.com/RazrFalcon/resvg is licensed with MPL-2.0, is it okay to incorporate with bevy or should it be separate MPL-2.0 licensed crate?

There needs also to be a way to supply parameters (at least render width, height) for per-resource rendering.

Maybe the SvgAssetLoader could also have (default/user-configurable) presets for handling?

// single asset params
asset_server.load("branding/bevy_logo_light.svg#width=1024,height=512");

// preset
asset_server.load("branding/bevy_logo_light.svg#svg_preset=my_custom_config");
mockersf commented 2 years ago

It's best to avoid MPL-2.0, you can find the list of licences accepted in Bevy here: https://github.com/bevyengine/bevy/blob/ba2916c45a6dcf23f7c4007e7ef2a6b0df393433/deny.toml#L16-L25

bjorn3 commented 2 years ago

Relicensing wgpu from MPL was important as it needs to be patched to run on certain consoles. An svg renderer doesn't need to be patched in any way that needs to remain behind an NDA, so I don't see much of a problem with using MPL here. It is weak and not strong copyleft, so you only need to publish any changes to resvg, not any other dependency or the game itself.

mockersf commented 2 years ago

MPL-2.0 also requires access to the source code of the parts licensed with it, so it would mean that all Bevy games will need to provide a link to the source code of resvg

bjorn3 commented 2 years ago

The MIT and Apache-2.0 license require attribution notices to be preserved afaik. You could see a link to source code as just another kind of attrbution notice, so it doesn't really add any more obligations on the licensee (the creator of the game) provided that the source code of the MPL licensed dependencies is unchanged.

WhatzGames commented 1 year ago

Maybe working with Lyon could be another option?

Seems to be fairly solid solid, but it also is considering switching from it's MIT/Apache 2.0 Model to MPL-2.0 aswell. But it is not decided as of today (2022-08-01)

TheBlckbird commented 2 months ago

As of 04/12/2024 Lyon did not switch to MPL-2.0. It is still using MIT/Apache 2.0

torsteingrindvik commented 5 days ago

If someone else stumbles into this issue just looking for a quick way to use e.g. web icons, here's a PNG workaround assuming linux and inkscape is installed:

  1. Copy the SVG, example hourglass icon
  2. Pipe it into inkscape and output:
# You can pipe it directly from clipboard but for GitHub comments this is more readable
FOO='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 32C0 14.3 14.3 0 32 0H64 320h32c17.7 0 32 14.3 32 32s-14.3 32-32 32V75c0 42.4-16.9 83.1-46.9 113.1L237.3 256l67.9 67.9c30 30 46.9 70.7 46.9 113.1v11c17.7 0 32 14.3 32 32s-14.3 32-32 32H320 64 32c-17.7 0-32-14.3-32-32s14.3-32 32-32V437c0-42.4 16.9-83.1 46.9-113.1L146.7 256 78.9 188.1C48.9 158.1 32 117.4 32 75V64C14.3 64 0 49.7 0 32zM96 64V75c0 25.5 10.1 49.9 28.1 67.9L192 210.7l67.9-67.9c18-18 28.1-42.4 28.1-67.9V64H96zm0 384H288V437c0-25.5-10.1-49.9-28.1-67.9L192 301.3l-67.9 67.9c-18 18-28.1 42.4-28.1 67.9v11z"/></svg>'

# Convert
echo $FOO | inkscape --pipe -o foo.png

But note that e.g. fontawesome has the attribution in the SVG which is lost on conversion so the attribution should be placed elsewhere.