diff --git a/CMakeLists.txt b/CMakeLists.txt index 11ce4d1..a9b1a89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ include(FindPackageHandleStandardArgs) # Options option(STEAM "Build for Steam" OFF) -option(DEBUG "Debug mode" OFF) +option(DEBUG "Debug mode" ON) option(NATIVE "Use native instructions(for local use only)" OFF) option(MODLOADER "Build with modloader" ON) @@ -77,6 +77,7 @@ set(MAIN_HEADERS src/meow.h src/modloader.h src/sunshine.h + src/lightmap.h ) set(MAIN_SOURCE @@ -122,6 +123,7 @@ set(MAIN_SOURCE src/meow.cpp src/modloader.cpp src/sunshine.cpp + src/lightmap.cpp ) if(WIN32) @@ -194,6 +196,7 @@ set(EMBEDDED_INPUT shader/simpleColor.frag shader/simpleAlpha.frag shader/simpleAlphaUni.frag + shader/dynamicLight.frag shader/flashMap.frag shader/obscured.frag shader/minimal.vert @@ -287,6 +290,7 @@ set(BINDING_SOURCE binding-mri/shader-binging.cpp binding-mri/modloader-binding.cpp binding-mri/keybindings-binding.cpp + binding-mri/lightmap-binding.cpp ) source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS}) diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index e047a3f..c9005b4 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -139,6 +139,7 @@ void steamBindingInit(); void shaderBindingInit(); void ModLoaderBindingInit(); void keybindingsBindingInit(); +void lightmapBindingInit(); RB_METHOD(mriPrint); RB_METHOD(mriP); @@ -176,6 +177,7 @@ static void mriBindingInit(){ shaderBindingInit(); ModLoaderBindingInit(); keybindingsBindingInit(); + lightmapBindingInit(); _rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain); _rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop); diff --git a/binding-mri/bitmap-binding.cpp b/binding-mri/bitmap-binding.cpp index e048586..58ef3e1 100644 --- a/binding-mri/bitmap-binding.cpp +++ b/binding-mri/bitmap-binding.cpp @@ -273,8 +273,8 @@ DEF_PROP_OBJ_VAL(Bitmap, Font, Font, "font") RB_METHOD(bitmapGradientFillRect){ Bitmap *b = getPrivateData(self); - VALUE color1Obj, color2Obj; - Color *color1, *color2; + VALUE color1Obj, color2Obj, color3Obj, color4Obj; + Color *color1, *color2, *color3, *color4; bool vertical = false; if (argc == 3 || argc == 4){ @@ -289,7 +289,8 @@ RB_METHOD(bitmapGradientFillRect){ color2 = getPrivateDataCheck(color2Obj, ColorType); GUARD_EXC( b->gradientFillRect(rect->toIntRect(), color1->norm, color2->norm, vertical); ); - }else{ + } + else if (argc == 6 || argc == 7){ int x, y, width, height; rb_get_args(argc, argv, "iiiioo|b", &x, &y, &width, &height, @@ -300,21 +301,11 @@ RB_METHOD(bitmapGradientFillRect){ GUARD_EXC( b->gradientFillRect(x, y, width, height, color1->norm, color2->norm, vertical); ); } - - return self; -} - -RB_METHOD(bitmapGradientFillRect4){ - Bitmap *b = getPrivateData(self); - - VALUE color1Obj, color2Obj, color3Obj, color4Obj; - Color *color1, *color2, *color3, *color4; - - if (argc == 4 || argc == 5){ + else if (argc == 5){ VALUE rectObj; Rect *rect; - rb_get_args(argc, argv, "ooo|b", &rectObj, &color1Obj, &color2Obj, &color3Obj, &color4Obj RB_ARG_END); + rb_get_args(argc, argv, "ooooo", &rectObj, &color1Obj, &color2Obj, &color3Obj, &color4Obj RB_ARG_END); rect = getPrivateDataCheck(rectObj, RectType); color1 = getPrivateDataCheck(color1Obj, ColorType); @@ -326,7 +317,7 @@ RB_METHOD(bitmapGradientFillRect4){ }else{ int x, y, width, height; - rb_get_args(argc, argv, "iiiioo|b", &x, &y, &width, &height, &color1Obj, &color2Obj, &color3Obj, &color4Obj RB_ARG_END); + rb_get_args(argc, argv, "iiiioooo", &x, &y, &width, &height, &color1Obj, &color2Obj, &color3Obj, &color4Obj RB_ARG_END); color1 = getPrivateDataCheck(color1Obj, ColorType); color2 = getPrivateDataCheck(color2Obj, ColorType); diff --git a/binding-mri/lightmap-binding.cpp b/binding-mri/lightmap-binding.cpp new file mode 100644 index 0000000..0c81757 --- /dev/null +++ b/binding-mri/lightmap-binding.cpp @@ -0,0 +1,95 @@ +#include "lightmap.h" +#include "disposable-binding.h" +#include "flashable-binding.h" +#include "sceneelement-binding.h" +#include "viewportelement-binding.h" +#include "binding-util.h" + +DEF_TYPE(LightMap); + +RB_METHOD(lightmapInitialize){ + LightMap *s = viewportElementInitialize(argc, argv, self); + + setPrivateData(self, s); + + /* Wrap property objects */ + s->initDynAttribs(); + + return self; +} + +DEF_PROP_OBJ_REF(LightMap, Bitmap, WallMap, "wallmap") + +DEF_PROP_I(LightMap, CameraX) +DEF_PROP_I(LightMap, CameraY) +DEF_PROP_I(LightMap, TilemapOffsetX) +DEF_PROP_I(LightMap, TilemapOffsetY) + +static VALUE clearStaticSources(VALUE self) { + LightMap *k = getPrivateData(self); + k->clearStaticLightSources(); + return Qnil; +} + +static VALUE clearDynamicSources(VALUE self) { + LightMap *k = getPrivateData(self); + k->clearDynamicLightSources(); + return Qnil; +} + +static VALUE addStaticSource(VALUE self, VALUE rb_x, VALUE rb_y, VALUE rb_power, VALUE rb_radius, VALUE rb_color) { + LightMap *k = getPrivateData(self); + Color *color; + color = getPrivateDataCheck(rb_color, ColorType); + k->addStaticLightSource( LightSource ( + NUM2DBL(rb_x), + NUM2DBL(rb_y), + NUM2DBL(rb_power), + NUM2DBL(rb_radius), + *color + )); + return Qnil; +} + +static VALUE addDynamicSource(VALUE self, VALUE rb_x, VALUE rb_y, VALUE rb_power, VALUE rb_radius, VALUE rb_color) { + LightMap *k = getPrivateData(self); + Color *color; + color = getPrivateDataCheck(rb_color, ColorType); + k->addDynamicLightSource( LightSource ( + NUM2DBL(rb_x), + NUM2DBL(rb_y), + NUM2DBL(rb_power), + NUM2DBL(rb_radius), + *color + )); + return Qnil; +} + +static VALUE setAmbient(VALUE self, VALUE var){ + LightMap *k = getPrivateData(self); + k->setAmbient(NUM2DBL(var)); + return Qnil; +} + +void lightmapBindingInit(){ + VALUE klass = rb_define_class("LightMap", rb_cObject); + rb_define_alloc_func(klass, classAllocate<&LightMapType>); + + disposableBindingInit (klass); + flashableBindingInit (klass); + viewportElementBindingInit(klass); + + _rb_define_method(klass, "initialize", lightmapInitialize); + + INIT_PROP_BIND( LightMap, WallMap, "wallmap" ); + INIT_PROP_BIND( LightMap, CameraX, "camera_x" ); + INIT_PROP_BIND( LightMap, CameraY, "camera_y" ); + INIT_PROP_BIND( LightMap, TilemapOffsetX, "tilemap_offset_x" ); + INIT_PROP_BIND( LightMap, TilemapOffsetY, "tilemap_offset_y" ); + + rb_define_method(klass, "clear_static_sources", clearStaticSources, 0); + rb_define_method(klass, "clear_dynamic_sources", clearDynamicSources, 0); + rb_define_method(klass, "add_static_source", addStaticSource, 5); + rb_define_method(klass, "add_dynamic_source", addDynamicSource, 5); + rb_define_method(klass, "ambient=", setAmbient, 1); +} diff --git a/mklangsrc.rb b/mklangsrc.rb index 872b01b..02a9ccd 100644 --- a/mklangsrc.rb +++ b/mklangsrc.rb @@ -47,6 +47,22 @@ module RPG end end class Table + def each_with_index + x = 0 + y = 0 + z = 0 + while z < self.sizez + while y < self.sizey + while x < self.sizex + yield(self[x, y, z], x, y, z) + x += 1 + end + y += 1 + end + z += 1 + end + end + def self._load(foo) end end diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index c6aae62..a7b86a5 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -82,7 +82,8 @@ set(scripts i18n_Language.rb Window_TPtL.rb Settings.rb - Input.rb) + Input.rb + DynamicLight.rb) find_program(RUBY_EXE ruby DOC "Location of the Ruby executable") diff --git a/scripts/Data_Settings.rb b/scripts/Data_Settings.rb index 87227ec..036e2e1 100644 --- a/scripts/Data_Settings.rb +++ b/scripts/Data_Settings.rb @@ -11,6 +11,8 @@ module Settings :colorblind => false, :frameskip => true, :twm_shader => true, + :light => true, + :light_shadows => true, # UI :in_game_timer => false, @@ -153,6 +155,17 @@ class Window_Settings :name => tr('World machine shader'), :parameter => :twm_shader }, + { :type => :sep, :name => tr("Lighting")}, + { + :type => :bool, + :name => tr('Dynamic Light'), + :parameter => :light, + }, + #{ + # :type => :bool, + # :name => tr('Shadows'), + # :parameter => :light_shadows + #}, ], tr("UI") => [ { diff --git a/scripts/Data_Settings_Classes.rb b/scripts/Data_Settings_Classes.rb index 3ce6b42..326e174 100644 --- a/scripts/Data_Settings_Classes.rb +++ b/scripts/Data_Settings_Classes.rb @@ -34,7 +34,7 @@ class Window_Settings @selection_sprite = Sprite.new(@viewport) @selection_sprite.bitmap = Bitmap.new(PARAMETER_WIDTH + 16, PARAMETER_HEIGHT) - @selection_sprite.bitmap.fill_rect(Rect.new(0, 0, PARAMETER_WIDTH + 16, PARAMETER_HEIGHT), Color.new(255, 255, 255, 64)) + @selection_sprite.bitmap.fill_rect(Rect.new(0, 0, PARAMETER_WIDTH + 16, PARAMETER_HEIGHT), Color.new(255, 255, 255, 128)) @selection_sprite.x = @x - 8 @selection_sprite.y = @offset @selection_sprite.opacity = 0 @@ -46,6 +46,7 @@ class Window_Settings @screen_panel_selection_sprite.bitmap.gradient_fill_rect(0, 0, @screen_panel_selection_sprite.bitmap.width, @screen_panel_selection_sprite.bitmap.height, Color.new(255, 255, 255, 0), Color.new(255, 255, 255, 128), true) @screen_panel_selection_sprite.y = SCREENS_PANELS_MARGIN + SCREENS_PANELS_TOP_MARGIN @screen_panel_selection_sprite.x = Graphics.width / 2 + @screen_panel_selection_sprite.blend_type = 1 @switch_panels_hint_left = Sprite.new(@viewport) @switch_panels_hint_left.bitmap = Bitmap.new(PARAMETER_WIDTH / 2 - SCREENS_PANELS_MARGIN, @screen_panel_selection_sprite.height / 2) @@ -61,6 +62,7 @@ class Window_Settings @switch_panels_hint_left.zoom_x = @switch_panels_hint_right.zoom_x = @switch_panels_hint_left.zoom_y = @switch_panels_hint_right.zoom_y = 2 @switch_panels_hint_left.opacity = @switch_panels_hint_right.opacity = 127 + @switch_panels_hint_left.blend_type = @switch_panels_hint_right.blend_type = 1 redraw_panels_hints diff --git a/scripts/DynamicLight.rb b/scripts/DynamicLight.rb new file mode 100644 index 0000000..73f95d3 --- /dev/null +++ b/scripts/DynamicLight.rb @@ -0,0 +1,123 @@ +class DynamicLight + attr_accessor :done + attr_accessor :plr_light_power + attr_accessor :plr_light_color + + def initialize(viewport) + @done = false # for events + @awaked = false + + @light_npc = [] + + @ambient_light = 255 + @ambient_light_lerped = 255 + + @plr_light_power = 0 + @plr_light_radius = -1.0 + @plr_light_radius_lerped = 5.0 + @plr_light_color = Color.new(255, 255, 255) + + @light_sprite = LightMap.new(viewport) + @light_sprite.wallmap = Bitmap.new($game_map.width, $game_map.height) + + for x in 0...$game_map.width + for y in 0...$game_map.height + @light_sprite.wallmap.set_pixel(x, y, Color.new( + b(light_passable($game_map.data[x, y, 0])), + b(light_passable($game_map.data[x, y, 1])), + b(light_passable($game_map.data[x, y, 2])) + )) + end + end + end + + def dispose + @light_sprite.dispose + end + + def update + @plr_light_radius_lerped = @plr_light_radius_lerped * 0.8 + @plr_light_radius * 0.2 + + @light_sprite.visible = Settings[:light] && @awaked + if (!Settings[:light] && @awaked) + return + end + + @light_sprite.camera_x = $game_map.display_x / 4 + @light_sprite.camera_y = $game_map.display_y / 4 + @light_sprite.tilemap_offset_x = ($game_map.display_x / 128) * 32 - $game_map.display_x / 4 + @light_sprite.tilemap_offset_y = ($game_map.display_y / 128) * 32 - $game_map.display_y / 4 + + @light_sprite.clear_dynamic_sources() + plr_light = [$game_player.real_x / 128.0, $game_player.real_y / 128.0, @plr_light_power, @plr_light_radius_lerped, @plr_light_color] + if has_effect(plr_light) + @light_sprite.add_dynamic_source(plr_light[0], plr_light[1] - 0.5, plr_light[2], plr_light[3], plr_light[4]) + end + + @light_npc.each { |npc_light_source| + npc_light = [npc_light_source[0].real_x / 128.0, npc_light_source[0].real_y / 128.0, npc_light_source[1], npc_light_source[2], npc_light_source[3]] + if has_effect(npc_light) + @light_sprite.add_dynamic_source(npc_light[0], npc_light[1] - 0.5, npc_light[2], npc_light[3], npc_light[4]) + end + } + end + + def light_passable(tile_id) + $game_map.passages[tile_id] & 15 == 0 + end + + def b(val) + val ? 255 : 0 + end + + def add(x, y, power, radius, color = Color.new(255, 255, 255)) + @awaked = true + @light_sprite.add_static_source(x, y, power, radius, color) + end + + def add_npc(npc_id, power, radius, color = Color.new(255, 255, 255)) + @awaked = true + @light_npc << [$game_map.events[npc_id], power, radius, color] + end + + def has_effect(source) + source[2] > 0 && source[3] > 0 && source[4].alpha >= 0 && (source[4].red > 0 || source[4].green > 0 || source[4].blue > 0) + end + + #def remove(x, y) + # @light_sources.each do |sourse| + # if sourse[0] == x && sourse[1] == y + # @light_sources.delete(sourse) + # return true + # end + # end + # return false + #end + + def awake + @awaked = true + end + + def sleep # fully disables dynamic light system + @awaked = false + end + + def ambient_light + @ambient_light + end + def ambient_light=(val) + @ambient_light = val + @light_sprite.ambient = val + @awaked = true + end + + def plr_light_radius + @plr_light_radius + end + def plr_light_radius=(val) + if @plr_light_radius < 0 + @plr_light_radius_lerped = val + end + @plr_light_radius = val + end +end \ No newline at end of file diff --git a/scripts/Game_Event.rb b/scripts/Game_Event.rb index 51b70fc..06e86e5 100644 --- a/scripts/Game_Event.rb +++ b/scripts/Game_Event.rb @@ -241,13 +241,13 @@ class Game_Event < Game_Character def update super - if(@made_text == false && @event.name.start_with?("@text")) - if @list.size > 1 && @list[0].code == 101 + if(@made_text == false && @event.name.start_with?("@text")) + if @list.size > 1 && @list[0].code == 101 $scene.new_maptext(Language.tr(@list[0].parameters[0].strip), @x, @y) - @list.delete_at(0) + @list.delete_at(0) end - @made_text = true - end + @made_text = true + end # Automatic event starting determinant check_event_trigger_auto # If parallel process is valid diff --git a/scripts/Game_Map.rb b/scripts/Game_Map.rb index c89e059..d250ba1 100644 --- a/scripts/Game_Map.rb +++ b/scripts/Game_Map.rb @@ -264,7 +264,7 @@ class Game_Map # distance : scroll distance #-------------------------------------------------------------------------- def scroll_down(distance) - if self.height < Graphics.height / 28 + if self.height < Graphics.height / 28 @display_y = self.height / 2 else @display_y = [@display_y + distance, (self.height - Graphics.height / 28) * 128].min @@ -289,10 +289,10 @@ class Game_Map # distance : scroll distance #-------------------------------------------------------------------------- def scroll_up(distance) - if self.height < Graphics.height / 28 - @display_y = self.height / 2 + if self.height < Graphics.height / 28 + @display_y = self.height / 2 else - @display_y = [@display_y - distance, 0].max + @display_y = [@display_y - distance, 0].max end end #-------------------------------------------------------------------------- diff --git a/scripts/Input.rb b/scripts/Input.rb index 5183933..cf14b13 100644 --- a/scripts/Input.rb +++ b/scripts/Input.rb @@ -8,4 +8,35 @@ module Input return normalized < Settings[:gamepad_deadzone] / 10.0 ? 0.0 : normalized end end + + GAMEPAD_BUTTONS_NAMES = { + "a" => "A", # ▲□Ο + "b" => "B", + "x" => "X", + "y" => "Y", + "lefttringger" => "LT", + "righttringger" => "RT", + "leftshoulder" => "LS", + "rightshoulder" => "RS", + "dpdown" => "D-Pad Down", + "dpleft" => "D-Pad Left", + "dpright" => "D-Pad Right", + "dpup" => "D-Pad Up", + "back" => "Back", + "guide" => "Guide", + "start" => "Start", + "leftstick" => "Left Stick", + "rightstick" => "Right Stick", + "leftpaddle1" => "Left Paddle 1", + "rightpaddle1" => "Right Paddle 1", + "leftpaddle2" => "Left Paddle 2", + "rightpaddle2" => "Right Paddle 2", + "touchpad" => "Touchpad", + "misc1" => "Misc 1", + "misc2" => "Misc 2", + "misc3" => "Misc 3", + "misc4" => "Misc 4", + "misc5" => "Misc 5", + "misc6" => "Misc 6", + } end \ No newline at end of file diff --git a/scripts/Scene_Map.rb b/scripts/Scene_Map.rb index d5bd680..be34609 100644 --- a/scripts/Scene_Map.rb +++ b/scripts/Scene_Map.rb @@ -438,22 +438,22 @@ class Scene_Map def new_footprint(direction, x, y) if @spriteset != nil @spriteset.new_footprint(direction, x, y) - end + end end def new_footsplash(direction, x, y) if @spriteset != nil @spriteset.new_footsplash(direction, x, y) - end + end end def new_maptext(text, x, y) if @spriteset != nil @spriteset.new_maptext(text, x, y) - end + end end def fix_footsplashes(xDelt, yDelt) if @spriteset != nil @spriteset.fix_footsplashes(xDelt, yDelt) - end + end end def menu_open? @menu.visible || @item_menu.visible diff --git a/scripts/Spriteset_Map.rb b/scripts/Spriteset_Map.rb index 97d61c9..32ea97b 100644 --- a/scripts/Spriteset_Map.rb +++ b/scripts/Spriteset_Map.rb @@ -78,13 +78,16 @@ class Spriteset_Map @timer_sprite = Sprite_Timer.new # Make lightbulb sprite @bulb = Sprite.new(@viewport_lights) - @bulb.x = 0 - if Graphics.width == 1280 - @bulb.bitmap = RPG::Cache.light('bulb_16') - else - @bulb.bitmap = RPG::Cache.light('bulb') - end + @bulb.x = 0 + if Graphics.width == 1280 + @bulb.bitmap = RPG::Cache.light('bulb_16') + else + @bulb.bitmap = RPG::Cache.light('bulb') + end @bulb.opacity = has_lightbulb? ? 255 : 0 + # Make dynamic light + @dynamic_light = DynamicLight.new(@viewport_lights) + $light = @dynamic_light # Panorama animation timer @pan_animate_timer = 0 RPG::Mod.exec_hooks("hooks/Spriteset_Map/init", binding) @@ -116,6 +119,8 @@ class Spriteset_Map @footprint_sprites.each do |sprite| sprite.dispose end + # Dispose of dynamic light + @dynamic_light.dispose # Dispose of weather @weather.dispose # Dispose of picture sprites @@ -264,9 +269,9 @@ class Spriteset_Map @tilemap.oy = $game_map.display_y / 4 @tilemap.update # Update panorama plane - if $game_map.always_moving - $game_map.pan_move_offset += 1 - end + if $game_map.always_moving + $game_map.pan_move_offset += 1 + end if $game_map.clamped_x x = ($game_player.real_x.to_f / (($game_map.width - 1) * 128)) * (@panorama.bitmap.width * $game_map.pan_zoom - Graphics.width) @panorama.ox = x < 0.0 ? 0.0 : x @@ -318,25 +323,15 @@ class Spriteset_Map @character_sprites.each do |sprite| if !sprite.character.is_a?(Game_Event) sprite.update - elsif - # this is just a check to make sure the sprite is onscreen for game events # based on its current width and height # no point in updating the sprite if offscreen # this greatly increases performance on larger maps - if Graphics.width == 1280 - ((sprite.character.real_x + (sprite.ox*4) > Graphics.width - 128) && - (sprite.character.real_x - (sprite.ox*4) < Graphics.width + (21 * 128)) && - (sprite.character.real_y + (sprite.oy*4) > (Graphics.height) - 128) && - (sprite.character.real_y - (sprite.oy*4) < (Graphics.height) + (17 * 128))) - sprite.update - else - ((sprite.character.real_x + (sprite.ox*4) > Graphics.width - 128) && - (sprite.character.real_x - (sprite.ox*4) < Graphics.width + (10 * 128)) && - (sprite.character.real_y + (sprite.oy*4) > (Graphics.height) - 128) && - (sprite.character.real_y - (sprite.oy*4) < (Graphics.height) + (6 * 128))) - sprite.update - end + elsif ((sprite.character.real_x + (sprite.ox*4) > $game_map.display_x - 128) && + (sprite.character.real_x - (sprite.ox*4) < $game_map.display_x + ((Graphics.width / 32 + 1) * 128)) && + (sprite.character.real_y + (sprite.oy*4) > ($game_map.display_y) - 128) && + (sprite.character.real_y - (sprite.oy*4) < ($game_map.display_y) + (Graphics.height / 32 * 128))) + sprite.update else sprite.update_fast end @@ -367,6 +362,8 @@ class Spriteset_Map @bulb.opacity -= 2.125 end end + + @dynamic_light.update # Update particles @particles.update if @particles # Update timer sprite diff --git a/scripts/_scripts.txt b/scripts/_scripts.txt index 6285ef8..a06888a 100644 --- a/scripts/_scripts.txt +++ b/scripts/_scripts.txt @@ -101,6 +101,7 @@ Doc_Message Desktop_Message Credits_Message Particles +DynamicLight EdText Main diff --git a/shader/dynamicLight.frag b/shader/dynamicLight.frag new file mode 100644 index 0000000..e53e409 --- /dev/null +++ b/shader/dynamicLight.frag @@ -0,0 +1,42 @@ +uniform vec4 lightSources[64]; +uniform vec4 lightSourcesColors[64]; +uniform int lightSourcesCount; +uniform float ambientLight; + +uniform sampler2D texture; +uniform sampler2D wallMapTexture; + +uniform vec2 wallMapResolution; + +uniform vec2 cameraPosition; +uniform vec2 tileMapOffset; + +uniform vec2 texSizeInv; +varying vec2 v_texCoord; + +const vec2 tileSize = vec2(32, 32); +const float powerBase = 255.0; + +float distance(vec2 a, vec2 b){ + vec2 c = a - b; + return sqrt(c.x * c.x + c.y * c.y); +} + +void main(){ + vec2 screenPoint = v_texCoord / texSizeInv; + vec2 mapPoint = cameraPosition + screenPoint; + vec2 wallmapUV = mapPoint / tileSize / wallMapResolution; + + vec3 light = vec3(0, 0, 0); + + for(int i = 0; i < lightSourcesCount; i++) { + vec4 sourceInfo = lightSources[i]; + vec4 sourceColor = lightSourcesColors[i]; + vec2 lightScreenPoint = (sourceInfo.xy - cameraPosition / tileSize) * tileSize + tileSize / 2.0; + light += sourceInfo.z / powerBase * + sourceColor.rgb * + pow(clamp(1.0 - distance(screenPoint, lightScreenPoint) / 32.0 / sourceInfo.w, 0.0, 1.0), sourceColor.a * 2.0); + } + + gl_FragColor = vec4(light + ambientLight / powerBase, 1.0); +} diff --git a/shader/tilemapWater.frag b/shader/tilemapWater.frag index 35b2c66..052efd2 100644 --- a/shader/tilemapWater.frag +++ b/shader/tilemapWater.frag @@ -48,8 +48,10 @@ void main(){ float noise2 = pnoise(uv2 + vec2(WATER_DISTORTION, -WATER_DISTORTION) * noise); float p2 = pow(noise2 + 0.2, 10.0); - vec4 frag = mix(vec4(waterColor1.rgb, 1.0), vec4(waterColor2.rgb, 1.0), noise1) + vec4(vec3(mix(p, 1.0, textureColor.r)) * waterColor3.rgb, 1.0); - frag += mix(vec4(waterColor1.rgb, 1.0), vec4(waterColor2.rgb, 1.0), noise2) + vec4(vec3(mix(p2, 1.0, textureColor.r)) * waterColor3.rgb, 1.0); + vec4 frag = mix(vec4(waterColor1.rgb, 1.0), vec4(waterColor2.rgb, 1.0), noise1) + vec4(vec3(mix(p, 1.0, textureColor.r)) * waterColor3.rgb, 0.0); + frag += mix(vec4(waterColor1.rgb, 1.0), vec4(waterColor2.rgb, 1.0), noise2) + vec4(vec3(mix(p2, 1.0, textureColor.r)) * waterColor3.rgb, 0.0); + frag.a = clamp(frag.a / 2.0, 0.0, 1.0); + frag.rgb = clamp(frag.rgb, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); float isWater = float( waterColorCheck == vec4(1, 0, 0, 1) diff --git a/shader/water.frag b/shader/water.frag index 68b67fc..54816ee 100644 --- a/shader/water.frag +++ b/shader/water.frag @@ -40,7 +40,9 @@ void main(){ float p2 = pow(noise2 + 0.02, 20.0); vec4 frag = mix(vec4(color.r, color.g, color.b, 1.0) * color.a, vec4(color.r, color.g, color.b, 1.0) * tone.a, noise1) + vec4(p, p, p, 1.0) * tone; - frag += mix(vec4(color.r, color.g, color.b, 1.0) * color.a, vec4(color.r, color.g, color.b, 1.0) * tone.a, noise2) + vec4(p2, p2, p2, 1.0) * tone; + frag += mix(vec4(color.r, color.g, color.b, 1.0) * color.a, vec4(color.r, color.g, color.b, 1.0) * tone.a, noise2) + vec4(p2, p2, p2, 1.0) * tone; + frag.a = clamp(frag.a / 2.0, 0.0, 1.0); + frag.rgb = clamp(frag.rgb, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); /* Apply opacity */ frag.a *= opacity; diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 008dd57..b1184dc 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -770,6 +770,11 @@ void Bitmap::setPixel(int x, int y, const Color &color){ GUARD_MEGA; + if (x < 0 || y < 0 || x >= width() || y >= height()) { + Debug() << "Invalid pixel position " << x << " " << y << " in bitmap " << width() << "x" << height(); + return; + } + uint8_t pixel[] = { (uint8_t) clamp(color.red, 0, 255), (uint8_t) clamp(color.green, 0, 255), diff --git a/src/bitmap.h b/src/bitmap.h index 94e8704..de8e4f1 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -25,6 +25,7 @@ #include "disposable.h" #include "etc-internal.h" #include "etc.h" +#include "debugwriter.h" // #include #include diff --git a/src/debugwriter.h b/src/debugwriter.h index 61ac667..1825e6d 100644 --- a/src/debugwriter.h +++ b/src/debugwriter.h @@ -62,7 +62,7 @@ public: #elif __EMSCRIPTEN__ emscripten_console_log(buf.str().c_str()); #else - std::cerr << buf.str() << "\n"; + std::cout << buf.str() << "\n"; #endif } diff --git a/src/etc-internal.h b/src/etc-internal.h index 49b2390..abc3042 100644 --- a/src/etc-internal.h +++ b/src/etc-internal.h @@ -62,6 +62,22 @@ struct Vec4{ } }; +struct Vec4i{ + int x, y, z, w; + + Vec4i() + : x(0), y(0), z(0), w(0) + {} + + Vec4i(int x, int y, int z, int w) + : x(x), y(y), z(z), w(w) + {} + + bool operator==(const Vec4i &other) const{ + return (x == other.x && y == other.y && z == other.z && w == other.w); + } +}; + struct Vec2i{ int x, y; diff --git a/src/etc.cpp b/src/etc.cpp index 09186af..127457b 100644 --- a/src/etc.cpp +++ b/src/etc.cpp @@ -68,17 +68,17 @@ void Color::set(double red, double green, double blue, double alpha){ void Color::setRed(double value){ red = value; - norm.x = clamp(value, 0, 255) / 255; + norm.x = value / 255; } void Color::setGreen(double value){ green = value; - norm.y = clamp(value, 0, 255) / 255; + norm.y = value / 255; } void Color::setBlue(double value){ blue = value; - norm.z = clamp(value, 0, 255) / 255; + norm.z = value / 255; } void Color::setAlpha(double value){ diff --git a/src/etc.h b/src/etc.h index 779f4a8..151afbb 100644 --- a/src/etc.h +++ b/src/etc.h @@ -34,7 +34,8 @@ enum BlendType{ BlendNormal = 0, BlendAddition = 1, - BlendSubstraction = 2 + BlendSubstraction = 2, + BlendMultiply = 3 }; struct Color : public Serializable{ @@ -192,6 +193,27 @@ struct Rect : public Serializable{ sigc::signal valueChanged; }; + +struct LightSource { + float x; + float y; + float power; + float radius; + Color color; + + LightSource() + : x(0), y(0), power(0), radius(0), color(Color()) + {}; + + LightSource(float x, float y, float power, float radius, Color color) + : x(x), y(y), power(power), radius(radius), color(color) + {}; + + bool hasEffect(){ + return radius > 0.0 && power > 0.0 && color.alpha >= 0.0 && (color.red > 0.0 || color.green > 0.0 || color.blue > 0.0); + } +}; + /* For internal use. * All drawable classes have properties of one or more of the above * types, which in an interpreted environment act as independent diff --git a/src/gl-fun.h b/src/gl-fun.h index 6e2af9c..a13a9fc 100644 --- a/src/gl-fun.h +++ b/src/gl-fun.h @@ -90,6 +90,7 @@ typedef void (APIENTRYP _PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); typedef void (APIENTRYP _PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); typedef void (APIENTRYP _PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (APIENTRYP _PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP _PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (APIENTRYP _PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); /* Vertex attribute */ @@ -174,6 +175,7 @@ typedef void (APIENTRYP _PFNGLRELEASESHADERCOMPILERPROC) (void); GL_FUN(Uniform2f, _PFNGLUNIFORM2FPROC) \ GL_FUN(Uniform4f, _PFNGLUNIFORM4FPROC) \ GL_FUN(Uniform1i, _PFNGLUNIFORM1IPROC) \ + GL_FUN(Uniform4i, _PFNGLUNIFORM4IPROC) \ GL_FUN(UniformMatrix4fv, _PFNGLUNIFORMMATRIX4FVPROC) \ /* Vertex attribute */ \ GL_FUN(BindAttribLocation, _PFNGLBINDATTRIBLOCATIONPROC) \ diff --git a/src/gl-util.h b/src/gl-util.h index d0d7a3d..112799c 100644 --- a/src/gl-util.h +++ b/src/gl-util.h @@ -72,7 +72,7 @@ namespace TEX{ } static inline void uploadImage(GLsizei width, GLsizei height, const void *data, GLenum format){ - gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, GL_UNSIGNED_BYTE, data); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, format, GL_UNSIGNED_BYTE, data); } static inline void uploadSubImage(GLint x, GLint y, GLsizei width, GLsizei height, const void *data, GLenum format){ @@ -80,7 +80,7 @@ namespace TEX{ } static inline void allocEmpty(GLsizei width, GLsizei height){ - gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_HALF_FLOAT, 0); } static inline void setRepeat(bool mode){ @@ -217,4 +217,10 @@ struct TEXFBO{ } }; +#define GL_CHECK() { \ + GLenum e = gl.GetError(); \ + if (e != GL_NO_ERROR) \ + printf("%s:%d -> %x\n", __FILE__, __LINE__, e); \ +} + #endif // GLUTIL_H diff --git a/src/glstate.cpp b/src/glstate.cpp index 8d72445..a206b08 100644 --- a/src/glstate.cpp +++ b/src/glstate.cpp @@ -82,6 +82,12 @@ void GLBlendMode::apply(const BlendType &value){ gl.BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE); break; + + case BlendMultiply : + gl.BlendEquation(GL_FUNC_ADD); + gl.BlendFuncSeparate(GL_DST_COLOR, GL_ZERO, + GL_ZERO, GL_ONE); + break; } } diff --git a/src/lightmap.cpp b/src/lightmap.cpp new file mode 100644 index 0000000..0571519 --- /dev/null +++ b/src/lightmap.cpp @@ -0,0 +1,256 @@ +#include "lightmap.h" + +#include "sharedstate.h" +#include "bitmap.h" +#include "etc.h" +#include "etc-internal.h" +#include "util.h" + +#include "gl-util.h" +#include "quad.h" +#include "transform.h" +#include "shader.h" +#include "glstate.h" +#include "quadarray.h" +#include "config.h" +#include "debugwriter.h" +#include "sunshine.h" + +#include + +#include + +#include +#include + +struct LightMapPrivate{ + + Bitmap *bitmap; + Bitmap *wallMap; + + int cameraX, cameraY, tilemapOffsetX, tilemapOffsetY; + float ambient; + std::vector staticLightSources; + std::vector dynamicLightSources; + std::vector gpuBuffer; + + Quad quad; + Transform trans; + + Rect *srcRect; + sigc::connection srcRectCon; + + IntRect sceneRect; + Vec2i sceneOrig; + + /* Would this sprite be visible on + * the screen if drawn? */ + bool isVisible; + + EtcTemps tmp; + + sigc::connection prepareCon; + + LightMapPrivate() + : bitmap(new Bitmap(shState->graphics().width(), shState->graphics().height())), + wallMap(0), + cameraX(0), + cameraY(0), + tilemapOffsetX(0), + tilemapOffsetY(0), + ambient(255.0), + srcRect(&tmp.rect), + isVisible(false) + + { + sceneRect.x = sceneRect.y = 0; + + updateSrcRectCon(); + + prepareCon = shState->prepareDraw.connect(sigc::mem_fun(this, &LightMapPrivate::prepare)); + + bitmap->ensureNonMega(); + //bitmap->fillRect(0, 0, shState->graphics().width(), shState->graphics().height(), Vec4(0, 0, 0, 1)); + + staticLightSources.reserve(48); + dynamicLightSources.reserve(16); + gpuBuffer.reserve(64); + } + + ~LightMapPrivate(){ + srcRectCon.disconnect(); + prepareCon.disconnect(); + } + + void onSrcRectChange(){ + FloatRect rect = srcRect->toFloatRect(); + Vec2i bmSize; + + if (!nullOrDisposed(bitmap)) + bmSize = Vec2i(bitmap->width(), bitmap->height()); + + /* Clamp the rectangle so it doesn't reach outside + * the bitmap bounds */ + rect.w = clamp(rect.w, 0, bmSize.x-rect.x); + rect.h = clamp(rect.h, 0, bmSize.y-rect.y); + + quad.setTexRect(rect); + + quad.setPosRect(FloatRect(0, 0, rect.w, rect.h)); + } + + void updateSrcRectCon(){ + /* Cut old connection */ + srcRectCon.disconnect(); + /* Create new one */ + srcRectCon = srcRect->valueChanged.connect(sigc::mem_fun(this, &LightMapPrivate::onSrcRectChange)); + } + + void updateVisibility(){ + isVisible = false; + + if (nullOrDisposed(bitmap)) + return; + + /* Compare sprite bounding box against the scene */ + + /* If sprite is zoomed/rotated, just opt out for now + * for simplicity's sake */ + const Vec2 &scale = trans.getScale(); + if (scale.x != 1 || scale.y != 1 || trans.getRotation() != 0){ + isVisible = true; + return; + } + + IntRect self; + self.setPos(trans.getPositionI() - (trans.getOriginI() + sceneOrig)); + self.w = bitmap->width(); + self.h = bitmap->height(); + + isVisible = SDL_HasRectIntersection(&self, &sceneRect); + } + + void prepare(){ + updateVisibility(); + } +}; + +LightMap::LightMap(Viewport *viewport) + : ViewportElement(viewport){ + p = new LightMapPrivate; + onGeometryChange(scene->getGeometry()); + + if (nullOrDisposed(p->bitmap)) + return; + + p->bitmap->ensureNonMega(); + + *p->srcRect = p->bitmap->rect(); + p->onSrcRectChange(); + p->quad.setPosRect(p->srcRect->toFloatRect()); +} + +LightMap::~LightMap(){ + dispose(); +} + +DEF_ATTR_RD_SIMPLE(LightMap, WallMap, Bitmap*, p->wallMap) + +DEF_ATTR_SIMPLE(LightMap, CameraX, int, p->cameraX) +DEF_ATTR_SIMPLE(LightMap, CameraY, int, p->cameraY) +DEF_ATTR_SIMPLE(LightMap, TilemapOffsetX, int, p->tilemapOffsetX) +DEF_ATTR_SIMPLE(LightMap, TilemapOffsetY, int, p->tilemapOffsetY) + +DEF_ATTR_SIMPLE(LightMap, Ambient, float, p->ambient); + +void LightMap::setWallMap(Bitmap *bitmap){ + guardDisposed(); + + if (p->wallMap == bitmap) + return; + + p->wallMap = bitmap; + + if (nullOrDisposed(bitmap)) + return; + + bitmap->ensureNonMega(); +} + +void LightMap::initDynAttribs(){ + p->srcRect = new Rect; + + p->updateSrcRectCon(); +} + +/* Flashable */ +void LightMap::update(){ + guardDisposed(); + + Flashable::update(); +} + +void LightMap::clearStaticLightSources(){ + p->staticLightSources.clear(); +} +void LightMap::clearDynamicLightSources(){ + p->dynamicLightSources.clear(); +} +void LightMap::addStaticLightSource(LightSource source){ + p->staticLightSources.push_back(source); +} +void LightMap::addDynamicLightSource(LightSource source){ + p->dynamicLightSources.push_back(source); +} + +/* SceneElement */ +void LightMap::draw(){ + if (!p->isVisible) + return; + + if (emptyFlashFlag) + return; + + DynamicLightShader &shader = shState->shaders().dynamicLight; + shader.bind(); + shader.applyViewportProj(); + + if (p->wallMap) + shader.setWallMapTexture(p->wallMap->getGLTypes().tex); + shader.setWallMapResolution(p->wallMap->width(), p->wallMap->height()); + shader.setCameraPosition(p->cameraX, p->cameraY); + shader.setTileMapOffset(p->tilemapOffsetX, p->tilemapOffsetY); + + p->gpuBuffer.clear(); + p->gpuBuffer.insert(p->gpuBuffer.end(), p->staticLightSources.begin(), p->staticLightSources.end()); + p->gpuBuffer.insert(p->gpuBuffer.end(), p->dynamicLightSources.begin(), p->dynamicLightSources.end()); + shader.setLightSources(p->gpuBuffer); + shader.setAmbient(p->ambient); + + boost::chrono::high_resolution_clock::time_point currentTime = boost::chrono::high_resolution_clock::now(); + boost::chrono::duration elapsed = currentTime - startTime; + shader.setTime(elapsed.count()); + + glState.blendMode.pushSet(BlendMultiply); + + p->bitmap->bindTex(shader); + + p->quad.draw(); + + glState.blendMode.pop(); +} + +void LightMap::onGeometryChange(const Scene::Geometry &geo){ + /* Offset at which the sprite will be drawn + * relative to screen origin */ + p->trans.setGlobalOffset(geo.offset()); + + p->sceneRect.setSize(geo.rect.size()); + p->sceneOrig = geo.orig; +} + +void LightMap::releaseResources(){ + unlink(); + + delete p; +} \ No newline at end of file diff --git a/src/lightmap.h b/src/lightmap.h new file mode 100644 index 0000000..0071542 --- /dev/null +++ b/src/lightmap.h @@ -0,0 +1,50 @@ +#ifndef SPRITE_H +#define SPRITE_H + +#include "scene.h" +#include "flashable.h" +#include "disposable.h" +#include "viewport.h" +#include "util.h" +#include "shader.h" + +#include + +class Bitmap; + +struct LightMapPrivate; + +class LightMap : public ViewportElement, public Flashable, public Disposable{ +public: + LightMap(Viewport *viewport = 0); + ~LightMap(); + + void update(); + + DECL_ATTR( Bitmap, Bitmap* ) + DECL_ATTR( WallMap, Bitmap* ) + DECL_ATTR( CameraX, int ) + DECL_ATTR( CameraY, int ) + DECL_ATTR( TilemapOffsetX, int ) + DECL_ATTR( TilemapOffsetY, int ) + DECL_ATTR( Ambient, float ) + + void clearStaticLightSources(); + void clearDynamicLightSources(); + void addStaticLightSource(LightSource source); + void addDynamicLightSource(LightSource source); + void initDynAttribs(); + +private: + LightMapPrivate *p; + + void draw(); + void onGeometryChange(const Scene::Geometry &); + + void releaseResources(); + const char *klassName() const { return "lightmap"; } + + ABOUT_TO_ACCESS_DISP +}; + +#endif // SPRITE_H diff --git a/src/shader.cpp b/src/shader.cpp index 8eb72f6..29708b8 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -56,6 +56,7 @@ #include "blurH.vert.xxd" #include "blurV.vert.xxd" #include "obscured.frag.xxd" +#include "dynamicLight.frag.xxd" #include "meow.h" #include "sunshine.h" @@ -200,7 +201,7 @@ void Shader::setVec4Uniform(GLint location, const Vec4 &vec) { void Shader::setTexUniform(GLint location, unsigned unitIndex, TEX::ID texture) { GLenum texUnit = GL_TEXTURE0 + unitIndex; - + gl.ActiveTexture(texUnit); gl.BindTexture(GL_TEXTURE_2D, texture.gl); gl.Uniform1i(location, unitIndex); @@ -275,6 +276,65 @@ void SimpleShader::setTexOffsetX(int value){ } +DynamicLightShader::DynamicLightShader(){ + INIT_SHADER(simple, dynamicLight, DynamicLightShader); + + ShaderBase::init(); + + GET_U(wallMapTexture); + GET_U(wallMapResolution); + GET_U(cameraPosition); + GET_U(tileMapOffset); + GET_U(lightSourcesCount); + GET_U(ambientLight); + + u_lightSources = gl.GetUniformLocation(program, "lightSources[0]"); + u_lightSourcesColors = gl.GetUniformLocation(program, "lightSourcesColors[0]"); +} + +void DynamicLightShader::setWallMapTexture(TEX::ID texture){ + setTexUniform(u_wallMapTexture, 1, texture); +} + +void DynamicLightShader::setWallMapResolution(int x, int y){ + gl.Uniform2f(u_wallMapResolution, x, y); +} + +void DynamicLightShader::setCameraPosition(int x, int y){ + gl.Uniform2f(u_cameraPosition, x, y); +} + +void DynamicLightShader::setTileMapOffset(int x, int y){ + gl.Uniform2f(u_tileMapOffset, x, y); +} + +void DynamicLightShader::setLightSources(std::vector sources){ + gl.Uniform1i(u_lightSourcesCount, sources.size()); + for(int i = 0; i < sources.size(); i++) + { + if (!sources[i].hasEffect()) + continue; + + gl.Uniform4f(u_lightSources + i, + sources[i].x, + sources[i].y, + sources[i].power, + sources[i].radius + ); + gl.Uniform4f(u_lightSourcesColors + i, + sources[i].color.red / 255.0, + sources[i].color.green / 255.0, + sources[i].color.blue / 255.0, + sources[i].color.alpha / 255.0 + ); + } +} + +void DynamicLightShader::setAmbient(float power){ + gl.Uniform1f(u_ambientLight, power); +} + + SimpleColorShader::SimpleColorShader(){ INIT_SHADER(simpleColor, simpleColor, SimpleColorShader); diff --git a/src/shader.h b/src/shader.h index b8b1176..666d703 100644 --- a/src/shader.h +++ b/src/shader.h @@ -103,6 +103,22 @@ private: GLint u_texOffsetX; }; + +class DynamicLightShader : public ShaderBase{ +public: + DynamicLightShader(); + + void setWallMapTexture(TEX::ID texture); + void setWallMapResolution(int x, int y); + void setCameraPosition(int x, int y); + void setTileMapOffset(int x, int y); + void setLightSources(std::vector sources); + void setAmbient(float power); + +private: + GLint u_wallMapTexture, u_wallMapResolution, u_cameraPosition, u_tileMapOffset, u_lightSources, u_lightSourcesCount, u_lightSourcesColors, u_ambientLight; +}; + class SimpleColorShader : public ShaderBase{ public: SimpleColorShader(); @@ -353,7 +369,8 @@ private: X(blt, BltShader, Blt) \ X(simpleMatrix, SimpleMatrixShader, SimpleMatrix) \ X(blur, BlurShader, Blur) \ - X(obscured, ObscuredShader, Obscured) + X(obscured, ObscuredShader, Obscured) \ + X(dynamicLight, DynamicLightShader, DynamicLight) struct ShaderSet{ #define DECLARE_SHADER(name, type, rb) type name; diff --git a/src/sprite.cpp b/src/sprite.cpp index d43370b..e55f8e8 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -442,6 +442,9 @@ void Sprite::setBlendType(int type){ case BlendSubstraction : p->blendType = BlendSubstraction; return; + case BlendMultiply : + p->blendType = BlendMultiply; + return; } } diff --git a/src/tilemap.cpp b/src/tilemap.cpp index 76bfe52..0c18f13 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -63,12 +63,6 @@ static const int atAreaW = autotileW * 4; static const int atAreaH = autotileH * autotileCount; static const int tsLaneW = tilesetW / 2; -/* Map viewport size */ -//TODO: разные значения под разные разрешения экрана для оптимизации -static const int viewpW = 42; -static const int viewpH = 26; - -static const size_t zlayersMax = viewpH + 5; /* Vocabulary: * @@ -255,6 +249,9 @@ struct TilemapPrivate { std::vector animatedATs; } atlas; + int viewpW, viewpH; + size_t zlayersMax; + /* Map viewport position */ Vec2i viewpPos; @@ -262,11 +259,11 @@ struct TilemapPrivate { SVVector groundVert; /* ZLayer vertices */ - SVVector zlayerVert[zlayersMax]; + std::vector zlayerVert; /* Base quad indices of each zlayer * in the shared buffer */ - size_t zlayerBases[zlayersMax+1]; + std::vector zlayerBases; /* Shared buffers for all tiles */ struct{ @@ -285,7 +282,7 @@ struct TilemapPrivate { /* Scene elements */ struct{ GroundLayer *ground; - ZLayer* zlayers[zlayersMax]; + std::vector zlayers; /* Used layers out of 'zlayers' (rest is hidden) */ size_t activeLayers; Scene::Geometry sceneGeo; @@ -330,8 +327,14 @@ struct TilemapPrivate { buffersDirty(false), mapViewportDirty(false), zOrderDirty(false), - tilemapReady(false) + tilemapReady(false), + viewpW(shState->graphics().width() / 32 + 1), + viewpH(shState->graphics().height() / 32 + 2), + zlayersMax(viewpH + 5) { + zlayerVert.resize(zlayersMax); + zlayerBases.resize(zlayersMax + 1); + SDL_memset(autotiles, 0, sizeof(autotiles)); atlas.animatedATs.reserve(autotileCount); @@ -351,6 +354,7 @@ struct TilemapPrivate { GLMeta::vaoInit(tiles.vao); elem.ground = new GroundLayer(this, viewport); + elem.zlayers.resize(zlayersMax); for (size_t i = 0; i < zlayersMax; ++i) elem.zlayers[i] = new ZLayer(this, viewport); @@ -363,8 +367,8 @@ struct TilemapPrivate { ~TilemapPrivate(){ /* Destroy elements */ delete elem.ground; - for (size_t i = 0; i < zlayersMax; ++i) - delete elem.zlayers[i]; + for (ZLayer* ptr : elem.zlayers) + delete ptr; shState->releaseAtlasTex(atlas.gl); @@ -820,7 +824,7 @@ struct TilemapPrivate { * are muted via the 'batchedFlag'. For simplicity, * single sized batches are possible. */ void prepareZLayerBatches(){ - ZLayer *const *zlayers = elem.zlayers; + ZLayer *const *zlayers = elem.zlayers.data(); for (size_t i = 0; i < elem.activeLayers; ++i){ ZLayer *batchHead = zlayers[i];