X3dv background with sprite

I took my simple Demo Sprite application for Lazarus that I shared on GitHub and modified it by loading the background as x3dv (as in Dragon_spine) instead of TGLImage (so that it resizes with the window).
Then I loaded in the same way a sprite cut from the background that has the same size as the background but is transparent in all the rest and placing it in the position x = 0 and y = 0 the sprite overlaps precisely on the background image.
In this way in the Dragon_spine demo changing the Z axis of the sprite (x3dv) it stays in front of the dragon.
But now I have a sprite instead of the dragon and I don’t have the Z axis to place this player between the background image and the image that contains the sprite cut from the background (both x3dv).
It is also not possible to add the player to the Viewport as an item.

Maybe the two approaches cannot be mixed?
What could be an alternative solution?

You can place as many TCastleScene within a viewport as you’d like, and you can use their Z (and call SortBackToFront2D as mentioned in other thread) to make them relative to each other.

But now I have a sprite instead of the dragon

Do you mean the TSprite class? If yes, then the answer is:

Indeed, you cannot place TSprite inside the Viewport.Items. Two solutions:

  1. Best: convert sprite sheet into X3D, which you can then load as TCastleScene. See tools/sprite-sheet-to-x3d/ . See also https://github.com/castle-engine/castle-engine/wiki/2D-Games – basically, it is better to use TCastleScene for everything.

    I’m open to extending this approach / making it more comfortable in the future, I already plan this actually.

  2. You can also have layers, by creating multiple TCastleViewport instances with the same position/size. The viewport may have Transparent = true to make things behind it visible (which may be other viewports, or any other UI controls, e.g. your own TCastleUserInterface that draws TSprite, https://castle-engine.io/manual_2d_ui_custom_drawn.php ). So if you can organize your world in layers, e.g. “stuff behind player”, player, “stuff in front of player” than this is a workable solution.

Thank you for your support.
I looked at the sprite-sheet-to-x3d tool but from the code it seems to support only .xml and .plist as input even if there is a .png sheet in the samples folder.
Do I need to create an .xml file of my png spritesheet?

sprite-sheet-to-x3d was designed to convert starling and cocos sprite formats, basically it is a file (either .plist or .xml) alongside with your image, which contain necessary information about your sprites (the size of each frame, as well as anchors, and list of animation contain these frames). This way you can pack multiple sprites with different sizes into a single image to save space.

In your case there’s 2 options:

  • Generate a starling (.xml) or cocos (.plist) file from your spritesheet. Personally I use ShoeBox to generate such file.
  • Extend the tool to accept raw spritesheet. You will need to implement a new PNGParser, which translate your png information (sprite size, number of frames) into a TFrameList and feed it to the tool so that it can convert your sprite to x3d file.

Edit: Another option is to implement a completely new TSprite class that make use of X3D features instead of TGLImage and friends. I did that some time ago, although it never make it way to become CGE’s example. I left the whole programming stuff in 2 years and only notice Michalis message 1 year later, which I already lost the source code by that time.

2 Likes

Some time ago I published an application for Lazarus on GitHub that used the combine_images_into_sprite_sheet sample application. One idea could be to implement the function of creating an xml file.
Thanks

1 Like

All good ideas. My general plans for the future, about sprite sheets:

  1. it should be easy to use sprite sheets inside TCastleScene

  2. in particular, it should be easy to set it up using CGE editor.

And we almost have this :slight_smile: Thanks to sprite-sheet-to-x3d by @kagamma .

I have in my TODO to one day move sprite-sheet-to-x3d logic to a regular “loading routine” inside X3DLoad unit, so that doing LoadNode('xxx.plist') or TCastleScene.URL := 'xxx.plist' would automatically load a sprite sheet.

The XML variant of the sprite sheet could be detected by requiring less “generic” extension, e.g. we would require .xml-sprite-sheet instead of .xml.

This way sprite-sheet-to-x3d would no longer be needed. If someone would really need explicit conversion, then https://castle-engine.io/view3dscene.php already comes with tovrmlx3d tool that does it. But it is no longer necessary to do this explicitly to use sprite sheets. And then you can load sprite sheets into TCastleScene easily, from code or from CGE editor.

To support eventual custom options:

  • ideally, they should not be needed, as .plist / .xml-sprite-sheet should contain all information.

  • but we can also use “anchors” in a similar way how Spine loading uses anchors to specify atlases. E.g. to specify how make sprite frames per second are played, you could load castle-data:/xxx.plist#fps:10 instead of castle-data:/xxx.plist.

Hi Michalis,
I have a strange issue with combine_image_into_sprite_sheet.
Let me explain: I downloaded my application from GitHub which also includes the pre-compiled version.
If I use it, the creation of the spritesheet is successful without any errors (I believe I compiled it with an older version of CGE).
If I load the code into Lazarus and compile it again (6.5) I get this error:
Error when downloading “[path]\mysprite_00.png”.
…press Continue
Error when downloading “[path]\mysprite_15.png”.
…press Continue
Finally it creates spritesheet correctly

Images are 14, if I change their names with extension between _00 and _ 013 it only raises the error of image_014 not found but pressing Continue the operation is again successful.

According to the API it shouldn’t look for the first file with index 1?
If it contains @counter(*) macro, then we try to load image sequence starting from counter 1.
My images start at _01 and @counter is set to 2.

Do you have any idea?

this is the line that raise error:
InputVideo.LoadFromFile(InputUrl);

This could be a way to create the .xml file at the same time as the spritesheet (if the file structure must be the same as the one I found in the sprite-sheet-to-x3d tool).
Under Lazarus it works correctly (even if with the errors described in the previous post) but from the command line I could not try it because it returns the same error as Lazarus but it is not possible to proceed.

program combine_images_into_sprite_sheet;

uses [...]  { added  } Classes { end }

var
  [...]
  { added}
  XMLFileList: TStringList;
  XMLFileName: String;
  { end }
begin
  [...]
  try
    { added}
    XMLFileName := ExtractFileName(OutputUrl);
    XMLFileList := TStringList.Create;
    XMLFileList.Add('<?xml version="1.0" encoding="UTF-8"?>');
    XMLFileList.Add('<TextureAtlas imagePath="' + XMLFileName + '">');
    { end }
    [...]
    for I := 0 to InputVideo.Count - 1 do
    begin
      X := (I mod Columns) * InputVideo.Width;
      Y := OutputImage.Height - (I div Columns + 1) * InputVideo.Height;
      InputVideo.Items[I].DrawTo(OutputImage, X, Y, dmOverwrite);
      { added}
      XMLFileList.add('<SubTexture name="' + ExtractFileName(InputVideo.Items[I].URL) +
                      '" x="' + X.ToString +
                      '" y="' + Y.ToString +
                      '" width="' + InputVideo.Items[I].Width.ToString +
                      '" height="' + InputVideo.Items[I].Height.ToString + '"/>');
      { end }
    end;
    { added }
    XMLFileList.Add('</TextureAtlas>');
    XMLFileList.SaveToFile(ExtractFilePath(OutputUrl) + ChangeFileExt(XMLFileName, '') + '.xml');
    { end }
    SaveImage(OutputImage, OutputUrl);
  finally
    [...]
    { added}
    if Assigned (XMLFileList) then
      FreeAndNil(XMLFileList);
    { end }
  end;
end.

[edit]
I converted the sprite with sprite-sheet-to-x3d and I loaded it as TCastleScene but frames are reversed. They start from the first bottom left instead of the first top left of the spritesheet. As soon as I can I’ll correct the code.

It’s because you run inside Lazarus debugger (user would not see these failures). Indeed we try to load from index 0 (in case your image sequence starts from 0, not 1). If the loading failed with exception, the exception is caught and we continue.

So this is all normal. We detect “missing image” by just trying to open it, and catching exception. In general I try to avoid this pattern (raising exception when actually nothing alarming happened), but in this case this seemed an easiest implementation (made a TODO, we can improve it using URIExists now).

So this is all just a bug in the documentation, that should say “your image sequence can start from 0 or from 1”. Fixed, it now says:

          We use @link(FormatNameCounter) to recognize URLs with
          @code(@@counter(*)) macro. If URL contains @code(@@counter(*)) macro,
          then we try to load image sequence starting from counter equal 0
          or (if that doesn't exist) from counter equal 1.
          This way your image sequence numbering can start from 0 or from 1,
          and it will work in any case.

          If URL doesn't contain @code(@@counter(*)) macro,
          then we just load a single image (and treat it as a movie with only one frame).

This could be a way to create the .xml file at the same time as the spritesheet (if the file structure must be the same as the one I found in the sprite-sheet-to-x3d tool).

Could you submit a pull request? I would be happy to incorporate this in CGE examples/sprite_sheets/combine_images_into_sprite_sheet/combine_images_into_sprite_sheet.lpr code. (if you never done “pull request” before, Internet has a number of guides how to do this, GitHub docs are on https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/creating-an-issue-or-pull-request ).

I’d gladly do this if I don’t have the same command line problem and can’t test it.
I am sure that with the previous configuration (Lazarus + CGE) it did not raise any errors even under the IDE.

To make things as easy as possible I moved the images to the same folder as combine_images_into_sprite_sheet.

I use from the Windows Power Shell started as administrator
.\combine_images_into_sprite_sheet.exe [email protected](2).png SpriteSheet.png 5

The images are named from James_01 to James_30

I get the error:

An unhandled exception occurred at …:
EDownloadError: Error when downloading “James_@counter”: Exception “EFOpenError”:
Unable to open file “James_@counter

Put [email protected](2).png is double quotes, like "[email protected](2).png"

I’m not sure about Windows Power Shell quoting rules, but at least standard Unix shells would need it, otherwise the parenthesis ( is a special character.

1 Like