• Dear Cerberus X User!

    As we prepare to transition the forum ownership from Mike to Phil (TripleHead GmbH), we need your explicit consent to transfer your user data in accordance with our amended Terms and Rules in order to be compliant with data protection laws.

    Important: If you accept the amended Terms and Rules, you agree to the transfer of your user data to the future forum owner!

    Please read the new Terms and Rules below, check the box to agree, and click "Accept" to continue enjoying your Cerberus X Forum experience. The deadline for consent is April 5, 2024.

    Do not accept the amended Terms and Rules if you do not wish your personal data to be transferred to the future forum owner!

    Accepting ensures:

    - Continued access to your account with a short break for the actual transfer.

    - Retention of your data under the same terms.

    Without consent:

    - You don't have further access to your forum user account.

    - Your account and personal data will be deleted after April 5, 2024.

    - Public posts remain, but usernames indicating real identity will be anonymized. If you disagree with a fictitious name you have the option to contact us so we can find a name that is acceptable to you.

    We hope to keep you in our community and see you on the forum soon!

    All the best

    Your Cerberus X Team

bgfx module/target

@Ferdi - I think if you have implemented bgfx::setScissor you should not need to change the view mode to sequential as it will apply to the next draw primitive with the default indexing. However, having the ability to use sequential indexing is still useful and is probably a better choice for 2D drawing. Maybe you could test with the view mode set to default just to make sure.

For the clear screen it looks like as long as you set your view rectangle to the full screen, the clear should work, but of course you have two views to clear so double check that.

bgfx::setViewRect(0, xOffset, yOffset, width, height);
bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, color.asInteger(), 1.0f, 0);

bgfx::setViewRect(1, xOffset, yOffset, width, height);
bgfx::setViewClear(1, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, color.asInteger(), 1.0f, 0);

Not sure if it really matters, if DrawRect does the job, but I guess the setViewClear is more thorough. Also not sure if you are using frames because that could affect things too as you can't clear bgfx view multiple times per frame. Finally, maybe you would need to do a bgfx::submit(0) then one for view 1 directly after the call to each setViewClear?
 
@Ferdi - I think if you have implemented bgfx::setScissor you should not need to change the view mode to sequential as it will apply to the next draw primitive with the default indexing. However, having the ability to use sequential indexing is still useful and is probably a better choice for 2D drawing. Maybe you could test with the view mode set to default just to make sure.

Sorry for the late reply, dynamicimage/readpixels was tough.

I tried it without sequential and it doesn't work. It draws it all wrong. I will look into sequential more, but later I think. Once all the example is going, then I will experiment with sequential.

For the clear screen it looks like as long as you set your view rectangle to the full screen, the clear should work, but of course you have two views to clear so double check that.

bgfx::setViewRect(0, xOffset, yOffset, width, height);
bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, color.asInteger(), 1.0f, 0);

bgfx::setViewRect(1, xOffset, yOffset, width, height);
bgfx::setViewClear(1, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, color.asInteger(), 1.0f, 0);

I am not following, why do I have two views? I have only 1 view, _mojoCanvas which is view id 0. I am pretty sure I do bgfxSetViewRect, but I will double check that.

Not sure if it really matters, if DrawRect does the job, but I guess the setViewClear is more thorough. Also not sure if you are using frames because that could affect things too as you can't clear bgfx view multiple times per frame. Finally, maybe you would need to do a bgfx::submit(0) then one for view 1 directly after the call to each setViewClear?

If I can only do one clear per frame, then I will use the DrawRect method to clear screen.

FYI, I am calling bgfxFrame(false) once, in glfwgame.cpp, void BBGlfwGame::Run(). This seems to be the best place. So in GLFW it was calling glfwSwapBuffers, in bgfx now it is bgfx_frame.

Code:
    StartGame();
 
    while( !glfwWindowShouldClose( _window ) ){
 
        RenderGame();
     
        // glfwSwapBuffers( _window );
        // Advance to next frame. Rendering thread will be kicked to
        // process submitted rendering primitives.
        bgfx_frame(false);
     
        //Wait for next update
        if( _nextUpdate ){
            double delay=_nextUpdate-GetTime();
            if( delay>0 ) Sleep( delay );
        }

I am going to look into this nextish. Well I am going to get all the mojo 1 example going. Color is my biggest problem right now, it is wrong for some reason.

I managed to get mak/dynamicimage.cxs. Not fully, but the main part is done. I don't like how I implemented this. bgfx has a way to save screen shot. I used this to read pixel. So first I have to hook the screen shot callback in init

Code:
    bgfx_init_t init;
    bgfx_init_ctor(&init);

    bgfx_callback_interface_t m_callback_interface;
    bgfx_callback_vtbl_t m_callback_vtbl;
...
    m_callback_vtbl.screen_shot = bgfx_callback_screen_shot;

    m_callback_interface.vtbl = &m_callback_vtbl;
    init.callback = &m_callback_interface;

    bgfx_init(&init);

Now to read pixel I request a screen shot like this:

Code:
void * _bgfx_screen_shot_pixels;
int _bgfx_screen_shot_x;
int _bgfx_screen_shot_y;
int _bgfx_screen_shot_width;
int _bgfx_screen_shot_height;
int _bgfx_screen_shot_offset;
int _bgfx_screen_shot_pitch;
bool _bgfx_screen_shot_done;

// bgfx_callback_screen_shot is hooked in glfwgame.cpp in method void BBGlfwGame::Run() during bgfxInit
void bgfx_callback_screen_shot(bgfx_callback_interface_t* _this, const char* _filePath, uint32_t _width, uint32_t _height, uint32_t _pitch, const void* _data, uint32_t _size, bool _yflip)
{
    printf("bgfx_callback_screen_shot\n");
    printf("_width=%d _height=%d _pitch=%d _size=%d _yflip=%d\n", _width, _height, _pitch, _size, _yflip);
    printf("x=%d y=%d width=%d height=%d offset=%d _pitch=%d\n", _bgfx_screen_shot_x, _bgfx_screen_shot_y, _bgfx_screen_shot_width, _bgfx_screen_shot_height, _bgfx_screen_shot_offset, _bgfx_screen_shot_pitch);

    unsigned char * _src = (unsigned char *)_data;
    unsigned char * _dst = (unsigned char *)_bgfx_screen_shot_pixels;

    // TODO: optimize this
    for(int j=0 ; j < _bgfx_screen_shot_height ; j ++ )
    {
        for(int i=0 ; i < _bgfx_screen_shot_width ; i ++ )
        {
            int data_index = (_bgfx_screen_shot_y + j) * _pitch + (_bgfx_screen_shot_x + i) * 4;
            int pixel_index = ( (_bgfx_screen_shot_y + j) * _bgfx_screen_shot_pitch + (_bgfx_screen_shot_x + i) + _bgfx_screen_shot_offset) * 4;
            _dst[ pixel_index + 0 ] = _src[ data_index + 0 ];    // blue
            _dst[ pixel_index + 1 ] = _src[ data_index + 1 ];    // green
            _dst[ pixel_index + 2 ] = _src[ data_index + 2 ];    // red
            _dst[ pixel_index + 3 ] = _src[ data_index + 3 ];    // alpha
        }
    }

    // memcpy( _screen_shot_pixels, _data, _size );

    _bgfx_screen_shot_done = true;
}

void _bgfx_read_pixels( Array<int> pixels, int x, int y, int width, int height, int arrayOffset, int arrayPitch)
{
    printf("_bgfx_read_pixels\n");

    _bgfx_screen_shot_pixels = (void *)&pixels[0];
    _bgfx_screen_shot_x = x;
    _bgfx_screen_shot_y = y;
    _bgfx_screen_shot_width = width;
    _bgfx_screen_shot_height = height;
    _bgfx_screen_shot_offset = arrayOffset;
    _bgfx_screen_shot_pitch = arrayPitch;

    bgfx_frame(false);

    // flag for waiting until bgfx_callback_screen_shot is done
    _bgfx_screen_shot_done = false;

    bgfx_frame_buffer_handle_t fbh = BGFX_INVALID_HANDLE;
    bgfx_request_screen_shot(fbh, "temp");

    while (_bgfx_screen_shot_done == false)
    {
        // wait until bgfx_callback_screen_shot is done
        // bgfx_touch(0);    // should we do bgfx_touch(0)? it doesn't hurt to do it
        bgfx_frame(false);
    }
}

Notice the while loop in _bgfx_read_pixels, I am really scared about that while loop. But it is required because the code has to wait until the callback is done. The other way I know of doing this is using framebuffers. (examples/mojo2/rendertoimage.cxs) But it does not have direct pixels access. That code above is a real hack to get read pixels going.

I also got monkeystien to work, but it is just red and all the texture is wrong. The matrix in Mojo 1 needs to be reset at the start of every OnRender. I think that was the major problem there.

charlie/blobmonster is also working. This is because in Mojo 2, Image.SetHandle(0.5, 0.5) works. But in Mojo 1 it needs to be Image.SetHandle( width * 0.5, height * 0.5 ). So I rename the Mojo 2 SetHandle to SetHandlef (may be there is a more descriptive name here), and create a SetHandle that is compatible with Mojo 1.
 
Last edited:
@Ferdi - Sorry about the multiple view id comment for clear screen, I think I was confusing the earlier work you were doing before implementing sequential indexing and maybe some mixup between your work on mojo1 and mojo2. I was looking at your comment with code where you were talking about the framebuffer using view 0 and 1 with colortex and normtex, but again I think that was related to mojo2.

I saw the bgfx screenShot and captureFrame methods, so as you mentioned, those would probably result in a more optimized way to grab a screenshot. But as long as you have the placeholder to implement those later and the current read pixel implementation is working it seems ok to move on to other things and circle back to the screenshot later.

It looks like you are doing a mix of mojo1 and mojo2 work simultaneously, are you still planning to take on mojo2 in its entirety or are you just seeing how far you could take it? Good to see the progress you are making!
 
@FerdiIt looks like you are doing a mix of mojo1 and mojo2 work simultaneously, are you still planning to take on mojo2 in its entirety or are you just seeing how far you could take it? Good to see the progress you are making!

I am using mojo 2, and I am creating a mojo 1 wrapper codes around mojo 2. That sounds confusing. Here is some example of the wrapper code:

Code:
Global _mojoCanvas:Canvas ' this is declared in graphics.cxs

' these are functions are in wrapper.cxs
Function DrawPoint:Void( x0:Float, y0:Float, material:Material=Null, s0:Float=0, t0:Float=0 )
    _mojoCanvas.DrawPoint( x0, y0, material, s0, t0 )
End

Function DrawLine:Void( x0:Float, y0:Float, x1:Float, y1:Float, material:Material=Null, s0:Float=0, t0:Float=0, s1:Float=1, t1:Float=0 )
    _mojoCanvas.DrawLine( x0, y0, x1, y1, material, s0, t0, s1, t1 )
End

Function DrawCircle:Void( x:Float, y:Float, r:Float, material:Material=Null )
    _mojoCanvas.DrawCircle( x, y, r, material )
End

Function DrawImage:Void( image:Image, tx:Float, ty:Float, frame:Int=0 )
    _mojoCanvas.DrawImage( image, tx, ty, frame )
End

Function DrawImage:Void( image:Image, tx:Float, ty:Float, rz:Float, sx:Float, sy:Float, frame:Int=0 )
    _mojoCanvas.DrawImage( image, tx, ty, rz, sx, sy, frame )
End

Function DrawImage9P:Void( image:Image, tx:Float, ty:Float, patchSize:Int, rz:Float, scaleX:Float, scaleY:Float, frame:Int=0 )
    _mojoCanvas.DrawImage9P( image, tx, ty, patchSize, rz, scaleX, scaleY, frame )
End

Mojo 1 uses OpenGL 1.1? I think. It does not have shader. The canvas GLFW code for Mojo 1 is in modules/mojo/native/mojo.glfw.cpp (class gxtkGraphics). In that C++ file, OpenGL functions are called directly in C++. For the bgfx Mojo 1 module, I created the bgfx module, so I can call bgfx from Cerberus. I create the canvas code that calls bgfx module functions, in Cerberus. Then it is just a wrapper code around the canvas.

So the layer looks like this: (and its equivalent Mojo 1 layer)

Code:
1. bgfx (CX) = OpenGL 1.1 (C++)
2. Canvas/Image (CX) = gxtkGraphics/gxtkSurface (C++)
3. wrapper.cxs (CX) = modules/mojo/graphics.cxs (CX)

Hope that make sense. Ask questions if you don't understand.

Mojo 2 is not possible. The user needs to change the view id manually in their code.

Screenshots: (Monkenstien, Blobmonster, and DynamicImage)

mojo1monkenstein.png mojo1blobmonster.png mojo1dynamicimage.png

In the shader code for DynamicImage, I had to multiply the rgb by the alpha to get the gradient look same as Mojo 1. But I feel this is incorrect. I need to look into it some more.

Code:
gl_FragColor=vec4( color.rgb * b3d_Alpha, b3d_Alpha );

Of I don't multiply color.rgb * b3d_Alpha (ie gl_FragColor=vec4( color.rgb, b3d_Alpha ) ), I will get the screenshot like this:

mojo1dynamicimage2.png

I have checked that I have all Mojo 1 functions from the help document (including class Image and Font).

I have run most examples, except for Mak directory, because some are not graphics related.

I am using Mojo 2 fonts which is a bigger size than Mojo 1 fonts. Do you want me to change it to Mojo 1 fonts? (I prefer Mojo 2 fonts)

I need to do the whole compiling + target to make it easy, but I do want to do this on top of dawlane changes, if not the merging will be hell.

I think I have Mojo 1 working in bgfx. I am still scared about ReadPixels, because of the while loop. I think if users want to do pixels stuff they need to use shader.
 
Last edited:
In the shader code for DynamicImage, I had to multiply the rgb by the alpha to get the gradient look same as Mojo 1. But I feel this is incorrect.

No no, this is correct. I don't know the reason, but mojo2 uses pre-multiplied colors for everything and therefore just blends by adding color values. Not doing the premultiplication results in bleeding colors.
 
No no, this is correct. I don't know the reason, but mojo2 uses pre-multiplied colors for everything and therefore just blends by adding color values. Not doing the premultiplication results in bleeding colors.

But if I used the shader that multiply b3d_Alpha (gl_FragColor=vec4( color.rgb * b3d_Alpha, b3d_Alpha )), blobmonster does not light up. Here is a screenshot of blobmonster not lighting up, which is wrong:
mojo1blobmonster2.png
Currently I am only using the multiply b3d_Alpha shader on class Image, when it is creating a material. Default shader I am still using a shader that does not multiply b3d_Alpha.

Hope Mike does not mind me doing this. I tried to run fantomEngine. I need to change "import mojo" to "import mojo1.bgfx" and "mojo.xxx" to mojo1bgfx.xxx" in several places, but apart from that it runs:

fEpathfinding.png fETexturePacker.png fEWaypoints.png
I also did the book examples, and I had to fix 2 problems on my end to get it working:
bookrocketcommander.png bookcometcrusher.png bookairdogs1942.png

If anyone has a Mojo 1 project, that I can test that will be great. So I can fix any problems now.
 
@Ferdi - Thanks for the explanation, that makes a lot more sense now. I do agree with you about the screenShot while loop with read pixel, but it still works and is a placeholder for a better solution if possible.
 
I have been looking at extending the Mojo 1 code, so it does Mojo 2 examples, with modifications. I created the following functions to see whether Mojo 2 examples would work:

Code:
Global _canvases:Stack<Canvas>
Global _canvasStack:Stack<Canvas> ' for PushCanvas and PopCanvas
Global _activeCanvas:Canvas

Function ResizeCanvases:Void( newSize:Int )

    Local length:Int = _canvases.Length()

    For Local viewId:Int = length Until newSize
        _canvases.Push( New Canvas( viewId ) )
    Next

    ' debugging
    length = _canvases.Length()
    For Local index:Int = 0 Until length
        Print "index=" + index + " _canvases[index].GetViewId()=" + _canvases.Get(index).GetViewId()
    Next
End

Function SetCanvasViewMode:Void( viewMode:Int )
    _activeCanvas.SetViewMode( viewMode )
End

Function SetCanvasViewMode:Void( viewId:Int, viewMode:Int )

    If _canvases.Length() <= viewId Then
        ResizeCanvases( viewId + 1 )
    Endif

    _canvases.Get(index).SetViewMode( viewMode )
End

Function ResetCanvas:Void()
    _activeCanvas = _canvases.Get(0)
End

Function SetCanvas:Void( viewId:Int )

    If _canvases.Length() <= viewId Then
        ResizeCanvases( viewId + 1 )
    Endif

    _activeCanvas = _canvases.Get(viewId)
End

Function GetCanvas:Int()
    Return _activeCanvas.GetViewId()
End

Function NextCanvas:Void()
    Local viewId:Int = GetCanvas() + 1
    If viewId >= MAX_CANVASES - 1 Then viewId = MAX_CANVASES - 1
    SetCanvas( viewId )
    Print "NextCanvas viewId=" + viewId
End

Function PrevCanvas:Void()
    Local viewId:Int = GetCanvas() - 1
    If viewId <= 0 Then viewId = 0
    SetCanvas( viewId )
End

Function PushCanvas:Void()
    _canvasStack.Push( _activeCanvas )
End

Function PopCanvas:Void()
    _activeCanvas = _canvasStack.Pop( )
    Print "PopCanvas viewId=" + GetCanvas()
End

It does work, but I feel I am still not comfortable with bgfx. So I am going to go and port more bgfx examples to Cerberus for now.

I have attached the current state of mojo 1 bgfx module, for backup purposes. In this version you need to compile the bgfx library yourself, and change the makefile to the directory of the bgfx library. Not very user friendly.
 

Attachments

  • mojo1bgfx_20181102.zip
    890.1 KB · Views: 438
I managed to get example 2 metaballs working over the weekend:

02-metaballs.png

The text on the top left is drawn using mojo1bgfx. So it is possible to draw 3d and use mojo1bgfx for 2d stuff on top.

Source code is attached.
 

Attachments

  • metaballs.cxs
    31.3 KB · Views: 317
Hope Mike does not mind me doing this. I tried to run fantomEngine. I need to change "import mojo" to "import mojo1.bgfx" and "mojo.xxx" to mojo1bgfx.xxx" in several places, but apart from that it runs
Not at all. I see a Problem with that screenshot:
fetexturepacker-png.360


The purple piece you be smaller and circling around the center of the orb.
 
Thanks Mike! I have managed to fix that problem. The problem is with the handle when using DrawImageRect. I need to divide the handle by the sourcewidth and sourceheight pass in by DrawImageRect, instead of using the image width and height. DrawImageRect is for drawing an image in an atlas.

fETexturePacker2.png

That brings me to the next point. Image.SetHandle is okay for DrawImage, because it is one image and the handle will most probably at the same location.

Code:
Method OnCreate()
   boxImg.SetHandle(16, 16)
End

Method OnRender()
   DrawImage(boxImg, 100, 100)
End

Image.SetHandle is okay for DrawImageRect only when all images in the atlas is the same size and handle. The best examples is RPG Maker sprite sheets. The width and height for each sprites are the same, and the handle can be set the same too. (https://www.google.com.au/search?q=rpg+maker+sprites)

Code:
Method OnCreate()
   atlasImg.SetHandle(16, 32)
End

Method OnRender()
   DrawImageRect(atlasImg, 100, 100, 0, 0, 32, 32)
   DrawImageRect(atlasImg, 150, 100, 32, 0, 32, 32)
   DrawImageRect(atlasImg, 200, 100, 64, 0, 32, 32)
End

But Image.SetHandle fails when the atlas has images of varying sizes and handle.

Code:
Method OnCreate()
   ' cannot do Image.SetHandle here
End

Method OnRender()
   atlasImg.SetHandle(32, 32)
   DrawImageRect(atlasImg, 100, 100, 0, 0, 64, 64)
   atlasImg.SetHandle(16, 16)
   DrawImageRect(atlasImg, 150, 100, 64, 0, 32, 32)
   atlasImg.SetHandle(16, 32)
   DrawImageRect(atlasImg, 200, 100, 96, 0, 32, 64)
End

So I have implemented the following functions to solve this problem

Code:
' handleX = 0 -> srcWidth
' handleY = 0 -> srcHeight
Function DrawImageHandle:Void( image:Image, x:Float, y:Float, srcX:Int, srcY:Int, srcWidth:Int, srcHeight:Int, handleX:Float, handleY:Float, frame:Int=0 )
Function DrawImageHandle:Void( image:Image, x:Float, y:Float, srcX:Int, srcY:Int, srcWidth:Int, srcHeight:Int, rotation:Float, scaleX:Float, scaleY:Float, handleX:Float, handleY:Float, frame:Int=0 )

' alignX = 0.0 -> 1.0
' alignY = 0.0 -> 1.0
Function DrawImageAlign:Void( image:Image, x:Float, y:Float, srcX:Int, srcY:Int, srcWidth:Int, srcHeight:Int, alignX:Float, alignY:Float, frame:Int=0 )
Function DrawImageAlign:Void( image:Image, x:Float, y:Float, srcX:Int, srcY:Int, srcWidth:Int, srcHeight:Int, rotation:Float, scaleX:Float, scaleY:Float, alignX:Float, alignY:Float, frame:Int=0 )

Hopefully that make sense why I added these functions. Another way is to use GrabImage, but that creates a separate texture. OpenGL/bgfx prefers not to swap texture during drawing.
 
opefully that make sense why I added these functions. Another way is to use GrabImage, but that creates a separate texture. OpenGL/bgfx prefers not to swap texture during drawing.
I can tell you that not many use sprite sheets. So swapping textures is pretty common.
 
I can tell you that not many use sprite sheets. So swapping textures is pretty common.

I found Crunch, it is a texture packer made for the game Celeste. I thought it would be nice to have functions that load up atlases directly.

https://github.com/ChevyRay/crunch (MIT license)

But if users does not want to use sprite sheets then I won't implement it.

Anyway, I did another example. Example 3 raymarch:

03-raymarch.png

Source code is attached.
 

Attachments

  • raymarch.cxs
    6.2 KB · Views: 304
There are 3 things I need to talk about.

1. I did example 4 loading up the bunny. Here is a screenshot:

04-mesh.png

I have also attached the code. I need to move all the bgfxUtilsXXX into bgfx module directory. Currently it is in the example. Also I haven't implemented reading index buffer compression chunk (BGFX_CHUNK_MAGIC_IBC).

2. I also did example 5 instancing. Here is a screenshot:

05-instancing.png

I don't like how I have implemented transient buffer and instancing buffer. You have to use PokeFloat, PokeInt etc. I think this is just too slow. It needs to either pass the data pointer to Cerberus OR Cerberus pass one big memory to bgfx. I notice in DataBuffer class, there is PokeFloats, PokeInts etc which pokes an array of float and an array of int repectively. I think I need to do something similar.

3. In regard to atlas, I looked at fantom engine td_spritesheet.txt. And I emailed Code And Web. (He was very nice.) He pointed me to LibGDX. Also he told me Code And Web Texture Packer can export to LibGDX texture packer format. I also found this in LibGDX tools:

https://libgdx.badlogicgames.com/tools.html

I tried using the libgdx texture packer, "java -jar runnable-texturepacker.jar sprites" and it created the following atlas file:

Code:
pack.png
size: 512,256
format: RGBA8888
filter: Nearest,Nearest
repeat: none
Destroyer
  rotate: false
  xy: 2, 124
  size: 128, 128
  orig: 128, 128
  offset: 0, 0
  index: -1
EkstraLive
  rotate: false
  xy: 262, 220
  size: 32, 32
  orig: 32, 32
  offset: 0, 0
  index: -1
Gold
  rotate: false
  xy: 2, 14
  size: 32, 32
  orig: 32, 32
  offset: 0, 0
  index: -1
... etc

If you want to use Code And Web Texture Packer you can, as long as you export the atlas data to LibGDX format. And if you don't have access to it, then you can use the LibGDX Texture Packer Tools at this link: https://libgdx.badlogicgames.com/tools.html

So I have code to load the above atlas file up. The atlas API looks like this right now:

Code:
    Field destroyerImg:AtlasImage
    Field ekstraLiveImg:AtlasImage
    Field goldImg:AtlasImage

    Method OnCreate()
        LoadAtlas("td_spritesheet.txt")
        destroyerImg = LoadAtlasImage("sprites/Destroyer.png")
        ekstraLiveImg = LoadAtlasImage("sprites/EkstraLive.png")
        goldImg = LoadAtlasImage("sprites/Gold.png")
    End

    Method OnRender()
        DrawAtlasImage(destroyerImg, 0, 0)
        DrawAtlasImage(ekstraLiveImg, 100, 0)
        DrawAtlasImage(goldImg, 200, 0)
    End

LoadAtlas load the atlas images into a global stack of atlas images. So LoadAtlasImage looks through that stack, whether there is that image, if it does not find that image it will try to load the file by itself.

Code:
        ' comment out LoadAtlas to use individual files
        ' LoadAtlas("td_spritesheet.txt")
        destroyerImg = LoadAtlasImage("sprites/Destroyer.png")
        ekstraLiveImg = LoadAtlasImage("sprites/EkstraLive.png")
        goldImg = LoadAtlasImage("sprites/Gold.png")

So during development I can comment out LoadAtlas and use the individual file, only when I am done with all my graphics then I create the atlas(es) and I put the line LoadAtlas.

I can change it if the community does not like this API.

EDIT: Are you guys interested in me loading up that 2D Particle Editor file at https://libgdx.badlogicgames.com/tools.html ? Or do you want to use something else?
 

Attachments

  • 04-mesh.cxs
    12.4 KB · Views: 314
  • 05-instancing.cxs
    7.4 KB · Views: 358
Last edited:
What happens with 2 atlas and image names are the same?

Yes that will make it fail.

Sorry I forgot to mention this and it is rather important. So this is what I have, when it is pushing to create the global atlas image, it checks for file name duplication:

Code:
Class Atlas

    Global _images:Stack<AtlasImage> = New Stack<AtlasImage>()

    Function Load:Void(filePath:String)
                    ...
                    atlasImage.index = Int(_lineValues[0])

                    ' print an error if there is a duplicate name
                    CheckDuplicateName(atlasImage.name)

                    _images.Push(atlasImage)
                    ...
    End

    Function CheckDuplicateName:Void(name:String)
        For Local ii:Int = 0 Until _images.Length()
            Local atlasImage:AtlasImage = _images.Get(ii)
            If atlasImage.name = name Then
                Print "Duplicate atlas image name: " + name
                Exit
            Endif
        Next
    End

End

The current solution is to print any duplicate file name at runtime. Not a clean solution. We can change this if you don't like it.
 
I have no preferation about this. But i can see devs using several atlas where names collide.
Anyway someone could always use a selfmade solution. I would have implented it differently but each dev has their own way of doing things.
 
Back
Top Bottom