X-Git-Url: http://nitlanguage.org diff --git a/lib/gamnit/flat/flat_core.nit b/lib/gamnit/flat/flat_core.nit index cd1d11d..800b3de 100644 --- a/lib/gamnit/flat/flat_core.nit +++ b/lib/gamnit/flat/flat_core.nit @@ -26,8 +26,6 @@ import gamnit intrude import gamnit::cameras intrude import gamnit::cameras_cache import gamnit::dynamic_resolution -import gamnit::limit_fps -import gamnit::camera_control # Visible 2D entity in the game world or UI # @@ -243,6 +241,32 @@ class Sprite tint_direct = value end + # Draw order, higher values cause this sprite to be drawn latter + # + # Change this value to avoid artifacts when drawing non-opaque sprites. + # In general, sprites with a non-opaque `texture` and sprites closer to + # the camera should have a higher value to be drawn last. + # + # Sprites sharing a `draw_order` are drawn in the same pass. + # The sprite to sprite draw order is undefined and may change when adding + # and removing sprites, or changing their attributes. + # + # ### Warning + # + # Changing this value may have a negative performance impact if there are + # many different `draw_order` values across many sprites. + # Sprites sharing some attributes are drawn as group to reduce + # the communication overhead between the CPU and GPU, + # and changing `draw_order` may break up large groups into smaller groups. + var draw_order = 0 is writable(draw_order_direct=) + + # Set draw order, see `draw_order` + fun draw_order=(value: Int) + do + if isset _draw_order and value != draw_order then needs_remap + draw_order_direct = value + end + # Is this sprite static and added in bulk? # # Set to `true` to give a hint to the framework that this sprite won't @@ -354,10 +378,10 @@ redef class App var ui_camera = new UICamera(app.display.as(not null)) is lazy # World sprites drawn as seen by `world_camera` - var sprites: Set[Sprite] = new SpriteSet + var sprites = new SpriteSet # UI sprites drawn as seen by `ui_camera`, over world `sprites` - var ui_sprites: Set[Sprite] = new SpriteSet + var ui_sprites = new SpriteSet # Main method to refine in clients to update game logic and `sprites` fun update(dt: Float) do end @@ -513,7 +537,7 @@ redef class App # Draw world sprites from `sprites` protected fun frame_core_world_sprites(display: GamnitDisplay) do - frame_core_sprites(display, sprites.as(SpriteSet), world_camera) + frame_core_sprites(display, sprites, world_camera) end # Draw UI sprites from `ui_sprites` @@ -522,7 +546,7 @@ redef class App # Reset only the depth buffer glClear gl_DEPTH_BUFFER_BIT - frame_core_sprites(display, ui_sprites.as(SpriteSet), ui_camera) + frame_core_sprites(display, ui_sprites, ui_camera) end end @@ -851,26 +875,26 @@ redef class OffsetPoint3d end # Set of sprites sorting them into different `SpriteContext` -private class SpriteSet +class SpriteSet super HashSet[Sprite] - # Map texture then static vs dynamic to a `SpriteContext` - var contexts_map = new HashMap3[RootTexture, nullable RootTexture, Bool, Array[SpriteContext]] - - # Contexts in `contexts_map` - var contexts_items = new Array[SpriteContext] - - # Sprites needing resorting in `contexts_map` - var sprites_to_remap = new Array[Sprite] - # Animation speed multiplier (0.0 to pause, 1.0 for normal speed, etc.) var time_mod = 1.0 is writable # Seconds elapsed since the launch of the program, in world time responding to `time_mod` var time = 0.0 + # Map texture then static vs dynamic to a `SpriteContext` + private var contexts_map = new HashMap4[RootTexture, nullable RootTexture, Bool, Int, Array[SpriteContext]] + + # Contexts in `contexts_map`, sorted by draw order + private var contexts_items = new Array[SpriteContext] + + # Sprites needing resorting in `contexts_map` + private var sprites_to_remap = new Array[Sprite] + # Add a sprite to the appropriate context - fun map_sprite(sprite: Sprite) + private fun map_sprite(sprite: Sprite) do assert sprite.context == null else print_error "Sprite {sprite} belongs to another SpriteSet" @@ -879,7 +903,8 @@ private class SpriteSet var animation = sprite.animation var animation_texture = if animation != null then animation.frames.first.root else null - var contexts = contexts_map[texture, animation_texture, sprite.static] + var draw_order = sprite.draw_order + var contexts = contexts_map[texture, animation_texture, sprite.static, draw_order] var context = null if contexts != null then @@ -894,15 +919,17 @@ private class SpriteSet if context == null then var usage = if sprite.static then gl_STATIC_DRAW else gl_DYNAMIC_DRAW - context = new SpriteContext(texture, animation_texture, usage) + context = new SpriteContext(texture, animation_texture, usage, draw_order) if contexts == null then contexts = new Array[SpriteContext] - contexts_map[texture, animation_texture, sprite.static] = contexts + contexts_map[texture, animation_texture, sprite.static, draw_order] = contexts end contexts.add context + contexts_items.add context + sprite_draw_order_sorter.sort(contexts_items) end context.sprites.add sprite @@ -919,7 +946,7 @@ private class SpriteSet end # Remove a sprite from its context - fun unmap_sprite(sprite: Sprite) + private fun unmap_sprite(sprite: Sprite) do var context = sprite.context assert context != null @@ -930,14 +957,16 @@ private class SpriteSet end # Draw all sprites by all contexts - fun draw + private fun draw do + # Remap sprites that may need to change context for sprite in sprites_to_remap do unmap_sprite sprite map_sprite sprite end sprites_to_remap.clear + # Sort by draw order for context in contexts_items do context.draw end @@ -990,6 +1019,9 @@ private class SpriteContext # OpenGL ES usage of `buffer_array` and `buffer_element` var usage: GLBufferUsage + # Draw order shared by all `sprites` + var draw_order: Int + # Sprites drawn by this context var sprites = new GroupedSprites @@ -1179,8 +1211,12 @@ private class SpriteContext data[o+36] = tc[v*2+1] # a_tex_diff - var dx = animation.frames[1].texture_coords[0] - animation.frames[0].texture_coords[0] - var dy = animation.frames[1].texture_coords[1] - animation.frames[0].texture_coords[1] + var dx = 0.0 + var dy = 0.0 + if animation.frames.length > 1 then + dx = animation.frames[1].texture_coords[0] - animation.frames[0].texture_coords[0] + dy = animation.frames[1].texture_coords[1] - animation.frames[0].texture_coords[1] + end data[o+37] = dx data[o+38] = dy @@ -1698,3 +1734,20 @@ redef class NativeGLfloatArray self[i+dst_offset] = (GLfloat)matrix[i]; `} end + +redef class Sys + private var sprite_draw_order_sorter = new DrawOrderComparator is lazy +end + +# Sort `SpriteContext` by their `draw_order` +private class DrawOrderComparator + super Comparator + + # This class can't set COMPARED because + # `the public property cannot contain the private type...` + #redef type COMPARED: SpriteContext + + # Require: `a isa SpriteContext and b isa SpriteContext` + redef fun compare(a, b) + do return a.as(SpriteContext).draw_order <=> b.as(SpriteContext).draw_order +end