Loading an embed SpriteFont

Hi,

I currently working on something like an engine and I would like to embed a sprite font like we can do with images, shaders, etc… and open it with a method like this

var stream = GetAssembly().GetManifestResourceStream("blablabla")

The main problem is that we can’t create an instance of SpriteFont. So I have two questions about that

  1. Is it possible to “virtually” add an xnb during the runtime into the content manager (not into the folder, directly into the content manager, with a byte array or something else)
  2. Or is it possible to inherit from SpriteFont, creating a custom constructor and providing the necessary resources to construct an object usable with an instance of spriteBatch ?

Thank for your help :wink:

1.Is it possible to “virtually” add an xnb during the runtime
into the content manager

Not really… this is an extension we could discuss, but it isn’t in there now.

2.Or is it possible to inherit from SpriteFont, creating a
custom constructor

Not how it is implemented now. Its constructor would have to be changed to be at least protected.

What you can do right now is created a derived class from ContentManager and overload the ContentManager.OpenStream method. This would allow you to supply it with a Stream that was opened from the resources.

So for example you can handle a special string like RES://This/Is/My/Resource to open from the assembly resources.

Hum… I understand,

The second solution can work but is not interessting but the first solution could be usefull. Would you accept a PR which add support for loading some resources from a stream?

var fontSteam = new Stream(blablabla);
Content.LoadFromStream<SpriteFont>(fontStream);

There are a bunch of complications with that.

Mainly we have 2 platforms that require reloading of assets (WP8 and Android). How would they reload assets that were loaded from streams?

Also how would you ask for this same resource a second time? Pass it a stream again? So you get two loaded copies and not the same copy like every other ContentManager element?

I understand, it’s not easy… Using the content manager is maybe not a good idea, the best could be to have an extension, written by myself, which load the asset. With this it’s my own responsibility to load/reload the asset, the content manager is not affected. But as you said the current design of SpriteFont is not made to do that.

Have you had other demands like that ? If I’m the only one who want to load a spriteFont from a stream, we can forget this post, however, if this type of feature can interest other people then it can be interesting to change a little the design (a protected constructor ? a static method like SpriteFont.New). What are your opinion about that ?

Embedding a SpriteFont is really useful, for an editor and an engine or framework but I know that it’s clearly not necessary for a game and the main target for MonoGame is the game.

Not that I can remember, but I understand the need.

Or some sort of SpriteFont.FromStream() or something like that best matches the behavior in other APIs. The annoying complication here is that there is no way to generate a SpriteFont outside of an XNB. So this would be the only FromStream method that requires an XNB and needs to go thru the complex XNB decoding process.

What if instead we officially support a prefix like RES in the official ContentManager? This would allow you to load anything as an embedded resource. So something like:

var myTexture = content.Load<Texture2D>("RES://Some.Texture.Resource");

Note it would have to be a texture XNB and not just a PNG or anything.

@KonajuGames - Thoughts?

In my opinion if it’s not too difficult and too risky (the code need to stay clean) this is usefull for all type of Framework and engine.

Actually I already have an helper class to deal with the resources folder to load textures and effects (note that this code is backported from an “old” project, I must change the extensions of shaders files from mgfxo to xnb ;))

With this addition anyone can embed resources easily ! Is it complex to support this feature ? I can maybe/surely help ?

The compiled effects generated from 2MGFX are not XNB files. If they were XNB files, we would have named them that. Renaming it to XNB will not do anything useful.

I have recently been playing with a fully managed TrueType rasterizer that could be used at runtime to generate a SpriteFont from a TrueType file. Tom pointed me to this thread from the discussions we were having about this rasterizer. I’m also planning a DynamicSpriteFont that would rasterize glyphs on demand at runtime. I have used similar dynamic fonts in previous games to support Asian languages and their thousands of glyphs.

@Tom ok
@KonajuGames So this kind of feature can be useful for you too right ?

So, I found a bit of a workaround for loading graphical content via resources, at least on Windows.
The idea is to implement a simple IServiceProvider and IGraphicsDeviceService that can be created from a graphics device, like so:

internal class SimpleServiceProvider : IServiceProvider
{
    GraphicsDeviceService _graphicsDevice;

    public SimpleServiceProvider(GraphicsDevice graphics)
    {
        _graphicsDevice = new GraphicsDeviceService(graphics);
    }

   public object GetService(Type serviceType)
    {
        if (serviceType == typeof(IGraphicsDeviceService))
            return _graphicsDevice;
        return null;
    }
}

internal class GraphicsDeviceService : IGraphicsDeviceService
{
    GraphicsDevice _device;

    public GraphicsDeviceService(GraphicsDevice device)
    {
        _device = device;
        _device.DeviceReset += DeviceReset;
        _device.DeviceResetting += DeviceResetting;
       _device.Disposing += DeviceDisposing;
    }

    public event EventHandler<EventArgs> DeviceCreated;
    public event EventHandler<EventArgs> DeviceDisposing;
    public event EventHandler<EventArgs> DeviceReset;
    public event EventHandler<EventArgs> DeviceResetting;

    public GraphicsDevice GraphicsDevice
    {
        get { return _device; }
    }
}

Using this setup, you can (assuming you have some sort of handle to your graphics device), load content via the ResourceContentManager, like so:

SimpleServiceProvider service = new SimpleServiceProvider(graphics);
ResourceContentManager manager = new ResourceContentManager(service, Resources.ResourceManager);
SpriteFont font = manager.Load<SpriteFont>("BasicFont");

Cheers!

1 Like

Thank you ! this sound good for me and I’ll test it quickly !