Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: The new graphics API in SFML 2  (Read 71990 times)

0 Members and 1 Guest are viewing this topic.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
The new graphics API in SFML 2
« Reply #30 on: September 14, 2011, 11:01:59 am »
Quote
A colored rectangle. Ok a shape... So they would be quite the same. Shape (with optional texture) and Sprite I mean. Would we need both ?

Technically we can definitely merge them, but their public API is quite different. It would be a weird mix: a sprite with a colored outline? a custom shape with a subrect? ....

Quote
In fact, I was not speaking about the font texture but what you can draw "in" the characters. Sorry for the misunderstanding here.

Characters are not geometrical entities, they are quads mapped with the font's texture. So you can't draw anything inside them.

Quote
As for the implementation if two consecutively drawn objects have the same texture then we do not have to load it again (from OpenGL pov)

It's far from being as easy as it looks, trust me :lol:

Quote
Gosh! It's a complicated!

Yeah... otherwise it would already be implemented and I would be thinking about SFML 3 ;)
Laurent Gomila - SFML developer

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
The new graphics API in SFML 2
« Reply #31 on: September 14, 2011, 11:07:39 am »
Quote
Characters are not geometrical entities, they are quads mapped with the font's texture. So you can't draw anything inside them.
Ho! So it change everything ! Texture is no more something in common for these classes so my argumentation doesn't work...
SFML / OS X developer

gsaurus

  • Sr. Member
  • ****
  • Posts: 262
    • View Profile
    • Evolution Engine
The new graphics API in SFML 2
« Reply #32 on: September 15, 2011, 01:04:08 am »
So, it's ok to assume that Draw(sprite) overrides texture with it's own, the problem is the transform (override or combine with the global state is not clear).

A not so elegant solution is to keep transform out of Sprite/Shape/Text, and let user decide how to store/apply transformation. The main purpose of these classes is to hide the manipulation of a mesh & texture after all.
This solution is more verbose, not so intuitive, but it doesn't break with the global states concept (and can encourage MVC by keeping sprite independent of transform).

Often users doesn't even need rotation (n)or scale, so they can have their own class with their own x,y fields if they want, and create a Transform on the fly (though keeping a Transform object is just the same)
Also often they use constants to draw static stuff like background sprites. Sometimes they just want to draw something in some place, they don't really want to keep a transform state.

Code: [Select]
class MyCharacter{
    ...
    sf::Transform transform;
    sf::Sprite representation;
    ...
};

...

window.SetTransform( myChar.GetTransform() );
window.Draw( myChar.GetRepresentation() );

sf::Sprite background;
window.SetTransform( sf::Transform( 100, 200) );
window.Draw(background);


The drawback is that we're applying the transform to the window, instead of to the sprite, which is weird for beginners.

I'm just pointing alternatives so we can think about it, not necessarily suggesting them as if they were better.

I think we'll have to abdicate of something for a reasonable solution :?
Pluma - Plug-in Management Framework

Disch

  • Full Member
  • ***
  • Posts: 220
    • View Profile
The new graphics API in SFML 2
« Reply #33 on: September 15, 2011, 04:48:03 am »
Quote from: "Laurent"
So a sprite would have to declare a getter/setter for every possible state: shader, view, transform, texture, blend mode.


Doesn't it already have that?  I thought the point here was to keep the current Sprite API as close to what it is now as possible while allowing the new lower-level functionality.

Quote
I don't think that getting rid of global states is a good idea. It's more flexible:


It's certainly flexible, but it's not very OO.  A good OO design can be just as flexible with less "gotcha!"  Perhaps don't remove it entirely, but I think you should definitely try to minimize it.

The whole idea of "global" is very "blech" to me.

Quote
some states are better activated once, globally, than assigned to every entity (view, shader)


I agree that View shouldn't be part of the RenderState class idea.  That's something that should be associated with a target.

It seems perfectly reasonable to me to have shaders associated with individual objects.  But if not, then keep them like they are now.  Separate things that you can optionally provide when you draw an object.

Quote
you can combine a transform with the current one (parent/child relationship)


I'm not sure I understand what you mean by this.  It sounds like you're saying you want to combine sf::Sprite's transform with the global transform -- but doesn't that kind of defeat the point?

Quote
- an object can set the states that it needs, while enjoying other states that it doesn't care about


Are you talking about high level objects (Sprite,Text) or low level ones (Mesh)?  I'm not sure I follow this either  =x



Maybe my idea wasn't really clear.  Let me try to better explain.

Sprite/Text API would stay pretty much the same as they are now.  You'd set them up and draw them to a RenderTarget.  They would be subject to further transformation by the target's View, but that's it.

The lower level API would require the user to keep a RenderState object and supply it whenever a mesh is drawn.  The mesh would be subject to transformations by the RenderState and by the target's View.  However since this RenderState does not have anything to do with any Sprites/Text, it wouldn't impact how those are drawn.


So to recap:

Overall association:
------------
View -> associated with RenderTarget
Color, Texture, Transform, Blend Mode -> associated with RenderState
Shader -> independent, applied per Draw() call as it is now


High level objects (Sprite/Text):
------------
Have an internal Mesh, RenderState.  Drawing a sprite would probably just be a wrap around drawing its interal mesh with its internal RenderState


Low level objects (Mesh):
------------
Need to have a RenderState supplied when drawing.  A shader could also optionally be supplied.


How it works:
--------------
You don't need to change any GL states between Draw calls unless the given RenderState has changed.  This means any number of Meshes can be drawn, all sharing the same RenderState, and the only thing that needs to be changed is the vertex/texcoord/etc lists you pass to OpenGL.

The higher level API still works as it did before.  The tradeoff of course being that you are constantly changing the render state for the sake of a simplified API.

What's more, since the RenderState embodies all the transformations, the high level API and the low level API operate independently, so you don't have to worry about the low level API "butting heads" with the high level API and causing unexpected transformations or other surprising/unpredicted render states.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
The new graphics API in SFML 2
« Reply #34 on: September 15, 2011, 08:14:15 am »
Quote
A not so elegant solution is to keep transform out of Sprite/Shape/Text, and let user decide how to store/apply transformation. The main purpose of these classes is to hide the manipulation of a mesh & texture after all.

This is a valid solution, but the transform part of the current API (set/get position/rotation/scale/origin) is also what makes the S of SFML, I feel like dropping it would be too abrupt.

Quote
Doesn't it already have that?

No, current classes only have accessors for their relevant states, not all possible ones.

Quote
I thought the point here was to keep the current Sprite API as close to what it is now as possible while allowing the new lower-level functionality.

No! The point is to find something new that's much better than the current API. Ok, maybe the final result will be something very close to it, but please try to be as open as possible ;)
If the goal was just to reimplement the current API... well it's already done.

Quote
It's certainly flexible, but it's not very OO. A good OO design can be just as flexible with less "gotcha!" Perhaps don't remove it entirely, but I think you should definitely try to minimize it.

This is something that I don't like (global states), so if we can find an alternative solution that doesn't remove this flexibility I'm ready to get rid of them ;)

Quote
I'm not sure I understand what you mean by this. It sounds like you're saying you want to combine sf::Sprite's transform with the global transform -- but doesn't that kind of defeat the point?

I'm not talking about sprites. It's a very common use case to draw hierarchical objects, ie. combine an object's (the child) transform with the current one (set by the parent).

Quote
Are you talking about high level objects (Sprite,Text) or low level ones (Mesh)? I'm not sure I follow this either =x

I think we're talking about the low-level API, aren't we? :D

Quote
Maybe my idea wasn't really clear. Let me try to better explain.

It's very clear now, thanks ;)
I think your point of view makes sense and is consistent.

However I have some questions:
- what does the RenderState class look like?
--> how do we avoid get/set transform every time we want to modify it?
--> is the texture a read-only (const) pointer or not?
- why do you keep the shader out of RenderState?
- Sprite::SetTransform, or Sprite::SetPosition/Rotation/etc.?
- isn't it confusing to have 3 different ways to handle states? (ok, I know it's already like this currently)

Quote
You don't need to change any GL states between Draw calls unless the given RenderState has changed. This means any number of Meshes can be drawn, all sharing the same RenderState, and the only thing that needs to be changed is the vertex/texcoord/etc lists you pass to OpenGL.

Like I said, implementing a cache system is not as easy as it looks. Look at all the tricky bugs in SFML 2 that involve the sf::Renderer class...

Quote
What's more, since the RenderState embodies all the transformations, the high level API and the low level API operate independently, so you don't have to worry about the low level API "butting heads" with the high level API and causing unexpected transformations or other surprising/unpredicted render states.

It's indeed an excellent point.

And how would you draw a sprite, by the way? Sprite.Draw(window, shader) or window.Draw(sprite, shader)?
Laurent Gomila - SFML developer

Lo-X

  • Hero Member
  • *****
  • Posts: 618
    • View Profile
    • My personal website, with CV, portfolio and projects
The new graphics API in SFML 2
« Reply #35 on: September 15, 2011, 02:16:33 pm »
Quote from: "Laurent"
And how would you draw a sprite, by the way? Sprite.Draw(window, shader) or window.Draw(sprite, shader)?


The drawing action is not made by the sprite but by the window, no ?
I think that's not logical if the sprite is drawn by itself.

gsaurus

  • Sr. Member
  • ****
  • Posts: 262
    • View Profile
    • Evolution Engine
The new graphics API in SFML 2
« Reply #36 on: September 15, 2011, 02:58:43 pm »
Quote from: "Laurent"
--> how do we avoid get/set transform every time we want to modify it?

I have a slightly different suggestion.
Code: [Select]
sf::RenderState lowLevelState;
lowLevelState.SetTexture( ... );
...
target.SetRenderState(state);
target.DrawMesh(mesh); // or target.Draw(mesh), but target.DrawMesh is more explicit (*)
target.Draw(sprite); // override the render state (and restore it back after drawing)


This way we are explicitly affecting the whole state at once for the low level meshes. Though we know that Sprite has it's own RenderState so there's no doubt that it will be drawn with it independently.
(*) target.DrawMesh makes more explicit the difference between mesh and Sprite/Text (mesh doesn't have it's own RenderState while high level classes does). But just Draw is also ok, it' simpler.


Alternatively create a low level Renderer (again...) to render meshes and handle global state independently of the higher level RenderTarget. Though it's not as low and flat as it was on the previous versions.
Code: [Select]
target.Draw(sprite); // High level API
sf::Renderer renderer = target.GetRenderer(); // access to the low level API)
renderer.SetTransform(...);
rendere.DrawMesh(myMesh);
Pluma - Plug-in Management Framework

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
The new graphics API in SFML 2
« Reply #37 on: September 15, 2011, 05:17:20 pm »
Quote
The drawing action is not made by the sprite but by the window, no ?
I think that's not logical if the sprite is drawn by itself.

Yes, but... this requires a base class (sf::Drawable) with at least a virtual Draw function.
With "sprite.Draw(window)" there's no need for such a class, everybody can write a new class that can be drawn like the existing ones.

Quote
I have a slightly different suggestion.

But you still have to Get/Set the transform before/after modifying it, right? Compared to the case where you directly own the sf::Transform instance.
And how is it better than setting each state independantly? Does it mean that you have to define every state every time you want to change something?

Quote
Alternatively create a low level Renderer (again...) to render meshes and handle global state independently of the higher level RenderTarget. Though it's not as low and flat as it was on the previous versions.

Same here, I can see that it's more complicated but I can't find what advantages it brings :)
Laurent Gomila - SFML developer

Disch

  • Full Member
  • ***
  • Posts: 220
    • View Profile
The new graphics API in SFML 2
« Reply #38 on: September 15, 2011, 06:44:05 pm »
Quote from: "Laurent"
No! The point is to find something new that's much better than the current API. Ok, maybe the final result will be something very close to it, but please try to be as open as possible


Okay.  that was my misunderstanding.  =P

I'll think about this more when I get some free time.

gsaurus

  • Sr. Member
  • ****
  • Posts: 262
    • View Profile
    • Evolution Engine
The new graphics API in SFML 2
« Reply #39 on: September 15, 2011, 08:50:25 pm »
Quote from: "Laurent"
Quote
I have a slightly different suggestion.

But you still have to Get/Set the transform

If you're referring to sprite, no, it has it's own transform.

Both suggestions are attempts to sugar the two APIs to solve this ambiguity:
Code: [Select]
target.SetTransform(...)
target.Draw(sprite); //how does it interact with global transform?

The first suggestion wraps the state properties into one concept (RenderState), as proposed before, but allow it to be applied globally on the RenderTarget, or "localy" by drawing a high level object (which owns it's own RenderState). This is probably not explicit enough, but it was an attempt to make explicit that sprite draws with it's own RenderState, while meshes use the global state. Though if we just want to change one property (like Texture) it's silly to create one RenderState just for that.


On the second I was trying to separate the two APIs: keeping high level operations at RenderTarget and low level at Renderer, to make explicit that RenderTarget.Draw doesn't affect the global state.




Perhaps renaming the SetTransform/SetTexture at RenderTarget would make it explicit? (rename like what? I don't know).

A question, what happens if you have different targets? Each target has it's "global" rendering state? or there is a global state for everything? In that case it would be needed a static class to deal with the state alone (there's mouse & keyboard already, so..)
Pluma - Plug-in Management Framework

Richy19

  • Full Member
  • ***
  • Posts: 190
    • View Profile
The new graphics API in SFML 2
« Reply #40 on: September 15, 2011, 09:38:32 pm »
Laurent, any advances on this or an ETA?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
The new graphics API in SFML 2
« Reply #41 on: September 16, 2011, 08:53:44 am »
Quote
If you're referring to sprite, no, it has it's own transform.

No, I mean:
Code: [Select]
sf::RenderStates states;
...
// I want to rotate
sf::Transform transform = states.GetTransform(); // I have to retrieve the transform first
transform.Rotate(45);
states.SetTransform(transform); // then assign it back


Quote
it was an attempt to make explicit that sprite draws with it's own RenderState, while meshes use the global state

So there no way to mix meshes (global) states and sprites/whatever states? Both APIs should not be 100% separated, they will often be mixed. Because some states have to be shared (view, shader).

Quote
Though if we just want to change one property (like Texture) it's silly to create one RenderState just for that.

Yep, another point against this new layer between states and the RenderTarget.

Quote
On the second I was trying to separate the two APIs: keeping high level operations at RenderTarget and low level at Renderer, to make explicit that RenderTarget.Draw doesn't affect the global state.

In fact, if all we see for high-level objects is 'object.Draw(window)', I think it's already clear enough that this won't change global states. All this function does is to draw the object.

Quote
Perhaps renaming the SetTransform/SetTexture at RenderTarget would make it explicit?

Why?

Quote
A question, what happens if you have different targets? Each target has it's "global" rendering state? or there is a global state for everything?

Each target has its own states. It would be silly to have non-static setters that would set states globally on all instances.

Quote
Laurent, any advances on this or an ETA?

Nothing more than what you can read here.
Laurent Gomila - SFML developer

gsaurus

  • Sr. Member
  • ****
  • Posts: 262
    • View Profile
    • Evolution Engine
The new graphics API in SFML 2
« Reply #42 on: September 16, 2011, 01:59:51 pm »
Quote from: "Laurent"
In fact, if all we see for high-level objects is 'object.Draw(window)', I think it's already clear enough that this won't change global states.

That should do  8). But how is the mesh drawn? window.Draw(mesh), i.e., in opposite to the high level object.Draw(window)? :roll:

Quote
Each target has its own states. It would be silly to have non-static setters that would set states globally on all instances.

If it was global on all instances, all the state setters was going to be in a static class, like Keyboard/Mouse, the global states manipulation was going to be all separated from RenderTarget. That would allow one to use the same texture to draw on several images, etc. But that's not really an issue :lol:, I like the states by target strategy, it makes it more OO :wink:
Pluma - Plug-in Management Framework

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
The new graphics API in SFML 2
« Reply #43 on: September 16, 2011, 02:52:29 pm »
Quote
If it was global on all instances, all the state setters was going to be in a static class, like Keyboard/Mouse, the global states manipulation was going to be all separated from RenderTarget.
How would you be able to draw something in a render image while you're drawing stuff in the window at the same time ? It seems quite complicated because you have to first save somehow the render state you're using to draw in the window, then set the render state for the render image and finally set the first render state back to continue drawing in the window. It might even be impossible if you're using more than one thread.

If you're planing to use sprite.Draw(target) I suggest renaming Draw to something else (like DrawOn, for example).

Also, mixing target.Draw(mesh) and sprite.Draw(target) is confusing I think. Why sprites would be drawn differently ? (Or why mesh would be drawn differently ?)

If you want to keep "target.Draw(xyz)" syntax and to express the fact that sprite and mesh are not drawn in the same render state, maybe (and only maybe) having "target.DrawMesh(mesh)" and "target.Draw(sprite)" (or any pair of two different names) could be a solution. This could imply something like "target.SetMeshTransform(...)" for meshes and "target.SetTransform" for sprites, ... and so on for any render state properties (*). It's more verbose, OK, but it can (if the methods' name are well chosen) express the fact that mesh's render state are not shared with sprite's render state.

(*) except for texture : if sprite owns their texture we can have "target.SetMeshTexture" and "sprite.SetTexture".

Is renaming methods while keeping most of the design exposed in Laurent's first post a valid solution in your opinion ?
SFML / OS X developer

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
The new graphics API in SFML 2
« Reply #44 on: September 16, 2011, 04:36:20 pm »
Quote
But how is the mesh drawn? window.Draw(mesh), i.e., in opposite to the high level object.Draw(window)?

It's a detail, I don't think we should waste too much time on it.

Quote
If you want to keep "target.Draw(xyz)" syntax and to express the fact that sprite and mesh are not drawn in the same render state, maybe (and only maybe) having "target.DrawMesh(mesh)" and "target.Draw(sprite)" (or any pair of two different names) could be a solution

Instead of talking about functions names, we should discuss the design first ;)
Why do you think that meshes and high-level objects shouldn't share the same states? I had the feeling that it would be better if they were mixed, high-level objects would just be a layer on top of the low-level API instead of a separate layer.
Laurent Gomila - SFML developer