DYNAMIC LIGHT OMG MEOW MEOW MEOW

This commit is contained in:
Issac332 2026-07-01 00:00:07 +03:00
parent 6d95f72376
commit e33ba85c03
33 changed files with 851 additions and 81 deletions

View file

@ -4,7 +4,7 @@ include(FindPackageHandleStandardArgs)
# Options # Options
option(STEAM "Build for Steam" OFF) 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(NATIVE "Use native instructions(for local use only)" OFF)
option(MODLOADER "Build with modloader" ON) option(MODLOADER "Build with modloader" ON)
@ -77,6 +77,7 @@ set(MAIN_HEADERS
src/meow.h src/meow.h
src/modloader.h src/modloader.h
src/sunshine.h src/sunshine.h
src/lightmap.h
) )
set(MAIN_SOURCE set(MAIN_SOURCE
@ -122,6 +123,7 @@ set(MAIN_SOURCE
src/meow.cpp src/meow.cpp
src/modloader.cpp src/modloader.cpp
src/sunshine.cpp src/sunshine.cpp
src/lightmap.cpp
) )
if(WIN32) if(WIN32)
@ -194,6 +196,7 @@ set(EMBEDDED_INPUT
shader/simpleColor.frag shader/simpleColor.frag
shader/simpleAlpha.frag shader/simpleAlpha.frag
shader/simpleAlphaUni.frag shader/simpleAlphaUni.frag
shader/dynamicLight.frag
shader/flashMap.frag shader/flashMap.frag
shader/obscured.frag shader/obscured.frag
shader/minimal.vert shader/minimal.vert
@ -287,6 +290,7 @@ set(BINDING_SOURCE
binding-mri/shader-binging.cpp binding-mri/shader-binging.cpp
binding-mri/modloader-binding.cpp binding-mri/modloader-binding.cpp
binding-mri/keybindings-binding.cpp binding-mri/keybindings-binding.cpp
binding-mri/lightmap-binding.cpp
) )
source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS}) source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS})

View file

@ -139,6 +139,7 @@ void steamBindingInit();
void shaderBindingInit(); void shaderBindingInit();
void ModLoaderBindingInit(); void ModLoaderBindingInit();
void keybindingsBindingInit(); void keybindingsBindingInit();
void lightmapBindingInit();
RB_METHOD(mriPrint); RB_METHOD(mriPrint);
RB_METHOD(mriP); RB_METHOD(mriP);
@ -176,6 +177,7 @@ static void mriBindingInit(){
shaderBindingInit(); shaderBindingInit();
ModLoaderBindingInit(); ModLoaderBindingInit();
keybindingsBindingInit(); keybindingsBindingInit();
lightmapBindingInit();
_rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain); _rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain);
_rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop); _rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop);

View file

@ -273,8 +273,8 @@ DEF_PROP_OBJ_VAL(Bitmap, Font, Font, "font")
RB_METHOD(bitmapGradientFillRect){ RB_METHOD(bitmapGradientFillRect){
Bitmap *b = getPrivateData<Bitmap>(self); Bitmap *b = getPrivateData<Bitmap>(self);
VALUE color1Obj, color2Obj; VALUE color1Obj, color2Obj, color3Obj, color4Obj;
Color *color1, *color2; Color *color1, *color2, *color3, *color4;
bool vertical = false; bool vertical = false;
if (argc == 3 || argc == 4){ if (argc == 3 || argc == 4){
@ -289,7 +289,8 @@ RB_METHOD(bitmapGradientFillRect){
color2 = getPrivateDataCheck<Color>(color2Obj, ColorType); color2 = getPrivateDataCheck<Color>(color2Obj, ColorType);
GUARD_EXC( b->gradientFillRect(rect->toIntRect(), color1->norm, color2->norm, vertical); ); GUARD_EXC( b->gradientFillRect(rect->toIntRect(), color1->norm, color2->norm, vertical); );
}else{ }
else if (argc == 6 || argc == 7){
int x, y, width, height; int x, y, width, height;
rb_get_args(argc, argv, "iiiioo|b", &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); ); GUARD_EXC( b->gradientFillRect(x, y, width, height, color1->norm, color2->norm, vertical); );
} }
else if (argc == 5){
return self;
}
RB_METHOD(bitmapGradientFillRect4){
Bitmap *b = getPrivateData<Bitmap>(self);
VALUE color1Obj, color2Obj, color3Obj, color4Obj;
Color *color1, *color2, *color3, *color4;
if (argc == 4 || argc == 5){
VALUE rectObj; VALUE rectObj;
Rect *rect; 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<Rect>(rectObj, RectType); rect = getPrivateDataCheck<Rect>(rectObj, RectType);
color1 = getPrivateDataCheck<Color>(color1Obj, ColorType); color1 = getPrivateDataCheck<Color>(color1Obj, ColorType);
@ -326,7 +317,7 @@ RB_METHOD(bitmapGradientFillRect4){
}else{ }else{
int x, y, width, height; 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<Color>(color1Obj, ColorType); color1 = getPrivateDataCheck<Color>(color1Obj, ColorType);
color2 = getPrivateDataCheck<Color>(color2Obj, ColorType); color2 = getPrivateDataCheck<Color>(color2Obj, ColorType);

View file

@ -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<LightMap>(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<LightMap>(self);
k->clearStaticLightSources();
return Qnil;
}
static VALUE clearDynamicSources(VALUE self) {
LightMap *k = getPrivateData<LightMap>(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<LightMap>(self);
Color *color;
color = getPrivateDataCheck<Color>(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<LightMap>(self);
Color *color;
color = getPrivateDataCheck<Color>(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<LightMap>(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 <LightMap>(klass);
flashableBindingInit <LightMap>(klass);
viewportElementBindingInit<LightMap>(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);
}

View file

@ -47,6 +47,22 @@ module RPG
end end
end end
class Table 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) def self._load(foo)
end end
end end

View file

@ -82,7 +82,8 @@ set(scripts
i18n_Language.rb i18n_Language.rb
Window_TPtL.rb Window_TPtL.rb
Settings.rb Settings.rb
Input.rb) Input.rb
DynamicLight.rb)
find_program(RUBY_EXE ruby find_program(RUBY_EXE ruby
DOC "Location of the Ruby executable") DOC "Location of the Ruby executable")

View file

@ -11,6 +11,8 @@ module Settings
:colorblind => false, :colorblind => false,
:frameskip => true, :frameskip => true,
:twm_shader => true, :twm_shader => true,
:light => true,
:light_shadows => true,
# UI # UI
:in_game_timer => false, :in_game_timer => false,
@ -153,6 +155,17 @@ class Window_Settings
:name => tr('World machine shader'), :name => tr('World machine shader'),
:parameter => :twm_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") => [ tr("UI") => [
{ {

View file

@ -34,7 +34,7 @@ class Window_Settings
@selection_sprite = Sprite.new(@viewport) @selection_sprite = Sprite.new(@viewport)
@selection_sprite.bitmap = Bitmap.new(PARAMETER_WIDTH + 16, PARAMETER_HEIGHT) @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.x = @x - 8
@selection_sprite.y = @offset @selection_sprite.y = @offset
@selection_sprite.opacity = 0 @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.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.y = SCREENS_PANELS_MARGIN + SCREENS_PANELS_TOP_MARGIN
@screen_panel_selection_sprite.x = Graphics.width / 2 @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 = Sprite.new(@viewport)
@switch_panels_hint_left.bitmap = Bitmap.new(PARAMETER_WIDTH / 2 - SCREENS_PANELS_MARGIN, @screen_panel_selection_sprite.height / 2) @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_x = @switch_panels_hint_right.zoom_x =
@switch_panels_hint_left.zoom_y = @switch_panels_hint_right.zoom_y = 2 @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.opacity = @switch_panels_hint_right.opacity = 127
@switch_panels_hint_left.blend_type = @switch_panels_hint_right.blend_type = 1
redraw_panels_hints redraw_panels_hints

123
scripts/DynamicLight.rb Normal file
View file

@ -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

View file

@ -241,13 +241,13 @@ class Game_Event < Game_Character
def update def update
super super
if(@made_text == false && @event.name.start_with?("@text")) if(@made_text == false && @event.name.start_with?("@text"))
if @list.size > 1 && @list[0].code == 101 if @list.size > 1 && @list[0].code == 101
$scene.new_maptext(Language.tr(@list[0].parameters[0].strip), @x, @y) $scene.new_maptext(Language.tr(@list[0].parameters[0].strip), @x, @y)
@list.delete_at(0) @list.delete_at(0)
end end
@made_text = true @made_text = true
end end
# Automatic event starting determinant # Automatic event starting determinant
check_event_trigger_auto check_event_trigger_auto
# If parallel process is valid # If parallel process is valid

View file

@ -264,7 +264,7 @@ class Game_Map
# distance : scroll distance # distance : scroll distance
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
def scroll_down(distance) def scroll_down(distance)
if self.height < Graphics.height / 28 if self.height < Graphics.height / 28
@display_y = self.height / 2 @display_y = self.height / 2
else else
@display_y = [@display_y + distance, (self.height - Graphics.height / 28) * 128].min @display_y = [@display_y + distance, (self.height - Graphics.height / 28) * 128].min
@ -289,10 +289,10 @@ class Game_Map
# distance : scroll distance # distance : scroll distance
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
def scroll_up(distance) def scroll_up(distance)
if self.height < Graphics.height / 28 if self.height < Graphics.height / 28
@display_y = self.height / 2 @display_y = self.height / 2
else else
@display_y = [@display_y - distance, 0].max @display_y = [@display_y - distance, 0].max
end end
end end
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------

View file

@ -8,4 +8,35 @@ module Input
return normalized < Settings[:gamepad_deadzone] / 10.0 ? 0.0 : normalized return normalized < Settings[:gamepad_deadzone] / 10.0 ? 0.0 : normalized
end end
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 end

View file

@ -438,22 +438,22 @@ class Scene_Map
def new_footprint(direction, x, y) def new_footprint(direction, x, y)
if @spriteset != nil if @spriteset != nil
@spriteset.new_footprint(direction, x, y) @spriteset.new_footprint(direction, x, y)
end end
end end
def new_footsplash(direction, x, y) def new_footsplash(direction, x, y)
if @spriteset != nil if @spriteset != nil
@spriteset.new_footsplash(direction, x, y) @spriteset.new_footsplash(direction, x, y)
end end
end end
def new_maptext(text, x, y) def new_maptext(text, x, y)
if @spriteset != nil if @spriteset != nil
@spriteset.new_maptext(text, x, y) @spriteset.new_maptext(text, x, y)
end end
end end
def fix_footsplashes(xDelt, yDelt) def fix_footsplashes(xDelt, yDelt)
if @spriteset != nil if @spriteset != nil
@spriteset.fix_footsplashes(xDelt, yDelt) @spriteset.fix_footsplashes(xDelt, yDelt)
end end
end end
def menu_open? def menu_open?
@menu.visible || @item_menu.visible @menu.visible || @item_menu.visible

View file

@ -78,13 +78,16 @@ class Spriteset_Map
@timer_sprite = Sprite_Timer.new @timer_sprite = Sprite_Timer.new
# Make lightbulb sprite # Make lightbulb sprite
@bulb = Sprite.new(@viewport_lights) @bulb = Sprite.new(@viewport_lights)
@bulb.x = 0 @bulb.x = 0
if Graphics.width == 1280 if Graphics.width == 1280
@bulb.bitmap = RPG::Cache.light('bulb_16') @bulb.bitmap = RPG::Cache.light('bulb_16')
else else
@bulb.bitmap = RPG::Cache.light('bulb') @bulb.bitmap = RPG::Cache.light('bulb')
end end
@bulb.opacity = has_lightbulb? ? 255 : 0 @bulb.opacity = has_lightbulb? ? 255 : 0
# Make dynamic light
@dynamic_light = DynamicLight.new(@viewport_lights)
$light = @dynamic_light
# Panorama animation timer # Panorama animation timer
@pan_animate_timer = 0 @pan_animate_timer = 0
RPG::Mod.exec_hooks("hooks/Spriteset_Map/init", binding) RPG::Mod.exec_hooks("hooks/Spriteset_Map/init", binding)
@ -116,6 +119,8 @@ class Spriteset_Map
@footprint_sprites.each do |sprite| @footprint_sprites.each do |sprite|
sprite.dispose sprite.dispose
end end
# Dispose of dynamic light
@dynamic_light.dispose
# Dispose of weather # Dispose of weather
@weather.dispose @weather.dispose
# Dispose of picture sprites # Dispose of picture sprites
@ -264,9 +269,9 @@ class Spriteset_Map
@tilemap.oy = $game_map.display_y / 4 @tilemap.oy = $game_map.display_y / 4
@tilemap.update @tilemap.update
# Update panorama plane # Update panorama plane
if $game_map.always_moving if $game_map.always_moving
$game_map.pan_move_offset += 1 $game_map.pan_move_offset += 1
end end
if $game_map.clamped_x 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) 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 @panorama.ox = x < 0.0 ? 0.0 : x
@ -318,25 +323,15 @@ class Spriteset_Map
@character_sprites.each do |sprite| @character_sprites.each do |sprite|
if !sprite.character.is_a?(Game_Event) if !sprite.character.is_a?(Game_Event)
sprite.update sprite.update
elsif
# this is just a check to make sure the sprite is onscreen for game events # this is just a check to make sure the sprite is onscreen for game events
# based on its current width and height # based on its current width and height
# no point in updating the sprite if offscreen # no point in updating the sprite if offscreen
# this greatly increases performance on larger maps # this greatly increases performance on larger maps
if Graphics.width == 1280 elsif ((sprite.character.real_x + (sprite.ox*4) > $game_map.display_x - 128) &&
((sprite.character.real_x + (sprite.ox*4) > Graphics.width - 128) && (sprite.character.real_x - (sprite.ox*4) < $game_map.display_x + ((Graphics.width / 32 + 1) * 128)) &&
(sprite.character.real_x - (sprite.ox*4) < Graphics.width + (21 * 128)) && (sprite.character.real_y + (sprite.oy*4) > ($game_map.display_y) - 128) &&
(sprite.character.real_y + (sprite.oy*4) > (Graphics.height) - 128) && (sprite.character.real_y - (sprite.oy*4) < ($game_map.display_y) + (Graphics.height / 32 * 128)))
(sprite.character.real_y - (sprite.oy*4) < (Graphics.height) + (17 * 128))) sprite.update
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
else else
sprite.update_fast sprite.update_fast
end end
@ -367,6 +362,8 @@ class Spriteset_Map
@bulb.opacity -= 2.125 @bulb.opacity -= 2.125
end end
end end
@dynamic_light.update
# Update particles # Update particles
@particles.update if @particles @particles.update if @particles
# Update timer sprite # Update timer sprite

View file

@ -101,6 +101,7 @@ Doc_Message
Desktop_Message Desktop_Message
Credits_Message Credits_Message
Particles Particles
DynamicLight
EdText EdText
Main Main

42
shader/dynamicLight.frag Normal file
View file

@ -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);
}

View file

@ -48,8 +48,10 @@ void main(){
float noise2 = pnoise(uv2 + vec2(WATER_DISTORTION, -WATER_DISTORTION) * noise); float noise2 = pnoise(uv2 + vec2(WATER_DISTORTION, -WATER_DISTORTION) * noise);
float p2 = pow(noise2 + 0.2, 10.0); 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); 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, 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, 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( float isWater = float(
waterColorCheck == vec4(1, 0, 0, 1) waterColorCheck == vec4(1, 0, 0, 1)

View file

@ -40,7 +40,9 @@ void main(){
float p2 = pow(noise2 + 0.02, 20.0); 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; 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 */ /* Apply opacity */
frag.a *= opacity; frag.a *= opacity;

View file

@ -770,6 +770,11 @@ void Bitmap::setPixel(int x, int y, const Color &color){
GUARD_MEGA; 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 pixel[] = {
(uint8_t) clamp<double>(color.red, 0, 255), (uint8_t) clamp<double>(color.red, 0, 255),
(uint8_t) clamp<double>(color.green, 0, 255), (uint8_t) clamp<double>(color.green, 0, 255),

View file

@ -25,6 +25,7 @@
#include "disposable.h" #include "disposable.h"
#include "etc-internal.h" #include "etc-internal.h"
#include "etc.h" #include "etc.h"
#include "debugwriter.h"
// #include <sigc++/signal.h> // #include <sigc++/signal.h>
#include <sigc++/signal.h> #include <sigc++/signal.h>

View file

@ -62,7 +62,7 @@ public:
#elif __EMSCRIPTEN__ #elif __EMSCRIPTEN__
emscripten_console_log(buf.str().c_str()); emscripten_console_log(buf.str().c_str());
#else #else
std::cerr << buf.str() << "\n"; std::cout << buf.str() << "\n";
#endif #endif
} }

View file

@ -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{ struct Vec2i{
int x, y; int x, y;

View file

@ -68,17 +68,17 @@ void Color::set(double red, double green, double blue, double alpha){
void Color::setRed(double value){ void Color::setRed(double value){
red = value; red = value;
norm.x = clamp<double>(value, 0, 255) / 255; norm.x = value / 255;
} }
void Color::setGreen(double value){ void Color::setGreen(double value){
green = value; green = value;
norm.y = clamp<double>(value, 0, 255) / 255; norm.y = value / 255;
} }
void Color::setBlue(double value){ void Color::setBlue(double value){
blue = value; blue = value;
norm.z = clamp<double>(value, 0, 255) / 255; norm.z = value / 255;
} }
void Color::setAlpha(double value){ void Color::setAlpha(double value){

View file

@ -34,7 +34,8 @@ enum BlendType{
BlendNormal = 0, BlendNormal = 0,
BlendAddition = 1, BlendAddition = 1,
BlendSubstraction = 2 BlendSubstraction = 2,
BlendMultiply = 3
}; };
struct Color : public Serializable{ struct Color : public Serializable{
@ -192,6 +193,27 @@ struct Rect : public Serializable{
sigc::signal<void> valueChanged; sigc::signal<void> 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. /* For internal use.
* All drawable classes have properties of one or more of the above * All drawable classes have properties of one or more of the above
* types, which in an interpreted environment act as independent * types, which in an interpreted environment act as independent

View file

@ -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 _PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);
typedef void (APIENTRYP _PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); 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 _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); typedef void (APIENTRYP _PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
/* Vertex attribute */ /* Vertex attribute */
@ -174,6 +175,7 @@ typedef void (APIENTRYP _PFNGLRELEASESHADERCOMPILERPROC) (void);
GL_FUN(Uniform2f, _PFNGLUNIFORM2FPROC) \ GL_FUN(Uniform2f, _PFNGLUNIFORM2FPROC) \
GL_FUN(Uniform4f, _PFNGLUNIFORM4FPROC) \ GL_FUN(Uniform4f, _PFNGLUNIFORM4FPROC) \
GL_FUN(Uniform1i, _PFNGLUNIFORM1IPROC) \ GL_FUN(Uniform1i, _PFNGLUNIFORM1IPROC) \
GL_FUN(Uniform4i, _PFNGLUNIFORM4IPROC) \
GL_FUN(UniformMatrix4fv, _PFNGLUNIFORMMATRIX4FVPROC) \ GL_FUN(UniformMatrix4fv, _PFNGLUNIFORMMATRIX4FVPROC) \
/* Vertex attribute */ \ /* Vertex attribute */ \
GL_FUN(BindAttribLocation, _PFNGLBINDATTRIBLOCATIONPROC) \ GL_FUN(BindAttribLocation, _PFNGLBINDATTRIBLOCATIONPROC) \

View file

@ -72,7 +72,7 @@ namespace TEX{
} }
static inline void uploadImage(GLsizei width, GLsizei height, const void *data, GLenum format){ 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){ 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){ 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){ 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 #endif // GLUTIL_H

View file

@ -82,6 +82,12 @@ void GLBlendMode::apply(const BlendType &value){
gl.BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, gl.BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE,
GL_ONE, GL_ONE); GL_ONE, GL_ONE);
break; break;
case BlendMultiply :
gl.BlendEquation(GL_FUNC_ADD);
gl.BlendFuncSeparate(GL_DST_COLOR, GL_ZERO,
GL_ZERO, GL_ONE);
break;
} }
} }

256
src/lightmap.cpp Normal file
View file

@ -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 <math.h>
#include <SDL3/SDL_rect.h>
#include <sigc++/connection.h>
#include <boost/chrono.hpp>
struct LightMapPrivate{
Bitmap *bitmap;
Bitmap *wallMap;
int cameraX, cameraY, tilemapOffsetX, tilemapOffsetY;
float ambient;
std::vector<LightSource> staticLightSources;
std::vector<LightSource> dynamicLightSources;
std::vector<LightSource> 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<int>(rect.w, 0, bmSize.x-rect.x);
rect.h = clamp<int>(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<float> 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;
}

50
src/lightmap.h Normal file
View file

@ -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 <boost/chrono.hpp>
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

View file

@ -56,6 +56,7 @@
#include "blurH.vert.xxd" #include "blurH.vert.xxd"
#include "blurV.vert.xxd" #include "blurV.vert.xxd"
#include "obscured.frag.xxd" #include "obscured.frag.xxd"
#include "dynamicLight.frag.xxd"
#include "meow.h" #include "meow.h"
#include "sunshine.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) { void Shader::setTexUniform(GLint location, unsigned unitIndex, TEX::ID texture) {
GLenum texUnit = GL_TEXTURE0 + unitIndex; GLenum texUnit = GL_TEXTURE0 + unitIndex;
gl.ActiveTexture(texUnit); gl.ActiveTexture(texUnit);
gl.BindTexture(GL_TEXTURE_2D, texture.gl); gl.BindTexture(GL_TEXTURE_2D, texture.gl);
gl.Uniform1i(location, unitIndex); 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<LightSource> 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(){ SimpleColorShader::SimpleColorShader(){
INIT_SHADER(simpleColor, simpleColor, SimpleColorShader); INIT_SHADER(simpleColor, simpleColor, SimpleColorShader);

View file

@ -103,6 +103,22 @@ private:
GLint u_texOffsetX; 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<LightSource> 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{ class SimpleColorShader : public ShaderBase{
public: public:
SimpleColorShader(); SimpleColorShader();
@ -353,7 +369,8 @@ private:
X(blt, BltShader, Blt) \ X(blt, BltShader, Blt) \
X(simpleMatrix, SimpleMatrixShader, SimpleMatrix) \ X(simpleMatrix, SimpleMatrixShader, SimpleMatrix) \
X(blur, BlurShader, Blur) \ X(blur, BlurShader, Blur) \
X(obscured, ObscuredShader, Obscured) X(obscured, ObscuredShader, Obscured) \
X(dynamicLight, DynamicLightShader, DynamicLight)
struct ShaderSet{ struct ShaderSet{
#define DECLARE_SHADER(name, type, rb) type name; #define DECLARE_SHADER(name, type, rb) type name;

View file

@ -442,6 +442,9 @@ void Sprite::setBlendType(int type){
case BlendSubstraction : case BlendSubstraction :
p->blendType = BlendSubstraction; p->blendType = BlendSubstraction;
return; return;
case BlendMultiply :
p->blendType = BlendMultiply;
return;
} }
} }

View file

@ -63,12 +63,6 @@ static const int atAreaW = autotileW * 4;
static const int atAreaH = autotileH * autotileCount; static const int atAreaH = autotileH * autotileCount;
static const int tsLaneW = tilesetW / 2; 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: /* Vocabulary:
* *
@ -255,6 +249,9 @@ struct TilemapPrivate {
std::vector<uint8_t> animatedATs; std::vector<uint8_t> animatedATs;
} atlas; } atlas;
int viewpW, viewpH;
size_t zlayersMax;
/* Map viewport position */ /* Map viewport position */
Vec2i viewpPos; Vec2i viewpPos;
@ -262,11 +259,11 @@ struct TilemapPrivate {
SVVector groundVert; SVVector groundVert;
/* ZLayer vertices */ /* ZLayer vertices */
SVVector zlayerVert[zlayersMax]; std::vector<SVVector> zlayerVert;
/* Base quad indices of each zlayer /* Base quad indices of each zlayer
* in the shared buffer */ * in the shared buffer */
size_t zlayerBases[zlayersMax+1]; std::vector<size_t> zlayerBases;
/* Shared buffers for all tiles */ /* Shared buffers for all tiles */
struct{ struct{
@ -285,7 +282,7 @@ struct TilemapPrivate {
/* Scene elements */ /* Scene elements */
struct{ struct{
GroundLayer *ground; GroundLayer *ground;
ZLayer* zlayers[zlayersMax]; std::vector<ZLayer*> zlayers;
/* Used layers out of 'zlayers' (rest is hidden) */ /* Used layers out of 'zlayers' (rest is hidden) */
size_t activeLayers; size_t activeLayers;
Scene::Geometry sceneGeo; Scene::Geometry sceneGeo;
@ -330,8 +327,14 @@ struct TilemapPrivate {
buffersDirty(false), buffersDirty(false),
mapViewportDirty(false), mapViewportDirty(false),
zOrderDirty(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)); SDL_memset(autotiles, 0, sizeof(autotiles));
atlas.animatedATs.reserve(autotileCount); atlas.animatedATs.reserve(autotileCount);
@ -351,6 +354,7 @@ struct TilemapPrivate {
GLMeta::vaoInit(tiles.vao); GLMeta::vaoInit(tiles.vao);
elem.ground = new GroundLayer(this, viewport); elem.ground = new GroundLayer(this, viewport);
elem.zlayers.resize(zlayersMax);
for (size_t i = 0; i < zlayersMax; ++i) for (size_t i = 0; i < zlayersMax; ++i)
elem.zlayers[i] = new ZLayer(this, viewport); elem.zlayers[i] = new ZLayer(this, viewport);
@ -363,8 +367,8 @@ struct TilemapPrivate {
~TilemapPrivate(){ ~TilemapPrivate(){
/* Destroy elements */ /* Destroy elements */
delete elem.ground; delete elem.ground;
for (size_t i = 0; i < zlayersMax; ++i) for (ZLayer* ptr : elem.zlayers)
delete elem.zlayers[i]; delete ptr;
shState->releaseAtlasTex(atlas.gl); shState->releaseAtlasTex(atlas.gl);
@ -820,7 +824,7 @@ struct TilemapPrivate {
* are muted via the 'batchedFlag'. For simplicity, * are muted via the 'batchedFlag'. For simplicity,
* single sized batches are possible. */ * single sized batches are possible. */
void prepareZLayerBatches(){ void prepareZLayerBatches(){
ZLayer *const *zlayers = elem.zlayers; ZLayer *const *zlayers = elem.zlayers.data();
for (size_t i = 0; i < elem.activeLayers; ++i){ for (size_t i = 0; i < elem.activeLayers; ++i){
ZLayer *batchHead = zlayers[i]; ZLayer *batchHead = zlayers[i];