Skip to main content

News

Topic: Sphere 5.5.2 (previously miniSphere) (Read 420954 times) previous topic - next topic

0 Members and 1 Guest are viewing this topic.
  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1560
There was a bug where fonts would be loaded without an alpha channel that I fixed, but nothing for images that I remember.  Could you give me some sample code that reproduces the bug?
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1561

There was a bug where fonts would be loaded without an alpha channel that I fixed, but nothing for images that I remember.  Could you give me some sample code that reproduces the bug?
Correction it's only for surfaces, not images in general we discussed this in December and I could run a sript and get one result (with the error) and you ran the same script and got a different result (no error).

See discussion below:
http://forums.spheredev.org/index.php/topic,1215.1425.html

I could try and look into this later on, would be very useful for me to get it fixed - and it may well be something I've messed up inn the compilation.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1562
There was a bug I fixed a few versions ago where Shape#draw() failed to normalize texture coordinates when drawn to a surface, which also indirectly affected prim.blit(), but yeah, I wasn't able to reproduce your bug in particular.  I also can't begin to figure out why it happens: You say it only affects cloneSurface and not cloneImage, but those two calls do the exact same thing internally (i.e. there is no low-level distinction between surfaces and images).
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1563
Found the fix to the error I'd found, did some googling...

But firstly to explain + clarify the issued:

1. Problems only arose if one used the Surface method: surface_object.cloneSection on a surface containing transparency - on using this anything that ought to be transparent in the clone would not be.

2. The problems would get worse if surface_object.createImage() was used on the cloned section returned from the above - weird seemingly inexplicable distortions would occur.

The result of my googling was this:
https://www.allegro.cc/forums/thread/616777
Apparently it would have been an issue on Linux too; the issue is with the function Allegro uses to create a new bitmap, the data for the new bitmap on Windows is automatically cleared whereas on Unix based systems it isn't.

Working fix I've implemented in my build:
In the file vanilla.c in the function: js_Surface_cloneSection(duk_context* ctx) I added  al_clear_to_color(al_map_rgba_f(0,0,0,0)); after the image is created. I've tested this with a couple of different test scripts and the error is now gone.

Full function with the addition:
Code: [Select]
static duk_ret_t
js_Surface_cloneSection(duk_context* ctx)
{
int      height;
image_t* image;
image_t* new_image;
int      width;
int      x;
int      y;

duk_push_this(ctx);
image = duk_require_class_obj(ctx, -1, "ssSurface");
x = duk_to_int(ctx, 0);
y = duk_to_int(ctx, 1);
width = duk_to_int(ctx, 2);
height = duk_to_int(ctx, 3);

if ((new_image = image_new(width, height)) == NULL)
duk_error_blame(ctx, -1, DUK_ERR_ERROR, "unable to create surface");
al_set_target_bitmap(image_bitmap(new_image));
al_clear_to_color(al_map_rgba_f(0,0,0,0));//the fix
al_draw_bitmap_region(image_bitmap(image), x, y, width, height, 0, 0, 0x0);
al_set_target_backbuffer(screen_display(g_screen));
duk_push_class_obj(ctx, "ssSurface", new_image);
return 1;
}


  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1564
Ah, that explains it, thanks for investigating!  Good to know the fix is simple.

I probably should have tested this in Linux; I have a Ubuntu VM I use when making releases but the extent of my testing there is usually just to make sure I didn't cause any major breakage (= if it doesn't crash, I'm satisfied).
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1565

Ah, that explains it, thanks for investigating!  Good to know the fix is simple.

I probably should have tested this in Linux; I have a Ubuntu VM I use when making releases but the extent of my testing there is usually just to make sure I didn't cause any major breakage (= if it doesn't crash, I'm satisfied).
I'll assume you'll add the above to the source tree?

Other question - I remember asking a while ago about the "CreateSpriteset()" function which doesn't exist in miniSphere - I can probably fake it by having a blank spriteset document I load would be nice to have it available to avoid the extra document. Another related function that doesn't seem to be there is spriteset_object.save() - I guess I could do what I'm doing for maps, have an image and a JSON file, load the JSON then load the image and have a function that sets up the spriteset object I need by combing info from the two but as Sphere's native spritesets do everything I need here nice if I could have those.

Alternatively I guess I could implement creating/saving spritesets in javascript with rawfiles but I image that would be more painful than it being implemented in C.

You said before that there was something unpleasant about the code behind the spriteset object and so the omission of the creation function was intentional?

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1566

I'll assume you'll add the above to the source tree?


Maybe not that exact patch; I'm thinking it might be better to move the clear call into image_new() to prevent the bug entirely.

As for CreateSpriteset, here's the issue: In miniSphere, a Spriteset object on the JS side is just a stand-in for a pointer on the C side, which points to a struct representing the actual spriteset data.  It's impossible (at least in Duktape) to catch all modifications to the JS Spriteset object; for example while one can easily swap out existing images and be sure it will work (because each image list entry has a custom setter):

Code: (javascript) [Select]

spriteset.images[0] = someOtherImage;


Things like adding and deleting images on the fly will NOT work and may cause strange things to happen:
Code: (javascript) [Select]

spriteset.images.push(someNewImage);  // suppose this becomes the 10th image (i.e. slot 9)
spriteset.directions[0].frames[0].index = 9;


If you try to use such a modified spriteset later, it will probably crash the engine because it didn't see the .push() and therefore has no idea about the new image(s) that you added.  So with that in mind, something like CreateSpriteset, specifically designed to build spritesets on-the-fly, just isn't going to work.  The reason all this works in 1.5, so my theory goes, is that the map engine just uses the JS spriteset object rather than using a pointer.  In other words it just reads directly from spriteset.images etc. so anything you add is automatically accounted for.  The downside is that doing things this way doesn't catch errors as quickly because the engine can't validate the modifications until you actually go to use the spriteset.

I can fix the spriteset system to be v1-compatible of course, but at this point it'd be a LOT of work for questionable gain, particularly as miniSphere 5.0 is slated to get a new modern map engine anyway and therefore the old map engine is about to become as deprecated as the rest of the v1 API.  Hm...
  • Last Edit: May 19, 2017, 12:34:20 am by Fat Cerberus
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1567
@Rhuan: Okay, the Surface#cloneSection() bug is fixed and will be in the next release, thanks again for that patch! ;D  I also fixed a similar bug in Surface#rotate() that I happened to notice along the way.
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1568
Thanks :) (Have you fixed it for surface.cloneSection as well as Surface#cloneSection?)

Couple of things I'd like to query:

1. Can you sell me on Sphere version2 - I'm still not sure what I think of it from the things you've said, I love the increased speed of miniSphere over 1.5 for most things, but can't currently see any significant benefits to changing API, I'm open to being persuaded though... I'm particularly interested in:
- understanding how Galileo is meant to work - my current graphics activities involve a lot of odd stuff - the code base for my current project uses quite a few graphics primitives and lots of zoomBlit and transformBlitMask in order to get the sort of graphical functions I want.
- Understanding what benefit the Dispatch API would have over the while loop of nested switch statements that I use - if any, with my loop everything is set up in order, and the switch statements ensure that the correct state is maintained and the correct actions happen, would Dispatch simplify this somehow? Could it be faster? i.e. instead of changing a state variable so the next iteration of the loop drops into a different case, I'd cancel one recurring job with dispatch and schedule another - should there be any particular benefit here?

2. On the Spritesets point
- I looked at the existing Sphere Map engine for my current project and quickly discarded it - it doesn't have nearly the level of flexibility I wanted
- So I scripted my own which is working very nicely (I can zoom in and out on the fly, move around, optimise by only drawing sprites that are on screen and handle various other things that my project wants); I'm therefore unlikely to be interested in the new map engine you're promising, at least for now.
- So why Sphere Spritesets - I want a way of having sprites that I can use easily, they need arrays of images, and directions and frames, all things that the sphere spriteset objects have
- I want to be able to load them quickly with one command rather than having a JS loop that goes through opening lots of images OR slicing up one larger image into the separate frames (I have a total of 202 images per sprite character - I split them accross two sprite files as some are a different size)
- ultimately I'm using tranformBlitMask to draw them when I need them, not any of Sphere's native sprite functions

- I don't know of a way to load an image in Sphere apart from via a spriteset or an image file (or a surface that I then slice up)

- I am using a non-sphere tool to make the spriteset graphics (it outputs a single image file with all the various frames)
- so my current process is to use the below setup code to create spriteset files (my project has a config document that stores if setup is complete or not - if not the below and a few other features are called, though for now setup only runs in 1.5 and the rest of the project only runs in miniSphere...

Code: [Select]
function make_sprite(name)
{
  var input  = LoadSurface("input/"+name+".png");
 
  //make the spritesets
  var output = CreateSpriteset(64, 64, 178, 21, 6);
  var a_output = CreateSpriteset(192, 192, 24, 4, 6);
 
  var t_x    = 0;
  var y      = 0;
  var x      = 0;
  var i      = 0;

  for (var y = 0; y < 21; ++y)
  {
    if(y < 4)
    {
      t_x = 7;
    }
    else if (y < 8)
    {
      t_x = 8;
    }
    else if (y < 12)
    {
      t_x = 9;
    }
    else if (y < 16)
    {
      t_x = 6;
    }
    else if (y < 20)
    {
      t_x = 13;
    }
    else
    {
      t_x = 6;
    }
    for(x = 0; x < t_x; ++x, ++i)
    {
      output.images[i]=(input.cloneSection(x*64,y*64,64,64).createImage());
      output.directions[y].frames[x].index = i;
    }
    output.directions[y].frames.length = x;
  }
  for(y = 0, i = 0; y < 4; ++ y)
  {
    for (x = 0; x < 6; ++ x, ++ i)
    {
      a_output.images[i] = input.cloneSection(x*192,1344+y*192,192,192).createImage();
      a_output.directions[y].frames[x].index = i;
    }
  }
 
  //name the directions - based on format of sheet
  output.directions[0].name   = "spell_n";
  output.directions[1].name   = "spell_w";
  output.directions[2].name   = "spell_s";
  output.directions[3].name   = "spell_e";
  output.directions[4].name   = "thrust_n";
  output.directions[5].name   = "thrust_w";
  output.directions[6].name   = "thrust_s";
  output.directions[7].name   = "thrust_e";
  output.directions[8].name   = "North";
  output.directions[9].name   = "West";
  output.directions[10].name  = "South";
  output.directions[11].name  = "East";
  output.directions[12].name  = "slash_n";
  output.directions[13].name  = "slash_w";
  output.directions[14].name  = "slash_s";
  output.directions[15].name  = "slash_e";
  output.directions[16].name  = "shoot_n";
  output.directions[17].name  = "shoot_w";
  output.directions[18].name  = "shoot_s";
  output.directions[19].name  = "shoot_e";
  output.directions[20].name  = "death";
  //attack animation directions
  a_output.directions[0].name = "North";
  a_output.directions[1].name = "West";
  a_output.directions[2].name = "South";
  a_output.directions[3].name = "East";
 
  output.save(name+".rss");
  a_output.save(name+"_a.rss");
}

var to_make = GetFileList("images/input");
for(var i = 0; i < to_make.length;++i)
{
  if(to_make[i][0]!=".")
  {
    make_sprite(to_make[i].slice(0,to_make[i].length-4));
  }
}
  • Last Edit: May 19, 2017, 01:04:30 pm by Rhuan

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1569

Thanks :) (Have you fixed it for surface.cloneSection as well as Surface#cloneSection?)


Those are the same thing - the # is just shorthand for prototype, in other words "Surface#cloneSection" is an abbreviation for "Surface.prototype.cloneSection" (which is then inherited by all surface objects).

I'll address your other points later, once I have more free time to sit down and reply.
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1570


Thanks :) (Have you fixed it for surface.cloneSection as well as Surface#cloneSection?)


Those are the same thing - the # is just shorthand for prototype, in other words "Surface#cloneSection" is an abbreviation for "Surface.prototype.cloneSection" (which is then inherited by all surface objects).

I'll address your other points later, once I have more free time to sit down and reply.
OH.... When I'd previously been reading the v2 api I genuinely thought using it would involve # symbols all over my code...

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1571

OH.... When I'd previously been reading the v2 api I genuinely thought using it would involve # symbols all over my code...


Nope, it's just a documentation convention used in the JS community, "Class#method()", means if you have an object of type Class, you can call obj.method().  Whereas if I said instead "Class.method()" that means you call it exactly as written (it's a method of the class itself, i.e. a static method).

Now for your other points:

  • Galileo exists because modern graphics hardware performs better when you tell it everything you want drawn at once.  Using the classic primitives involves a draw request for each primitive, which adds up quickly.  Essentially it's the difference between saying "Oh hey, I need you to draw this" for every primitive vs. giving the GPU a list and say "Hey, draw all of this stuff for me".  The latter is much faster because there's minimal overhead.  One practical application is in map engines (and this what FJ's map engine in TurboSphere was set up to do): You construct an entire map as a single Shape, and then just do map.draw() each frame.

  • The Dispatch API is designed to set asynchronous tasks to run, independent of the normal flow of your program.  For example, the term module uses it to run itself, so that you can pull up a debug console from any place in your game--without having to remember to call its update function yourself.  As long as you're drawing frames, the Dispatch API jobs will fire on time.  See https://github.com/fatcerberus/minisphere/blob/master/assets/system/modules/term.js#L47-L51

  • One of the goals I have in mind for the new map engine is to maximize the number of extension points.  The problem with the Sphere 1.x engine is that it's often not flexible enough.  It's certainly more open to extension than, say, any of the RPG Makers, but it does still get in the way sometimes, as you point out.  I want to make mine a lot more extensible so that it can be used in more scenarios.



Quote
so my current process is to use the below setup code to create spriteset files (my project has a config document that stores if setup is complete or not


Hehe, these types of setup routines were the bane of my existence during early miniSphere development. ;)  "In the Steps of the Blackfoot" uses a similar process that involves slicing up an image containing a bunch of mushroom sprites and saving each one as an individual image file.  That forced me to implement Image#save() to be compatible.  Actually that was supposed to be the purpose of Cell when I first came up with it, to avoid the need for such first-time setup.  All the setup would instead go into your Cellscript, where the developer could trigger the setup process before distributing the game, but otherwise keep things in their "native" format for development.  Unfortunately, Cell still doesn't have all the routines necessary to do such a thing, it mostly only serves as a transpiler to allow you to use the new JS syntax features.  I'm working on expanding it though. :)
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1572
So:

Galileo sounds good for a few things I do e.g. I have a pointer that is currently drawn by calling Line() 160 times each frame - I may actually be able to optimise this just be drawing it onto a surface once and going from there though without switching to Galileo. Where I'm not sure how Galileo would work is for things like my sprite handing system - this checks and updates the direction, frame and location for any number of sprites then draws the ones that are on screen - as a given frame/direction combination is a different image I assume I'd have to re-texture a shape every time I wanted to draw - this would at a minimum double the function calls for drawing these - unless it's meant to work some completely different way, any thoughts on this?

Dispatch could make my code look a lot cleaner by the sound of it - and cut down the number of conditions being checked - though thinking about it I may be able to do the same by implementing a function queue and a loop - I assume that's effectively what dispatch is though?

Sphere v1s API is plenty flexible if you don't mind doing some odd stuff :P


Question on the spriteset setup point: as I can load a tempalte spriteset now I don't really need CreateSpriteset, the one thing I really do need is Spriteset#save(), any chance of this being implement OR.... I'll probably go read up on the file format and write a script to do it with raw files, shouldn't take too long...

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: miniSphere 4.5.11
Reply #1573
To be honest Dispatch was mostly implemented in service to Oozaru, the browser version of Sphere I eventually need to get around to implementing. :P  In a browser, the normal Sphere workflow of having a central game loop won't fly--the browser expects your script to finish promptly and will forcibly kill your loop if it runs too long.  So if you want to be web-compatible, you HAVE to use the Dispatch API and your code ends up looking like this:
https://github.com/fatcerberus/minisphere/blob/master/assets/template/src/main.mjs

There's no loop, the engine (or browser) provides it.  This is also how Node.js works, incidentally.

Regarding Galileo for sprites: Even if you have one Shape per sprite pose and have to draw them individually, it still (probably) ends up being faster than a normal blit--assuming you construct the Shapes in advance and just swap them out at render time--because the vertices are already uploaded to the graphics card.  Calling Image#blit() involves sending two triangles to the GPU each time.  Basically, the Sphere v1 API was designed in a time when computers could actually work at the pixel level and render in 2D.  Nowadays, everything is 3D and polygon-based.  You don't draw images directly, but rather draw a quad (two triangles) using the desired image as a texture.

To get an idea of how much you can save on overhead, take your line segments.  Each one is in fact rendered as two triangles:
https://github.com/fatcerberus/minisphere/blob/master/assets/system/modules/prim.js#L79-L98

As you can see there, every line involves uploading 4 (not 2!) vertices to the GPU.  If you can find a way to construct such shapes in advance, that upload only has to happen once (hint: you can draw it at different positions using a Matrix).  Circle/ellipses are even worse as they consist of a large number of vertices proportional to their radius.  At some point uploading vertices every single frame is going to become a major bottleneck, and that's why Galileo exists.  Basically, it gives you ultimate control over HOW things are drawn.  You can choose to draw on-the-fly, as you are now, are construct things in advance if that turns out to be better.  You don't have the latter option with v1.

So yeah, I rambled a bit, but hopefully you can understand the purpose now.

At this point I feel I should mention: Oozaru will not support the v1 API.  So me referring to it as deprecated is not just blowing hot air: While it's provided in miniSphere for backward compatibility to make migration easier, I'd no longer consider it part of the "Sphere standard" so to speak.  v2 is the way forward, for better or worse.  Hopefully better, though. :P
Sphere 5.5.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: miniSphere 4.5.11
Reply #1574
I've got no current interest in making a project that runs in a web browser, maybe I should but I just don't right now - so the oozaru support point isn't a key concern for me.

I'm using miniSphere because it runs faster than Sphere 1.5 and it compiled fairly easily for mac - if the v1 api is dropped from newer versions of it at some point I can always stick with the last version that supports it.

On the sprites point, let me see if I understand, so currently each sprite is comprised of approximately 240 images. right now I have code that loops through the array of characters and for each one determines which image from its sprite to draw, where to draw it, what colour mask to use and how zoomed in to make it - it then draws it and moves on to the next one.

Now lets say instead I made 240 textured shapes per sprite and the code worked out which shape to draw and where and then drew; script wise the difference would be the extra step of making the shapes and then the step of transforming the relevant shapes each frame to move them before drawing them.

Galileo certainly sounds better for any complex shapes comprised of multiple primatives or the like OR even for when it is particular image or shape you're intending to keep on screen a lot and move around - it doesn't seem to make sense for sprites though unless I'm misunderstanding it.