diff --git a/README.md b/README.md index 9e6f12e..3904257 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ This binding only exists for testing purposes and does nothing (the engine quits * pixman * zlib (only ruby bindings) -mkxp employs Qt's qmake build system, so you'll need to install that beforehand. +mkxp employs Qt's qmake build system, so you'll need to install that beforehand. Alternatively, you can build with cmake (FIXME: add cmake instructions). qmake will use pkg-config to locate the respective include/library paths. If you installed any dependencies into non-standard prefixes, make sure to adjust your `PKG_CONFIG_PATH` variable accordingly. @@ -89,5 +89,5 @@ If a requested font is not found, no error is generated. Instead, a built-in fon To alleviate possible porting of heavily Win32API reliant scripts, I have added certain functionality that you won't find in the RGSS spec. Currently this amounts to the following: * The `Input.press?` family of functions accepts three additional button constants: `::MOUSELEFT`, `::MOUSEMIDDLE` and `::MOUSERIGHT` for the respective mouse buttons. -* The `Input` module has two additional functions, `#mouse_x` and `#mouse_y` to query the mouse pointer position relative to the game window. +* The `Input` module has two additional functions, `#mouse_x` and `#mouse_y` to query the mouse pointer position relative to the game screen. * The `Graphics` module has two additional properties: `fullscreen` represents the current fullscreen mode (`true` = fullscreen, `false` = windowed), `show_cursor` hides the system cursor inside the game window when `false`. diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index da99914..117f3a1 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -88,8 +88,13 @@ static void mriBindingInit() fileIntBindingInit(); +#ifdef RGSS3 + _rb_define_module_function(rb_mKernel, "msgbox", mriPrint); + _rb_define_module_function(rb_mKernel, "msgbox_p", mriP); +#else _rb_define_module_function(rb_mKernel, "print", mriPrint); _rb_define_module_function(rb_mKernel, "p", mriP); +#endif rb_eval_string(module_rpg); diff --git a/binding-mri/binding-util.cpp b/binding-mri/binding-util.cpp index aa798bc..c85a86a 100644 --- a/binding-mri/binding-util.cpp +++ b/binding-mri/binding-util.cpp @@ -237,6 +237,24 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...) break; } + case 'n' : + { + if (argI >= argc) + break; + + ID *sym = va_arg(ap, ID*); + + VALUE symVal = *arg++; + + if (!SYMBOL_P(symVal)) + rb_raise(rb_eTypeError, "Argument %d: Expected symbol", argI); + + *sym = SYM2ID(symVal); + ++argI; + + break; + } + case '|' : opt = true; break; diff --git a/binding-mri/binding-util.h b/binding-mri/binding-util.h index 286c097..2d1577e 100644 --- a/binding-mri/binding-util.h +++ b/binding-mri/binding-util.h @@ -131,7 +131,7 @@ wrapNilProperty(VALUE self, const char *iv) rb_iv_set(self, iv, Qnil); } -/* Implemented: oSszfib| */ +/* Implemented: oSszfibn| */ int rb_get_args(int argc, VALUE *argv, const char *format, ...); diff --git a/binding-mri/bitmap-binding.cpp b/binding-mri/bitmap-binding.cpp index f87d8e2..ad06d6f 100644 --- a/binding-mri/bitmap-binding.cpp +++ b/binding-mri/bitmap-binding.cpp @@ -52,12 +52,14 @@ RB_METHOD(bitmapInitialize) setPrivateData(self, b); /* Wrap properties */ - Font *font = new Font(); - b->setFont(font); - font->setColor(new Color(*font->getColor())); + VALUE fontKlass = rb_const_get(rb_cObject, rb_intern("Font")); + VALUE fontObj = rb_obj_alloc(fontKlass); + rb_obj_call_init(fontObj, 0, 0); - VALUE fontProp = wrapProperty(self, font, "font", FontType); - wrapProperty(fontProp, font->getColor(), "color", ColorType); + Font *font = getPrivateData(fontObj); + b->setFont(font); + + rb_iv_set(self, "font", fontObj); return self; } @@ -202,7 +204,7 @@ RB_METHOD(bitmapGetPixel) return Qnil; ) - Vec4 value; + Color value; GUARD_EXC( value = b->getPixel(x, y); ); Color *color = new Color(value); @@ -223,7 +225,7 @@ RB_METHOD(bitmapSetPixel) color = getPrivateDataCheck(colorObj, ColorType); - GUARD_EXC( b->setPixel(x, y, color->norm); ); + GUARD_EXC( b->setPixel(x, y, *color); ); return self; } diff --git a/binding-mri/font-binding.cpp b/binding-mri/font-binding.cpp index eacea9a..b270170 100644 --- a/binding-mri/font-binding.cpp +++ b/binding-mri/font-binding.cpp @@ -23,6 +23,9 @@ #include "binding-util.h" #include "binding-types.h" #include "exception.h" +#include "sharedstate.h" + +#include DEF_TYPE(Font); @@ -36,14 +39,16 @@ RB_METHOD(fontDoesExist) return rb_bool_new(Font::doesExist(name)); } +RB_METHOD(FontSetName); + RB_METHOD(fontInitialize) { - const char *name = 0; + VALUE name = Qnil; int size = 0; - rb_get_args(argc, argv, "|zi", &name, &size RB_ARG_END); + rb_get_args(argc, argv, "|oi", &name, &size RB_ARG_END); - Font *f = new Font(name, size); + Font *f = new Font(0, size); setPrivateData(self, f); @@ -51,6 +56,13 @@ RB_METHOD(fontInitialize) f->setColor(new Color(*f->getColor())); wrapProperty(self, f->getColor(), "color", ColorType); + if (NIL_P(name)) + name = rb_iv_get(rb_obj_class(self), "default_name"); + + /* Going over the 'name=' function automatically causes + * a possbile name array to be re-verified for existing fonts */ + FontSetName(1, &name, self); + return self; } @@ -77,21 +89,63 @@ RB_METHOD(FontGetName) { RB_UNUSED_PARAM; - Font *f = getPrivateData(self); + return rb_iv_get(self, "name"); +} - return rb_str_new_cstr(f->getName()); +static void +fontSetNameHelper(VALUE self, int argc, VALUE *argv, + const char *nameIv, char *outBuf, size_t outLen) +{ + rb_check_argc(argc, 1); + + VALUE arg = argv[0]; + int type = rb_type(arg); + + // Fixme: in RGSS3, specifying "" (and only that) as font name results in + // no text being drawn (everything else is substituted with Arial I think) + strncpy(outBuf, "", outLen); + + if (type == RUBY_T_STRING) + { + strncpy(outBuf, RSTRING_PTR(arg), outLen); + } + else if (type == RUBY_T_ARRAY) + { + for (long i = 0; i < RARRAY_LEN(arg); ++i) + { + VALUE str = rb_ary_entry(arg, i); + + /* Non-string objects are tolerated (ignored) */ + if (rb_type(str) != RUBY_T_STRING) + continue; + + const char *family = RSTRING_PTR(str); + + /* We only set the core Font object's name attribute + * to the actually existing font name */ + if (!shState->fontState().fontPresent(family)) + continue; + + strncpy(outBuf, family, outLen); + } + } + + /* RMXP doesn't even care if the argument type is + * something other than string/array. Whatever... */ + rb_iv_set(self, nameIv, arg); } RB_METHOD(FontSetName) { Font *f = getPrivateData(self); - VALUE name; - rb_get_args(argc, argv, "S", &name RB_ARG_END); + char result[256]; + fontSetNameHelper(self, argc, argv, "default_name", + result, sizeof(result)); - f->setName(RSTRING_PTR(name)); + f->setName(result); - return name; + return argv[0]; } #undef DEF_PROP_CHK_DISP @@ -124,18 +178,19 @@ DEF_KLASS_PROP(Font, bool, DefaultItalic, "b", rb_bool_new) RB_METHOD(FontGetDefaultName) { RB_UNUSED_PARAM; - return rb_str_new_cstr(Font::getDefaultName()); + + return rb_iv_get(self, "default_name"); } RB_METHOD(FontSetDefaultName) { - RB_UNUSED_PARAM; - VALUE nameObj; - rb_get_args(argc, argv, "S", &nameObj RB_ARG_END); + char result[256]; + fontSetNameHelper(self, argc, argv, "default_name", + result, sizeof(result)); - Font::setDefaultName(RSTRING_PTR(nameObj)); + Font::setDefaultName(result); - return nameObj; + return argv[0]; } RB_METHOD(FontGetDefaultColor) @@ -174,6 +229,7 @@ fontBindingInit() Font::setDefaultColor(new Color(*Font::getDefaultColor())); wrapProperty(klass, Font::getDefaultColor(), "default_color", ColorType); + rb_iv_set(klass, "default_name", rb_str_new_cstr(Font::getDefaultName())); INIT_KLASS_PROP_BIND(Font, DefaultName, "default_name"); INIT_KLASS_PROP_BIND(Font, DefaultSize, "default_size"); diff --git a/binding-mri/graphics-binding.cpp b/binding-mri/graphics-binding.cpp index df92610..a972fb8 100644 --- a/binding-mri/graphics-binding.cpp +++ b/binding-mri/graphics-binding.cpp @@ -99,6 +99,24 @@ RB_METHOD(graphicsFrameReset) DEF_GRA_PROP_I(FrameRate) DEF_GRA_PROP_I(FrameCount) +#ifdef RGSS2 + +RB_METHOD(graphicsWidth) +{ + RB_UNUSED_PARAM; + + return rb_fix_new(shState->graphics().width()); +} + +RB_METHOD(graphicsHeight) +{ + RB_UNUSED_PARAM; + + return rb_fix_new(shState->graphics().height()); +} + +#endif + DEF_GRA_PROP_B(Fullscreen) DEF_GRA_PROP_B(ShowCursor) @@ -120,6 +138,11 @@ void graphicsBindingInit() INIT_GRA_PROP_BIND( FrameRate, "frame_rate" ); INIT_GRA_PROP_BIND( FrameCount, "frame_count" ); +#ifdef RGSS2 + _rb_define_module_function(module, "width", graphicsWidth); + _rb_define_module_function(module, "height", graphicsHeight); +#endif + INIT_GRA_PROP_BIND( Fullscreen, "fullscreen" ); INIT_GRA_PROP_BIND( ShowCursor, "show_cursor" ); } diff --git a/binding-mri/input-binding.cpp b/binding-mri/input-binding.cpp index b224da9..6ff3a7c 100644 --- a/binding-mri/input-binding.cpp +++ b/binding-mri/input-binding.cpp @@ -33,40 +33,45 @@ RB_METHOD(inputUpdate) return Qnil; } +static int getButtonArg(VALUE self, int argc, VALUE *argv) +{ + int num; + +#ifdef RGSS3 + ID sym; + rb_get_args(argc, argv, "n", &sym RB_ARG_END); + + if (rb_const_defined(self, sym)) + num = FIX2INT(rb_const_get(self, sym)); + else + num = 0; +#else + (void) self; + rb_get_args(argc, argv, "i", &num RB_ARG_END); +#endif + + return num; +} + RB_METHOD(inputPress) { - RB_UNUSED_PARAM; + int num = getButtonArg(self, argc, argv); - int num; - rb_get_args(argc, argv, "i", &num RB_ARG_END); - - Input::ButtonCode bc = (Input::ButtonCode) num; - - return rb_bool_new(shState->input().isPressed(bc)); + return rb_bool_new(shState->input().isPressed(num)); } RB_METHOD(inputTrigger) { - RB_UNUSED_PARAM; + int num = getButtonArg(self, argc, argv); - int num; - rb_get_args(argc, argv, "i", &num RB_ARG_END); - - Input::ButtonCode bc = (Input::ButtonCode) num; - - return rb_bool_new(shState->input().isTriggered(bc)); + return rb_bool_new(shState->input().isTriggered(num)); } RB_METHOD(inputRepeat) { - RB_UNUSED_PARAM; + int num = getButtonArg(self, argc, argv); - int num; - rb_get_args(argc, argv, "i", &num RB_ARG_END); - - Input::ButtonCode bc = (Input::ButtonCode) num; - - return rb_bool_new(shState->input().isRepeated(bc)); + return rb_bool_new(shState->input().isRepeated(num)); } RB_METHOD(inputDir4) diff --git a/binding-mri/sprite-binding.cpp b/binding-mri/sprite-binding.cpp index e29c6a4..2d515cd 100644 --- a/binding-mri/sprite-binding.cpp +++ b/binding-mri/sprite-binding.cpp @@ -69,6 +69,39 @@ DEF_PROP_F(Sprite, Angle) DEF_PROP_B(Sprite, Mirror) +#ifdef RGSS2 + +RB_METHOD(spriteWidth) +{ + RB_UNUSED_PARAM; + + Sprite *s = getPrivateData(self); + + int value; + GUARD_EXC( value = s->getWidth(); ) + + return rb_fix_new(value); +} + +RB_METHOD(spriteHeight) +{ + RB_UNUSED_PARAM; + + Sprite *s = getPrivateData(self); + + int value; + GUARD_EXC( value = s->getHeight(); ) + + return rb_fix_new(value); +} + +DEF_PROP_I(Sprite, WaveAmp) +DEF_PROP_I(Sprite, WaveLength) +DEF_PROP_I(Sprite, WaveSpeed) +DEF_PROP_F(Sprite, WavePhase) + +#endif + void spriteBindingInit() { @@ -98,4 +131,14 @@ spriteBindingInit() INIT_PROP_BIND( Sprite, BlendType, "blend_type" ); INIT_PROP_BIND( Sprite, Color, "color" ); INIT_PROP_BIND( Sprite, Tone, "tone" ); + +#ifdef RGSS2 + _rb_define_method(klass, "width", spriteWidth); + _rb_define_method(klass, "height", spriteHeight); + + INIT_PROP_BIND( Sprite, WaveAmp, "wave_amp" ); + INIT_PROP_BIND( Sprite, WaveLength, "wave_length" ); + INIT_PROP_BIND( Sprite, WaveSpeed, "wave_speed" ); + INIT_PROP_BIND( Sprite, WavePhase, "wave_phase" ); +#endif } diff --git a/binding-mruby/bitmap-binding.cpp b/binding-mruby/bitmap-binding.cpp index f000b07..9010d02 100644 --- a/binding-mruby/bitmap-binding.cpp +++ b/binding-mruby/bitmap-binding.cpp @@ -194,7 +194,7 @@ MRB_METHOD(bitmapGetPixel) return mrb_nil_value(); ) - Vec4 value; + Color value; GUARD_EXC( value = b->getPixel(x, y); ) Color *color = new Color(value); @@ -215,7 +215,7 @@ MRB_METHOD(bitmapSetPixel) color = getPrivateDataCheck(mrb, colorObj, ColorType); - GUARD_EXC( b->setPixel(x, y, color->norm); ) + GUARD_EXC( b->setPixel(x, y, *color); ) return mrb_nil_value(); } diff --git a/binding-mruby/etc-binding.cpp b/binding-mruby/etc-binding.cpp index fcbad46..6fae107 100644 --- a/binding-mruby/etc-binding.cpp +++ b/binding-mruby/etc-binding.cpp @@ -24,22 +24,22 @@ #include "binding-types.h" #include "serializable-binding.h" -#define ATTR_RW(Type, attr, arg_type, mrb_val, arg_t_s) \ - MRB_METHOD(Type##Get_##attr) \ +#define ATTR_RW(Type, Attr, arg_type, mrb_val, arg_t_s) \ + MRB_METHOD(Type##Get##Attr) \ { \ Type *p = getPrivateData(mrb, self); \ \ - return mrb_##mrb_val##_value(p->attr); \ + return mrb_##mrb_val##_value(p->get##Attr()); \ } \ \ - MRB_METHOD(Type##Set_##attr) \ + MRB_METHOD(Type##Set##Attr) \ { \ Type *p = getPrivateData(mrb, self); \ \ arg_type arg; \ mrb_get_args(mrb, arg_t_s, &arg); \ \ - p->attr = arg; \ + p->set##Attr(arg); \ UPDATE_F \ \ return mrb_##mrb_val##_value(arg); \ @@ -64,22 +64,22 @@ #define ATTR_INT_RW(Type, attr) ATTR_RW(Type, attr, mrb_int, fixnum, "i") #define UPDATE_F p->updateInternal(); -ATTR_FLOAT_RW(Color, red) -ATTR_FLOAT_RW(Color, green) -ATTR_FLOAT_RW(Color, blue) -ATTR_FLOAT_RW(Color, alpha) +ATTR_FLOAT_RW(Color, Red) +ATTR_FLOAT_RW(Color, Green) +ATTR_FLOAT_RW(Color, Blue) +ATTR_FLOAT_RW(Color, Alpha) -ATTR_FLOAT_RW(Tone, red) -ATTR_FLOAT_RW(Tone, green) -ATTR_FLOAT_RW(Tone, blue) -ATTR_FLOAT_RW(Tone, gray) +ATTR_FLOAT_RW(Tone, Red) +ATTR_FLOAT_RW(Tone, Green) +ATTR_FLOAT_RW(Tone, Blue) +ATTR_FLOAT_RW(Tone, Gray) #undef UPDATE_F #define UPDATE_F -ATTR_INT_RW(Rect, x) -ATTR_INT_RW(Rect, y) -ATTR_INT_RW(Rect, width) -ATTR_INT_RW(Rect, height) +ATTR_INT_RW(Rect, X) +ATTR_INT_RW(Rect, Y) +ATTR_INT_RW(Rect, Width) +ATTR_INT_RW(Rect, Height) EQUAL_FUN(Color) EQUAL_FUN(Tone) @@ -163,15 +163,9 @@ CLONE_FUN(Tone) CLONE_FUN(Color) CLONE_FUN(Rect) -#define MRB_ATTR_R(Class, attr) mrb_define_method(mrb, klass, #attr, Class##Get_##attr, MRB_ARGS_NONE()) -#define MRB_ATTR_W(Class, attr) mrb_define_method(mrb, klass, #attr "=", Class##Set_##attr, MRB_ARGS_REQ(1)) -#define MRB_ATTR_RW(Class, attr) { MRB_ATTR_R(Class, attr); MRB_ATTR_W(Class, attr); } - -#define MRB_ATTR_RW_A(Class, attr, alias) \ -{ \ - mrb_define_method(mrb, klass, #alias, Class##Get_##attr, MRB_ARGS_NONE()); \ - mrb_define_method(mrb, klass, #alias "=", Class##Set_##attr, MRB_ARGS_REQ(1)); \ -} +#define MRB_ATTR_R(Class, Attr, sym) mrb_define_method(mrb, klass, sym, Class##Get##Attr, MRB_ARGS_NONE()) +#define MRB_ATTR_W(Class, Attr, sym) mrb_define_method(mrb, klass, sym "=", Class##Set##Attr, MRB_ARGS_REQ(1)) +#define MRB_ATTR_RW(Class, Attr, sym) { MRB_ATTR_R(Class, Attr, sym); MRB_ATTR_W(Class, Attr, sym); } #define INIT_BIND(Klass) \ { \ @@ -191,21 +185,21 @@ void etcBindingInit(mrb_state *mrb) RClass *klass; INIT_BIND(Color); - MRB_ATTR_RW(Color, red); - MRB_ATTR_RW(Color, green); - MRB_ATTR_RW(Color, blue); - MRB_ATTR_RW(Color, alpha); + MRB_ATTR_RW(Color, Red, "red" ); + MRB_ATTR_RW(Color, Green, "green"); + MRB_ATTR_RW(Color, Blue, "blue" ); + MRB_ATTR_RW(Color, Alpha, "alpha"); INIT_BIND(Tone); - MRB_ATTR_RW(Tone, red); - MRB_ATTR_RW(Tone, green); - MRB_ATTR_RW(Tone, blue); - MRB_ATTR_RW(Tone, gray); + MRB_ATTR_RW(Tone, Red, "red" ); + MRB_ATTR_RW(Tone, Green, "green"); + MRB_ATTR_RW(Tone, Blue, "blue" ); + MRB_ATTR_RW(Tone, Gray, "gray" ); INIT_BIND(Rect); - MRB_ATTR_RW(Rect, x); - MRB_ATTR_RW(Rect, y); - MRB_ATTR_RW(Rect, width); - MRB_ATTR_RW(Rect, height); + MRB_ATTR_RW(Rect, X, "x" ); + MRB_ATTR_RW(Rect, Y, "y" ); + MRB_ATTR_RW(Rect, Width, "width" ); + MRB_ATTR_RW(Rect, Height, "height"); mrb_define_method(mrb, klass, "empty", RectEmpty, MRB_ARGS_NONE()); } diff --git a/binding-mruby/input-binding.cpp b/binding-mruby/input-binding.cpp index 7231c9b..e415e6f 100644 --- a/binding-mruby/input-binding.cpp +++ b/binding-mruby/input-binding.cpp @@ -33,34 +33,44 @@ MRB_FUNCTION(inputUpdate) return mrb_nil_value(); } -MRB_FUNCTION(inputPress) +static mrb_int getButtonArg(mrb_state *mrb, mrb_value self) { mrb_int num; + +#ifdef RGSS3 + mrb_sym sym; + mrb_get_args(mrb, "n", &sym); + + if (mrb_const_defined(mrb, self, sym)) + num = mrb_fixnum(mrb_const_get(mrb, self, sym)); + else + num = 0; +#else mrb_get_args(mrb, "i", &num); +#endif - Input::ButtonCode bc = (Input::ButtonCode) num; - - return mrb_bool_value(shState->input().isPressed(bc)); + return num; } -MRB_FUNCTION(inputTrigger) +MRB_METHOD(inputPress) { - mrb_int num; - mrb_get_args(mrb, "i", &num); + mrb_int num = getButtonArg(mrb, self); - Input::ButtonCode bc = (Input::ButtonCode) num; - - return mrb_bool_value(shState->input().isTriggered(bc)); + return mrb_bool_value(shState->input().isPressed(num)); } -MRB_FUNCTION(inputRepeat) +MRB_METHOD(inputTrigger) { - mrb_int num; - mrb_get_args(mrb, "i", &num); + mrb_int num = getButtonArg(mrb, self); - Input::ButtonCode bc = (Input::ButtonCode) num; + return mrb_bool_value(shState->input().isTriggered(num)); +} - return mrb_bool_value(shState->input().isRepeated(bc)); +MRB_METHOD(inputRepeat) +{ + mrb_int num = getButtonArg(mrb, self); + + return mrb_bool_value(shState->input().isRepeated(num)); } MRB_FUNCTION(inputDir4) diff --git a/binding-mruby/sprite-binding.cpp b/binding-mruby/sprite-binding.cpp index b37feb1..2eabc71 100644 --- a/binding-mruby/sprite-binding.cpp +++ b/binding-mruby/sprite-binding.cpp @@ -68,6 +68,35 @@ DEF_PROP_F(Sprite, Angle) DEF_PROP_B(Sprite, Mirror) +#ifdef RGSS2 + +MRB_METHOD(spriteWidth) +{ + Sprite *s = getPrivateData(mrb, self); + + int value; + GUARD_EXC( value = s->getWidth(); ) + + return mrb_fixnum_value(value); +} + +MRB_METHOD(spriteHeight) +{ + Sprite *s = getPrivateData(mrb, self); + + int value; + GUARD_EXC( value = s->getHeight(); ) + + return mrb_fixnum_value(value); +} + +DEF_PROP_I(Sprite, WaveAmp) +DEF_PROP_I(Sprite, WaveLength) +DEF_PROP_I(Sprite, WaveSpeed) +DEF_PROP_F(Sprite, WavePhase) + +#endif + void spriteBindingInit(mrb_state *mrb) { @@ -95,5 +124,15 @@ spriteBindingInit(mrb_state *mrb) INIT_PROP_BIND( Sprite, Color, "color" ); INIT_PROP_BIND( Sprite, Tone, "tone" ); +#ifdef RGSS2 + mrb_define_method(mrb, klass, "width", spriteWidth, MRB_ARGS_NONE()); + mrb_define_method(mrb, klass, "height", spriteHeight, MRB_ARGS_NONE()); + + INIT_PROP_BIND( Sprite, WaveAmp, "wave_amp" ); + INIT_PROP_BIND( Sprite, WaveLength, "wave_length" ); + INIT_PROP_BIND( Sprite, WaveSpeed, "wave_speed" ); + INIT_PROP_BIND( Sprite, WavePhase, "wave_phase" ); +#endif + mrb_define_method(mrb, klass, "inspect", inspectObject, MRB_ARGS_NONE()); } diff --git a/mkxp.conf.sample b/mkxp.conf.sample index cfe7268..fcb6cef 100644 --- a/mkxp.conf.sample +++ b/mkxp.conf.sample @@ -119,3 +119,18 @@ # (default: disabled) # # useScriptNames=false + + +# Font substitutions allow drop-in replacements of fonts +# to be used without changing the RGSS scripts, +# eg. providing 'Open Sans' when the game thinkgs it's +# using 'Arial'. Font family to be substituted and +# replacement family are separated by one sole '>'. +# Be careful not to include any spaces. +# This is not connected to the built-in font, which is +# always used when a non-existing font family is +# requested by RGSS. +# (default: none) +# +# fontSub=Arial>Open Sans +# fontSub=Times New Roman>Liberation Serif diff --git a/mkxp.pro b/mkxp.pro index 40492ce..6c59957 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -44,6 +44,11 @@ RGSS2 { DEFINES += RGSS2 } +# Requires RGSS2 +RGSS3 { + DEFINES += RGSS3 +} + unix { CONFIG += link_pkgconfig PKGCONFIG += sigc++-2.0 glew pixman-1 zlib physfs \ diff --git a/shader/bitmapBlit.frag b/shader/bitmapBlit.frag index 1913c5b..18be173 100644 --- a/shader/bitmapBlit.frag +++ b/shader/bitmapBlit.frag @@ -20,18 +20,14 @@ void main() vec4 resFrag; - float ab = opacity; - float as = srcFrag.a; - float ad = dstFrag.a; + float co1 = srcFrag.a * opacity; + float co2 = dstFrag.a * (1.0 - co1); + resFrag.a = co1 + co2; - float at = ab*as; - resFrag.a = at + ad - ad*at; - - // Sigh... - if (ad == 0.0) + if (resFrag.a == 0.0) resFrag.rgb = srcFrag.rgb; else - resFrag.rgb = as*srcFrag.rgb + (1.0-at) * ad * dstFrag.rgb; + resFrag.rgb = (co1*srcFrag.rgb + co2*dstFrag.rgb) / resFrag.a; gl_FragColor = resFrag; } diff --git a/src/al-util.h b/src/al-util.h index c083c9a..7b1a85d 100644 --- a/src/al-util.h +++ b/src/al-util.h @@ -23,6 +23,7 @@ #define ALUTIL_H #include +#include #include namespace AL diff --git a/src/audio.cpp b/src/audio.cpp index 04364ec..ead1e6a 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -366,6 +366,7 @@ struct SDLSoundSource : ALDataSource ~SDLSoundSource() { + /* This also closes 'srcOps' */ Sound_FreeSample(sample); } diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 909e11e..b54eba4 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -55,10 +55,6 @@ struct BitmapPrivate { TEXFBO gl; - /* 'setPixel()' calls are cached and executed - * in batches on 'flush()' */ - PointArray pointArray; - Font *font; /* "Mega surfaces" are a hack to allow Tilesets to be used @@ -154,32 +150,9 @@ struct BitmapPrivate glState.blendMode.pop(); } - void flushPoints() - { - if (pointArray.count() == 0) - return; - - SimpleColorShader &shader = shState->shaders().simpleColor; - shader.bind(); - shader.setTranslation(Vec2i()); - - bindFBO(); - pushSetViewport(shader); - glState.blendMode.pushSet(BlendNone); - - pointArray.commit(); - pointArray.draw(); - pointArray.reset(); - - glState.blendMode.pop(); - popViewport(); - } - void fillRect(const IntRect &rect, const Vec4 &color) { - flushPoints(); - bindFBO(); glState.scissorTest.pushSet(true); @@ -271,7 +244,6 @@ Bitmap::Bitmap(const Bitmap &other) p->gl = shState->texPool().request(other.width(), other.height()); - other.flush(); blt(0, 0, other, rect()); } @@ -368,8 +340,6 @@ void Bitmap::stretchBlt(const IntRect &destRect, if (opacity == 255 && !p->touchesTaintedArea(destRect)) { /* Fast blit */ - flush(); - FBO::bind(source.p->gl.fbo, FBO::Read); FBO::bind(p->gl.fbo, FBO::Draw); @@ -379,8 +349,6 @@ void Bitmap::stretchBlt(const IntRect &destRect, else { /* Fragment pipeline */ - flush(); - float normOpacity = (float) opacity / 255.0f; TEXFBO &gpTex = shState->gpTexFBO(destRect.w, destRect.h); @@ -461,8 +429,6 @@ void Bitmap::gradientFillRect(const IntRect &rect, GUARD_MEGA; - flush(); - SimpleColorShader &shader = shState->shaders().simpleColor; shader.bind(); shader.setTranslation(Vec2i()); @@ -520,8 +486,6 @@ void Bitmap::blur() GUARD_MEGA; - flush(); - Quad &quad = shState->gpQuad(); FloatRect rect(0, 0, width(), height()); quad.setTexPosRect(rect, rect); @@ -567,8 +531,6 @@ void Bitmap::radialBlur(int angle, int divisions) GUARD_MEGA; - flush(); - angle = clamp(angle, 0, 359); divisions = clamp(divisions, 2, 100); @@ -666,9 +628,6 @@ void Bitmap::clear() GUARD_MEGA; - /* Any queued points won't be visible after this anyway */ - p->pointArray.reset(); - p->bindFBO(); glState.clearColor.pushSet(Vec4()); @@ -682,7 +641,7 @@ void Bitmap::clear() modified(); } -Vec4 Bitmap::getPixel(int x, int y) const +Color Bitmap::getPixel(int x, int y) const { GUARD_DISPOSED; @@ -691,24 +650,34 @@ Vec4 Bitmap::getPixel(int x, int y) const if (x < 0 || y < 0 || x >= width() || y >= height()) return Vec4(); - flush(); - FBO::bind(p->gl.fbo, FBO::Read); glState.viewport.pushSet(IntRect(0, 0, width(), height())); - Vec4 pixel = FBO::getPixel(x, y); + + uint8_t pixel[4]; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); + glState.viewport.pop(); - return pixel; + return Color(pixel[0], pixel[1], pixel[2], pixel[3]); } -void Bitmap::setPixel(int x, int y, const Vec4 &color) +void Bitmap::setPixel(int x, int y, const Color &color) { GUARD_DISPOSED; GUARD_MEGA; - p->pointArray.append(Vec2(x+.5, y+.5), color); + uint8_t pixel[] = + { + (uint8_t) clamp(color.red, 0, 255), + (uint8_t) clamp(color.green, 0, 255), + (uint8_t) clamp(color.blue, 0, 255), + (uint8_t) clamp(color.alpha, 0, 255) + }; + + TEX::bind(p->gl.tex); + TEX::uploadSubImage(x, y, 1, 1, &pixel, GL_RGBA); p->addTaintedArea(IntRect(x, y, 1, 1)); @@ -724,8 +693,6 @@ void Bitmap::hueChange(int hue) if ((hue % 360) == 0) return; - flush(); - TEXFBO newTex = shState->texPool().request(width(), height()); FloatRect texRect(rect()); @@ -748,8 +715,6 @@ void Bitmap::hueChange(int hue) p->blitQuad(quad); - shader.unbind(); - p->popViewport(); TEX::unbind(); @@ -779,8 +744,6 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align) if (str[0] == ' ' && str[1] == '\0') return; - flush(); - TTF_Font *font = p->font->getSdlFont(); Color *fontColor = p->font->getColor(); @@ -1011,17 +974,6 @@ IntRect Bitmap::textSize(const char *str) DEF_ATTR_SIMPLE(Bitmap, Font, Font*, p->font) -void Bitmap::flush() const -{ - if (isDisposed()) - return; - - if (p->megaSurface) - return; - - p->flushPoints(); -} - TEXFBO &Bitmap::getGLTypes() { return p->gl; diff --git a/src/bitmap.h b/src/bitmap.h index 72048b1..cb491bb 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -81,8 +81,8 @@ public: void clear(); - Vec4 getPixel(int x, int y) const; - void setPixel(int x, int y, const Vec4 &color); + Color getPixel(int x, int y) const; + void setPixel(int x, int y, const Color &color); void hueChange(int hue); @@ -105,9 +105,6 @@ public: DECL_ATTR(Font, Font*) /* */ - /* Warning: Flushing might change the current - * FBO binding (so don't call it during 'draw()' routines */ - void flush() const; TEXFBO &getGLTypes(); SDL_Surface *megaSurface() const; void ensureNonMega() const; diff --git a/src/config.cpp b/src/config.cpp index fcde0c0..6a187ad 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -35,7 +35,6 @@ namespace po = boost::program_options; Config::Config() : debugMode(false), - screenshots(false), winResizable(false), fullscreen(false), fixedAspectRatio(true), @@ -57,7 +56,6 @@ void Config::read(int argc, char *argv[]) { #define PO_DESC_ALL \ PO_DESC(debugMode, bool) \ - PO_DESC(screenshots, bool) \ PO_DESC(winResizable, bool) \ PO_DESC(fullscreen, bool) \ PO_DESC(fixedAspectRatio, bool) \ @@ -85,6 +83,7 @@ void Config::read(int argc, char *argv[]) podesc.add_options() PO_DESC_ALL ("RTP", po::value()->composing()) + ("fontSub", po::value()->composing()) ; po::variables_map vm; @@ -117,6 +116,8 @@ void Config::read(int argc, char *argv[]) GUARD_ALL( rtps = vm["RTP"].as(); ); + GUARD_ALL( fontSubs = vm["fontSub"].as(); ); + #undef PO_DESC #undef PO_DESC_ALL } diff --git a/src/config.h b/src/config.h index d91e13a..b2e3218 100644 --- a/src/config.h +++ b/src/config.h @@ -28,7 +28,6 @@ struct Config { bool debugMode; - bool screenshots; bool winResizable; bool fullscreen; @@ -56,6 +55,8 @@ struct Config std::string customScript; std::vector rtps; + std::vector fontSubs; + /* Game INI contents */ struct { std::string scripts; diff --git a/src/debugwriter.h b/src/debugwriter.h index 19495e3..27244d1 100644 --- a/src/debugwriter.h +++ b/src/debugwriter.h @@ -46,7 +46,7 @@ public: ~Debug() { - std::clog << buf.str() << "\n"; + std::clog << buf.str() << std::endl; } private: diff --git a/src/eventthread.cpp b/src/eventthread.cpp index acdb161..fa671ad 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -209,9 +209,6 @@ void EventThread::process(RGSSThreadData &rtData) break; } - if (event.key.keysym.scancode == SDL_SCANCODE_F3 && rtData.config.screenshots) - rtData.rqScreenshot = true; - keyStates[event.key.keysym.scancode] = true; break; diff --git a/src/eventthread.h b/src/eventthread.h index 5ed27c1..79ac199 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -178,8 +178,6 @@ struct RGSSThreadData std::string rgssErrorMsg; - volatile bool rqScreenshot; - RGSSThreadData(EventThread *ethread, const char *argv0, SDL_Window *window, @@ -190,8 +188,7 @@ struct RGSSThreadData argv0(argv0), window(window), sizeResoRatio(1, 1), - config(newconf), - rqScreenshot(false) + config(newconf) {} }; diff --git a/src/filesystem.cpp b/src/filesystem.cpp index d679a41..6a731ce 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -21,6 +21,7 @@ #include "filesystem.h" +#include "font.h" #include "util.h" #include "exception.h" #include "boost-hash.h" @@ -297,8 +298,12 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite) /* Check header */ uint32_t header1, header2; - readUint32(io, header1); - readUint32(io, header2); + + if (!readUint32(io, header1)) + return 0; + + if (!readUint32(io, header2)) + return 0; if (header1 != RGSS_HEADER_1 || header2 != RGSS_HEADER_2) return 0; @@ -476,41 +481,133 @@ RGSS_noop2(void*, const char*) static const PHYSFS_Archiver RGSS_Archiver = { 0, - { - "RGSSAD", - "RGSS encrypted archive format", - "Jonas Kulla ", - "http://k-du.de/rgss/rgss.html", - 0 /* symlinks not supported */ - }, - RGSS_openArchive, - RGSS_enumerateFiles, - RGSS_openRead, - RGSS_noop1, /* openWrite */ - RGSS_noop1, /* openAppend */ - RGSS_noop2, /* remove */ - RGSS_noop2, /* mkdir */ - RGSS_stat, - RGSS_closeArchive + { + "RGSSAD", + "RGSS encrypted archive format", + "Jonas Kulla ", + "http://k-du.de/rgss/rgss.html", + 0 /* symlinks not supported */ + }, + RGSS_openArchive, + RGSS_enumerateFiles, + RGSS_openRead, + RGSS_noop1, /* openWrite */ + RGSS_noop1, /* openAppend */ + RGSS_noop2, /* remove */ + RGSS_noop2, /* mkdir */ + RGSS_stat, + RGSS_closeArchive }; + +static inline PHYSFS_File *sdlPHYS(SDL_RWops *ops) +{ + return static_cast(ops->hidden.unknown.data1); +} + +static Sint64 SDL_RWopsSize(SDL_RWops *ops) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return -1; + + return PHYSFS_fileLength(f); +} + +static Sint64 SDL_RWopsSeek(SDL_RWops *ops, int64_t offset, int whence) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return -1; + + int64_t base; + + switch (whence) + { + default: + case RW_SEEK_SET : + base = 0; + break; + case RW_SEEK_CUR : + base = PHYSFS_tell(f); + break; + case RW_SEEK_END : + base = PHYSFS_fileLength(f); + break; + } + + int result = PHYSFS_seek(f, base + offset); + + return (result != 0) ? PHYSFS_tell(f) : -1; +} + +static size_t SDL_RWopsRead(SDL_RWops *ops, void *buffer, size_t size, size_t maxnum) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return 0; + + PHYSFS_sint64 result = PHYSFS_readBytes(f, buffer, size*maxnum); + + return (result != -1) ? (result / size) : 0; +} + +static size_t SDL_RWopsWrite(SDL_RWops *ops, const void *buffer, size_t size, size_t num) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return 0; + + PHYSFS_sint64 result = PHYSFS_writeBytes(f, buffer, size*num); + + return (result != -1) ? (result / size) : 0; +} + +static int SDL_RWopsClose(SDL_RWops *ops) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return -1; + + int result = PHYSFS_close(f); + + f = 0; + + return (result != 0) ? 0 : -1; +} + +static int SDL_RWopsCloseFree(SDL_RWops *ops) +{ + int result = SDL_RWopsClose(ops); + + SDL_FreeRW(ops); + + return result; +} + +const Uint32 SDL_RWOPS_PHYSFS = SDL_RWOPS_UNKNOWN+10; + struct FileSystemPrivate { - /* All keys are lower case */ + /* Maps: lower case filename, To: actual (mixed case) filename. + * This is for compatibility with games that take Windows' + * case insensitivity for granted */ BoostHash pathCache; + bool havePathCache; std::vector extensions[FileSystem::Undefined+1]; - // FIXME Need to find a better way to do this.. - char pathBuffer[512]; - bool havePathCache; - /* Attempt to locate an extension string in a filename. * Either a pointer into the input string pointing at the * extension, or null is returned */ const char *findExt(const char *filename) { - int len; + size_t len; for (len = strlen(filename); len > 0; --len) { @@ -524,110 +621,147 @@ struct FileSystemPrivate return 0; } - const char *completeFileName(const char *filename, - FileSystem::FileType type, - const char **foundExt) + /* Complete filename via regular physfs lookup */ + bool completeFilenameReg(const char *filename, + FileSystem::FileType type, + char *outBuffer, + size_t outN, + const char **foundExt) { - if (!havePathCache) + /* Try supplementing extensions to find an existing path */ + const std::vector &extList = extensions[type]; + + for (size_t i = 0; i < extList.size(); ++i) { - if (PHYSFS_exists(filename)) + const char *ext = extList[i].c_str(); + + snprintf(outBuffer, outN, "%s.%s", filename, ext); + + if (PHYSFS_exists(outBuffer)) { if (foundExt) - *foundExt = findExt(filename); + *foundExt = ext; - return filename; + return true; } - - const std::vector &extList = extensions[type]; - for (size_t i = 0; i < extList.size(); ++i) - { - const char *ext = extList[i].c_str(); - - snprintf(pathBuffer, sizeof(pathBuffer), "%s.%s", filename, ext); - - if (PHYSFS_exists(pathBuffer)) - { - if (foundExt) - *foundExt = ext; - - return pathBuffer; - } - } - - // Is this even necessary? - if (foundExt) - *foundExt = 0; - - return 0; } - char buff[512]; - size_t i; - - for (i = 0; i < sizeof(buff) && filename[i]; ++i) - buff[i] = tolower(filename[i]); - - buff[i] = '\0'; - - std::string key(buff); - - /* If the path was already complete, - * we are done at this point */ - if (pathCache.contains(key)) + /* Doing the check without supplemented extension + * fits the usage pattern of RMXP games */ + if (PHYSFS_exists(filename)) { - /* The extension might already be included here, - * so try to find it */ + strncpy(outBuffer, filename, outN); + if (foundExt) *foundExt = findExt(filename); - return pathCache[key].c_str(); + return true; } - char buff2[512]; + return false; + } - /* Try supplementing extensions - * to find an existing path */ - if (type != FileSystem::Undefined) + /* Complete filename via path cache */ + bool completeFilenamePC(const char *filename, + FileSystem::FileType type, + char *outBuffer, + size_t outN, + const char **foundExt) + { + size_t i; + char lowCase[512]; + + for (i = 0; i < sizeof(lowCase)-1 && filename[i]; ++i) + lowCase[i] = tolower(filename[i]); + + lowCase[i] = '\0'; + + std::string key; + + const std::vector &extList = extensions[type]; + + for (size_t i = 0; i < extList.size(); ++i) { - std::vector &extList = extensions[type]; - for (size_t i = 0; i < extList.size(); ++i) + const char *ext = extList[i].c_str(); + + snprintf(outBuffer, outN, "%s.%s", lowCase, ext); + key = outBuffer; + + if (pathCache.contains(key)) { - const char *ext = extList[i].c_str(); + strncpy(outBuffer, pathCache[key].c_str(), outN); - snprintf(buff2, sizeof(buff2), "%s.%s", buff, ext); - key = buff2; + if (foundExt) + *foundExt = ext; - if (pathCache.contains(key)) - { - if (foundExt) - *foundExt = ext; - - return pathCache[key].c_str(); - } + return true; } } - if (foundExt) - *foundExt = 0; + key = lowCase; - return 0; + if (pathCache.contains(key)) + { + strncpy(outBuffer, pathCache[key].c_str(), outN); + + if (foundExt) + *foundExt = findExt(filename); + + return true; + } + + return false; } - PHYSFS_File *openReadInt(const char *filename, - FileSystem::FileType type, - const char **foundExt) + /* Try to complete 'filename' with file extensions + * based on 'type'. If no combination could be found, + * returns false, and 'foundExt' is untouched */ + bool completeFileName(const char *filename, + FileSystem::FileType type, + char *outBuffer, + size_t outN, + const char **foundExt) { - const char *foundName = completeFileName(filename, type, foundExt); + if (havePathCache) + return completeFilenamePC(filename, type, outBuffer, outN, foundExt); + else + return completeFilenameReg(filename, type, outBuffer, outN, foundExt); + } - if (!foundName) + PHYSFS_File *openReadHandle(const char *filename, + FileSystem::FileType type, + const char **foundExt) + { + char found[512]; + + if (!completeFileName(filename, type, found, sizeof(found), foundExt)) throw Exception(Exception::NoFileError, "%s", filename); - PHYSFS_File *handle = PHYSFS_openRead(foundName); + PHYSFS_File *handle = PHYSFS_openRead(found); + if (!handle) throw Exception(Exception::PHYSFSError, "PhysFS: %s", PHYSFS_getLastError()); return handle; } + + void initReadOps(PHYSFS_File *handle, + SDL_RWops &ops, + bool freeOnClose) + { + ops.size = SDL_RWopsSize; + ops.seek = SDL_RWopsSeek; + ops.read = SDL_RWopsRead; + ops.write = SDL_RWopsWrite; + + if (freeOnClose) + ops.close = SDL_RWopsCloseFree; + else + ops.close = SDL_RWopsClose; + + ops.type = SDL_RWOPS_PHYSFS; + ops.hidden.unknown.data1 = handle; + } }; FileSystem::FileSystem(const char *argv0, @@ -724,123 +858,83 @@ void FileSystem::createPathCache() p->havePathCache = true; } -static inline PHYSFS_File *sdlPHYS(SDL_RWops *ops) +static void strToLower(std::string &str) { - return static_cast(ops->hidden.unknown.data1); + for (size_t i = 0; i < str.size(); ++i) + str[i] = tolower(str[i]); } -static Sint64 SDL_RWopsSize(SDL_RWops *ops) +struct FontSetsCBData { - PHYSFS_File *f = sdlPHYS(ops); + FileSystemPrivate *p; + SharedFontState *sfs; +}; - if (!f) - return -1; +static void fontSetEnumCB(void *data, const char *, + const char *fname) +{ + FontSetsCBData *d = static_cast(data); + FileSystemPrivate *p = d->p; - return PHYSFS_fileLength(f); + /* Only consider filenames with font extensions */ + const char *ext = p->findExt(fname); + + if (!ext) + return; + + std::string lower(ext); + strToLower(lower); + + if (!contains(p->extensions[FileSystem::Font], lower)) + return; + + std::string filename("Fonts/"); + filename += fname; + + PHYSFS_File *handle = PHYSFS_openRead(filename.c_str()); + + if (!handle) + return; + + SDL_RWops ops; + p->initReadOps(handle, ops, false); + + d->sfs->initFontSetCB(ops, filename); + + SDL_RWclose(&ops); } -static Sint64 SDL_RWopsSeek(SDL_RWops *ops, int64_t offset, int whence) +void FileSystem::initFontSets(SharedFontState &sfs) { - PHYSFS_File *f = sdlPHYS(ops); + FontSetsCBData d = { p, &sfs }; - if (!f) - return -1; - - int64_t base; - - switch (whence) - { - default: - case RW_SEEK_SET : - base = 0; - break; - case RW_SEEK_CUR : - base = PHYSFS_tell(f); - break; - case RW_SEEK_END : - base = PHYSFS_fileLength(f); - break; - } - - int result = PHYSFS_seek(f, base + offset); - - return (result != 0) ? PHYSFS_tell(f) : -1; + PHYSFS_enumerateFilesCallback("Fonts", fontSetEnumCB, &d); } -static size_t SDL_RWopsRead(SDL_RWops *ops, void *buffer, size_t size, size_t maxnum) -{ - PHYSFS_File *f = sdlPHYS(ops); - - if (!f) - return 0; - - PHYSFS_sint64 result = PHYSFS_readBytes(f, buffer, size*maxnum); - - return (result != -1) ? (result / size) : 0; -} - -static size_t SDL_RWopsWrite(SDL_RWops *ops, const void *buffer, size_t size, size_t num) -{ - PHYSFS_File *f = sdlPHYS(ops); - - if (!f) - return 0; - - PHYSFS_sint64 result = PHYSFS_writeBytes(f, buffer, size*num); - - return (result != -1) ? (result / size) : 0; -} - -static int SDL_RWopsClose(SDL_RWops *ops) -{ - PHYSFS_File *f = sdlPHYS(ops); - - if (!f) - return -1; - - int result = PHYSFS_close(f); - - f = 0; - - return (result != 0) ? 0 : -1; -} - -static int SDL_RWopsCloseFree(SDL_RWops *ops) -{ - int result = SDL_RWopsClose(ops); - - SDL_FreeRW(ops); - - return result; -} - -const Uint32 SDL_RWOPS_PHYSFS = SDL_RWOPS_UNKNOWN+10; - void FileSystem::openRead(SDL_RWops &ops, const char *filename, FileType type, bool freeOnClose, const char **foundExt) { - PHYSFS_File *handle = p->openReadInt(filename, type, foundExt); + PHYSFS_File *handle = p->openReadHandle(filename, type, foundExt); - ops.size = SDL_RWopsSize; - ops.seek = SDL_RWopsSeek; - ops.read = SDL_RWopsRead; - ops.write = SDL_RWopsWrite; + p->initReadOps(handle, ops, freeOnClose); +} - if (freeOnClose) - ops.close = SDL_RWopsCloseFree; - else - ops.close = SDL_RWopsClose; +void FileSystem::openReadRaw(SDL_RWops &ops, + const char *filename, + bool freeOnClose) +{ + PHYSFS_File *handle = PHYSFS_openRead(filename); + assert(handle); - ops.type = SDL_RWOPS_PHYSFS; - ops.hidden.unknown.data1 = handle; + p->initReadOps(handle, ops, freeOnClose); } bool FileSystem::exists(const char *filename, FileType type) { - const char *foundName = p->completeFileName(filename, type, 0); + char found[512]; - return (foundName != 0); + return p->completeFileName(filename, type, found, sizeof(found), 0); } diff --git a/src/filesystem.h b/src/filesystem.h index a9ab2ef..08d573f 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -25,6 +25,7 @@ #include struct FileSystemPrivate; +class SharedFontState; class FileSystem { @@ -35,9 +36,13 @@ public: void addPath(const char *path); - /* Call this after the last 'addPath()' */ + /* Call these after the last 'addPath()' */ void createPathCache(); + /* Scans "Fonts/" and creates inventory of + * available font assets */ + void initFontSets(SharedFontState &sfs); + /* For extension supplementing */ enum FileType { @@ -53,6 +58,11 @@ public: bool freeOnClose = false, const char **foundExt = 0); + /* Circumvents extension supplementing */ + void openReadRaw(SDL_RWops &ops, + const char *filename, + bool freeOnClose = false); + bool exists(const char *filename, FileType type = Undefined); diff --git a/src/flashable.h b/src/flashable.h index e32c89f..bd7e21d 100644 --- a/src/flashable.h +++ b/src/flashable.h @@ -55,7 +55,7 @@ public: flashAlpha = flashColor.w; } - void update() + virtual void update() { if (!flashing) return; diff --git a/src/font.cpp b/src/font.cpp index 11c37c7..a5dbed5 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -26,6 +26,7 @@ #include "exception.h" #include "boost-hash.h" #include "util.h" +#include "config.h" #include #include @@ -45,88 +46,151 @@ typedef std::pair FontKey; -static void strToLower(std::string &str) -{ - for (size_t i = 0; i < str.size(); ++i) - str[i] = tolower(str[i]); -} - -struct FontPoolPrivate -{ - BoostHash hash; -}; - -FontPool::FontPool() -{ - p = new FontPoolPrivate; -} - -FontPool::~FontPool() -{ - BoostHash::const_iterator iter; - for (iter = p->hash.cbegin(); iter != p->hash.cend(); ++iter) - TTF_CloseFont(iter->second); - - delete p; -} - static SDL_RWops *openBundledFont() { return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT)); } -_TTF_Font *FontPool::request(const char *filename, - int size) +struct FontSet { - // FIXME Find out how font path resolution is done in VX/Ace - std::string nameKey(filename); - strToLower(nameKey); - strReplace(nameKey, ' ', '_'); + /* 'Regular' style */ + std::string regular; - bool useBundled = false; - std::string path = std::string("Fonts/") + nameKey; - if (!shState->fileSystem().exists(path.c_str(), FileSystem::Font)) + /* Any other styles (used in case no 'Regular' exists) */ + std::string other; +}; + +struct SharedFontStatePrivate +{ + /* Maps: font family name, To: substituted family name, + * as specified via configuration file / arguments */ + BoostHash subs; + + /* Maps: font family name, To: set of physical + * font filenames located in "Fonts/" */ + BoostHash sets; + + /* Pool of already opened fonts; once opened, they are reused + * and never closed until the termination of the program */ + BoostHash pool; +}; + +SharedFontState::SharedFontState(const Config &conf) +{ + p = new SharedFontStatePrivate; + + /* Parse font substitutions */ + for (size_t i = 0; i < conf.fontSubs.size(); ++i) { - /* Use the same name key for the bundled font - * even when it resulted from multiple different - * font name requests. The space at the front is - * to prevent collisions (spaces are normally - * replaced with '_' */ - useBundled = true; - nameKey = " bundled"; + const std::string &raw = conf.fontSubs[i]; + size_t sepPos = raw.find_first_of('>'); + + if (sepPos == std::string::npos) + continue; + + std::string from = raw.substr(0, sepPos); + std::string to = raw.substr(sepPos+1); + + p->subs.insert(from, to); + } +} + +SharedFontState::~SharedFontState() +{ + BoostHash::const_iterator iter; + for (iter = p->pool.cbegin(); iter != p->pool.cend(); ++iter) + TTF_CloseFont(iter->second); + + delete p; +} + +void SharedFontState::initFontSetCB(SDL_RWops &ops, + const std::string &filename) +{ + TTF_Font *font = TTF_OpenFontRW(&ops, 0, 0); + + if (!font) + return; + + std::string family = TTF_FontFaceFamilyName(font); + std::string style = TTF_FontFaceStyleName(font); + + TTF_CloseFont(font); + + FontSet &set = p->sets[family]; + + if (style == "Regular") + set.regular = filename; + else + set.other = filename; +} + +_TTF_Font *SharedFontState::getFont(std::string family, + int size) +{ + /* Check for substitutions */ + if (p->subs.contains(family)) + family = p->subs[family]; + + /* Find out if the font asset exists */ + const FontSet &req = p->sets[family]; + + if (req.regular.empty() && req.other.empty()) + { + /* Doesn't exist; use built-in font */ + family = ""; } - FontKey key(nameKey, size); + FontKey key(family, size); - TTF_Font *font = p->hash.value(key, 0); + TTF_Font *font = p->pool.value(key); if (font) return font; - /* Not in hash, open */ + /* Not in pool; open new handle */ SDL_RWops *ops; - if (useBundled) + if (family.empty()) { + /* Built-in font */ ops = openBundledFont(); } else { + /* Use 'other' path as alternative in case + * we have no 'regular' styled font asset */ + const char *path = !req.regular.empty() + ? req.regular.c_str() : req.other.c_str(); + ops = SDL_AllocRW(); - shState->fileSystem().openRead(*ops, path.c_str(), FileSystem::Font, true); + shState->fileSystem().openReadRaw(*ops, path, true); } // FIXME 0.9 is guesswork at this point - font = TTF_OpenFontRW(ops, 1, (float) size * .90); +// float gamma = (96.0/45.0)*(5.0/14.0)*(size-5); +// font = TTF_OpenFontRW(ops, 1, gamma /** .90*/); + font = TTF_OpenFontRW(ops, 1, size* .90); if (!font) - throw Exception(Exception::SDLError, "SDL: %s", SDL_GetError()); + throw Exception(Exception::SDLError, "%s", SDL_GetError()); - p->hash.insert(key, font); + p->pool.insert(key, font); return font; } +bool SharedFontState::fontPresent(std::string family) +{ + /* Check for substitutions */ + if (p->subs.contains(family)) + family = p->subs[family]; + + const FontSet &set = p->sets[family]; + + return !(set.regular.empty() && set.other.empty()); +} + struct FontPrivate { @@ -173,7 +237,7 @@ struct FontPrivate {} }; -std::string FontPrivate::defaultName = "MS PGothic"; +std::string FontPrivate::defaultName = "Arial"; int FontPrivate::defaultSize = 22; bool FontPrivate::defaultBold = false; bool FontPrivate::defaultItalic = false; @@ -183,9 +247,7 @@ Color FontPrivate::defaultColorTmp(255, 255, 255, 255); bool Font::doesExist(const char *name) { - std::string path = std::string("Fonts/") + std::string(name); - - return shState->fileSystem().exists(path.c_str(), FileSystem::Font); + return shState->fontState().fontPresent(name); } Font::Font(const char *name, @@ -223,6 +285,10 @@ void Font::setSize(int value) if (p->size == value) return; + /* Catch illegal values (according to RMXP) */ + if (value < 6 || value > 96) + throw Exception(Exception::ArgumentError, "%s", "bad value for size"); + p->size = value; p->sdlFont = 0; } @@ -253,8 +319,8 @@ void Font::setDefaultName(const char *value) _TTF_Font *Font::getSdlFont() { if (!p->sdlFont) - p->sdlFont = shState->fontPool().request(p->name.c_str(), - p->size); + p->sdlFont = shState->fontState().getFont(p->name.c_str(), + p->size); int style = TTF_STYLE_NORMAL; diff --git a/src/font.h b/src/font.h index 3c73b54..df25001 100644 --- a/src/font.h +++ b/src/font.h @@ -25,22 +25,50 @@ #include "etc.h" #include "util.h" +struct SDL_RWops; struct _TTF_Font; -struct FontPoolPrivate; +struct Config; -class FontPool +struct SharedFontStatePrivate; + +class SharedFontState { public: - FontPool(); - ~FontPool(); + SharedFontState(const Config &conf); + ~SharedFontState(); - _TTF_Font *request(const char *filename, + /* Called from FileSystem during font cache initialization + * (when "Fonts/" is scanned for available assets). + * 'ops' is an opened handle to a possible font file, + * 'filename' is the corresponding path */ + void initFontSetCB(SDL_RWops &ops, + const std::string &filename); + + _TTF_Font *getFont(std::string family, int size); + bool fontPresent(std::string family); + private: - FontPoolPrivate *p; + SharedFontStatePrivate *p; }; +/* Concerning Font::name/defaultName : + * In RGSS, this is not actually a string; any type of + * object is accepted, however anything but strings and + * arrays is ignored (and text drawing turns blank). + * Single strings are interpreted as font family names, + * and directly passed to the underlying C++ object; + * arrays however are searched for the first string + * object corresponding to a valid font family name, + * and rendering is done with that. In mkxp, we pass + * this first valid font family as the 'name' attribute + * back to the C++ object on assignment and object + * creation (in case Font.default_name is also an array). + * Invalid parameters (things other than strings or + * arrays not containing any valid family name) are + * passed back as "". */ + struct FontPrivate; class Font @@ -57,9 +85,9 @@ public: const char *getName() const; void setName(const char *value); - DECL_ATTR( Size, int ) - DECL_ATTR( Bold, bool ) - DECL_ATTR( Italic, bool ) + DECL_ATTR( Size, int ) + DECL_ATTR( Bold, bool ) + DECL_ATTR( Italic, bool ) DECL_ATTR( Color, Color* ) DECL_ATTR_STATIC( DefaultName, const char* ) diff --git a/src/gl-util.h b/src/gl-util.h index a00c119..d283bf5 100644 --- a/src/gl-util.h +++ b/src/gl-util.h @@ -216,15 +216,6 @@ namespace FBO blit(srcX, srcY, srcW, srcH, dstX, dstY, srcW, srcH, mode); } - inline Vec4 getPixel(int x, int y) - { - Vec4 pixel; - - glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, &pixel.x); - - return pixel; - } - inline void clear() { glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/glstate.cpp b/src/glstate.cpp index f9c3557..2f1c7fe 100644 --- a/src/glstate.cpp +++ b/src/glstate.cpp @@ -20,6 +20,7 @@ */ #include "glstate.h" +#include "shader.h" #include "etc.h" #include @@ -95,6 +96,11 @@ void GLViewport::apply(const IntRect &value) glViewport(value.x, value.y, value.w, value.h); } +void GLProgram::apply(const unsigned int &value) +{ + glUseProgram(value); +} + GLState::Caps::Caps() { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); @@ -110,4 +116,5 @@ GLState::GLState() scissorTest.init(false); scissorBox.init(IntRect(0, 0, 640, 480)); texture2D.init(true); + program.init(0); } diff --git a/src/glstate.h b/src/glstate.h index 9ef66de..613c555 100644 --- a/src/glstate.h +++ b/src/glstate.h @@ -99,6 +99,11 @@ class GLViewport : public GLProperty void apply(const IntRect &value); }; +class GLProgram : public GLProperty /* GLuint */ +{ + void apply(const unsigned int &value); +}; + class GLState { @@ -109,6 +114,7 @@ public: GLTexture2D texture2D; GLBlendMode blendMode; GLViewport viewport; + GLProgram program; struct Caps { diff --git a/src/graphics.cpp b/src/graphics.cpp index fa769d7..b1e0dab 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -191,7 +191,6 @@ public: screenQuad.draw(); glState.blendMode.pop(); - shader.unbind(); } #ifdef RGSS2 @@ -508,44 +507,6 @@ struct GraphicsPrivate swapGLBuffer(); } - - void writeScreenshot(const char *filename) - { - int bpp; - uint32_t rm, gm, bm, am; - SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &rm, &gm, &bm, &am); - - /* Discard alpha channel because it might have bogus values */ - SDL_Surface *screenshot = - SDL_CreateRGBSurface(0, scRes.x, scRes.y, bpp, rm, gm, bm, 0); - - screen.getPP().bindLastBuffer(); - - glReadPixels(0, 0, scRes.x, scRes.y, GL_RGBA, GL_UNSIGNED_BYTE, screenshot->pixels); - - IMG_SavePNG(screenshot, filename); - - SDL_FreeSurface(screenshot); - } - - void checkScreenshotRq() - { - if (!threadData->rqScreenshot) - return; - - threadData->rqScreenshot = false; - - struct timeval tv; - gettimeofday(&tv, 0); - struct tm tm = *localtime(&tv.tv_sec); - - char filename[32]; - - snprintf(filename, sizeof(filename), "%d%02d%02d-%02d%02d%02d.png", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - - writeScreenshot(filename); - } }; Graphics::Graphics(RGSSThreadData *data) @@ -569,8 +530,6 @@ void Graphics::update() // p->cpuTimer->endTiming(); // p->gpuTimer->startTiming(); - p->checkScreenshotRq(); - if (p->frozen) return; diff --git a/src/plane.cpp b/src/plane.cpp index 8a876d8..cb76967 100644 --- a/src/plane.cpp +++ b/src/plane.cpp @@ -93,9 +93,6 @@ struct PlanePrivate updateQuadSource(); quadSourceDirty = false; } - - if (bitmap) - bitmap->flush(); } }; diff --git a/src/quadarray.h b/src/quadarray.h index 42c0c81..0d6d4b8 100644 --- a/src/quadarray.h +++ b/src/quadarray.h @@ -34,17 +34,41 @@ typedef uint32_t index_t; #define _GL_INDEX_TYPE GL_UNSIGNED_INT -struct ColorQuadArray +/* A small hack to get mutable QuadArray constructors */ +inline void initBufferBindings(Vertex *) { - std::vector vertices; + glEnableVertexAttribArray(Shader::Color); + glEnableVertexAttribArray(Shader::Position); + glEnableVertexAttribArray(Shader::TexCoord); + + glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset()); + glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset()); + glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::texPosOffset()); +} + +inline void initBufferBindings(SVertex *) +{ + glEnableVertexAttribArray(Shader::Position); + glEnableVertexAttribArray(Shader::TexCoord); + + glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), SVertex::posOffset()); + glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), SVertex::texPosOffset()); +} + +template +struct QuadArray +{ + std::vector vertices; VBO::ID vbo; VAO::ID vao; int quadCount; + GLsizeiptr vboSize; - ColorQuadArray() - : quadCount(0) + QuadArray() + : quadCount(0), + vboSize(-1) { vbo = VBO::gen(); vao = VAO::gen(); @@ -53,20 +77,16 @@ struct ColorQuadArray VBO::bind(vbo); shState->bindQuadIBO(); - glEnableVertexAttribArray(Shader::Color); - glEnableVertexAttribArray(Shader::Position); - glEnableVertexAttribArray(Shader::TexCoord); - - glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset()); - glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset()); - glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::texPosOffset()); + /* Call correct implementation here via overloading */ + VertexType *dummy = 0; + initBufferBindings(dummy); VAO::unbind(); IBO::unbind(); VBO::unbind(); } - ~ColorQuadArray() + ~QuadArray() { VBO::del(vbo); VAO::del(vao); @@ -78,15 +98,36 @@ struct ColorQuadArray quadCount = size; } + void clear() + { + vertices.clear(); + quadCount = 0; + } + /* This needs to be called after the final 'append()' call * and previous to the first 'draw()' call. */ void commit() { VBO::bind(vbo); - VBO::uploadData(vertices.size() * sizeof(Vertex), &vertices[0], GL_DYNAMIC_DRAW); - VBO::unbind(); - shState->ensureQuadIBO(quadCount); + GLsizeiptr size = vertices.size() * sizeof(VertexType); + + if (size > vboSize) + { + /* New data exceeds already allocated size. + * Reallocate VBO. */ + VBO::uploadData(size, &vertices[0], GL_DYNAMIC_DRAW); + vboSize = size; + + shState->ensureQuadIBO(quadCount); + } + else + { + /* New data fits in allocated size */ + VBO::uploadSubData(0, size, &vertices[0]); + } + + VBO::unbind(); } void draw(size_t offset, size_t count) @@ -110,67 +151,7 @@ struct ColorQuadArray } }; -struct PointArray -{ - std::vector vertices; - VBO::ID vbo; - VAO::ID vao; - - PointArray() - { - vbo = VBO::gen(); - vao = VAO::gen(); - - VAO::bind(vao); - VBO::bind(vbo); - - glEnableVertexAttribArray(Shader::Color); - glEnableVertexAttribArray(Shader::Position); - - glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset()); - glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset()); - - VAO::unbind(); - VBO::unbind(); - } - - ~PointArray() - { - VBO::del(vbo); - VAO::del(vao); - } - - void append(const Vec2 &pos, const Vec4 &color) - { - Vertex vert; - vert.pos = pos; - vert.color = color; - vertices.push_back(vert); - } - - void commit() - { - VBO::bind(vbo); - VBO::uploadData(vertices.size() * sizeof(Vertex), &vertices[0]); - VBO::unbind(); - } - - void reset() - { - vertices.clear(); - } - - void draw() - { - VAO::bind(vao); - glDrawArrays(GL_POINTS, 0, count()); - VAO::unbind(); - } - - int count() - { - return vertices.size(); - } -}; +typedef QuadArray ColorQuadArray; +typedef QuadArray SimpleQuadArray; #endif // QUADARRAY_H diff --git a/src/shader.cpp b/src/shader.cpp index f11e799..9aefb3b 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -99,13 +99,13 @@ Shader::~Shader() void Shader::bind() { - glUseProgram(program); + glState.program.set(program); } void Shader::unbind() { glActiveTexture(GL_TEXTURE0); - glUseProgram(0); + glState.program.set(0); } void Shader::init(const unsigned char *vert, int vertSize, diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index 7d0b587..c681776 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -67,8 +67,8 @@ struct SharedStatePrivate ShaderSet shaders; TexPool texPool; - FontPool fontPool; + SharedFontState fontState; Font *defaultFont; TEX::ID globalTex; @@ -90,6 +90,7 @@ struct SharedStatePrivate rtData(*threadData), config(threadData->config), graphics(threadData), + fontState(threadData->config), stampCounter(0) { if (!config.gameFolder.empty()) @@ -121,6 +122,8 @@ struct SharedStatePrivate if (config.pathCache) fileSystem.createPathCache(); + fileSystem.initFontSets(fontState); + globalTexW = 128; globalTexH = 64; @@ -206,8 +209,8 @@ GSATT(Audio&, audio) GSATT(GLState&, _glState) GSATT(ShaderSet&, shaders) GSATT(TexPool&, texPool) -GSATT(FontPool&, fontPool) GSATT(Quad&, gpQuad) +GSATT(SharedFontState&, fontState) void SharedState::setBindingData(void *data) { diff --git a/src/sharedstate.h b/src/sharedstate.h index c389f1f..9b1c22a 100644 --- a/src/sharedstate.h +++ b/src/sharedstate.h @@ -43,8 +43,8 @@ class Input; class Audio; class GLState; class TexPool; -class FontPool; class Font; +class SharedFontState; struct GlobalIBO; struct Config; struct Vec2i; @@ -74,8 +74,8 @@ struct SharedState ShaderSet &shaders(); TexPool &texPool(); - FontPool &fontPool(); + SharedFontState &fontState(); Font &defaultFont(); sigc::signal prepareDraw; diff --git a/src/sprite.cpp b/src/sprite.cpp index 4a797d9..b5888c1 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -32,6 +32,9 @@ #include "transform.h" #include "shader.h" #include "glstate.h" +#include "quadarray.h" + +#include #include @@ -63,6 +66,22 @@ struct SpritePrivate Color *color; Tone *tone; +#ifdef RGSS2 + struct + { + int amp; + int length; + int speed; + float phase; + + /* Wave effect is active (amp != 0) */ + bool active; + /* qArray needs updating */ + bool dirty; + SimpleQuadArray qArray; + } wave; +#endif + EtcTemps tmp; sigc::connection prepareCon; @@ -87,6 +106,13 @@ struct SpritePrivate prepareCon = shState->prepareDraw.connect (sigc::mem_fun(this, &SpritePrivate::prepare)); + +#ifdef RGSS2 + wave.amp = 0; + wave.length = 180; + wave.speed = 360; + wave.phase = 0.0; +#endif } ~SpritePrivate() @@ -117,6 +143,10 @@ struct SpritePrivate quad.setPosRect(IntRect(0, 0, srcRect->width, srcRect->height)); recomputeBushDepth(); + +#ifdef RGSS2 + wave.dirty = true; +#endif } void updateSrcRectCon() @@ -141,6 +171,16 @@ struct SpritePrivate if (!opacity) return; +#ifdef RGSS2 + if (wave.active) + { + /* Don't do expensive wave bounding box + * calculations */ + isVisible = true; + return; + } +#endif + /* Compare sprite bounding box against the scene */ /* If sprite is zoomed/rotated, just opt out for now @@ -161,10 +201,101 @@ struct SpritePrivate isVisible = SDL_HasIntersection(&self, &sceneRect); } +#ifdef RGSS2 + void emitWaveChunk(SVertex *&vert, float phase, int width, + float zoomY, int chunkY, int chunkLength) + { + float wavePos = phase + (chunkY / (float) wave.length) * M_PI * 2; + float chunkX = sin(wavePos) * wave.amp; + + FloatRect tex(0, chunkY / zoomY, width, chunkLength / zoomY); + FloatRect pos = tex; + pos.x = chunkX; + + Quad::setTexPosRect(vert, tex, pos); + vert += 4; + } + + void updateWave() + { + if (!bitmap) + return; + + if (wave.amp == 0) + { + wave.active = false; + return; + } + + wave.active = true; + + int width = srcRect->width; + int height = srcRect->height; + float zoomY = trans.getScale().y; + + if (wave.amp < -(width / 2)) + { + wave.qArray.resize(0); + wave.qArray.commit(); + + return; + } + + /* RMVX does this, and I have no fucking clue why */ + if (wave.amp < 0) + { + wave.qArray.resize(1); + + int x = -wave.amp; + int w = width - x * 2; + + FloatRect tex(x, srcRect->y, w, srcRect->height); + + Quad::setTexPosRect(&wave.qArray.vertices[0], tex, tex); + wave.qArray.commit(); + + return; + } + + /* The length of the sprite as it appears on screen */ + int visibleLength = height * zoomY; + + /* First chunk length (aligned to 8 pixel boundary */ + int firstLength = ((int) trans.getPosition().y) % 8; + + /* Amount of full 8 pixel chunks in the middle */ + int chunks = (visibleLength - firstLength) / 8; + + /* Final chunk length */ + int lastLength = (visibleLength - firstLength) % 8; + + wave.qArray.resize(!!firstLength + chunks + !!lastLength); + SVertex *vert = &wave.qArray.vertices[0]; + + float phase = (wave.phase * M_PI) / 180.f; + + if (firstLength > 0) + emitWaveChunk(vert, phase, width, zoomY, 0, firstLength); + + for (int i = 0; i < chunks; ++i) + emitWaveChunk(vert, phase, width, zoomY, firstLength + i * 8, 8); + + if (lastLength > 0) + emitWaveChunk(vert, phase, width, zoomY, firstLength + chunks * 8, lastLength); + + wave.qArray.commit(); + } +#endif + void prepare() { - if (bitmap) - bitmap->flush(); +#ifdef RGSS2 + if (wave.dirty) + { + updateWave(); + wave.dirty = false; + } +#endif updateVisibility(); } @@ -196,14 +327,21 @@ DEF_ATTR_RD_SIMPLE(Sprite, Angle, float, p->trans.getRotation()) DEF_ATTR_RD_SIMPLE(Sprite, Mirror, bool, p->mirrored) DEF_ATTR_RD_SIMPLE(Sprite, BushDepth, int, p->bushDepth) DEF_ATTR_RD_SIMPLE(Sprite, BlendType, int, p->blendType) -DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width) -DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height) DEF_ATTR_SIMPLE(Sprite, BushOpacity, int, p->bushOpacity) DEF_ATTR_SIMPLE(Sprite, Opacity, int, p->opacity) DEF_ATTR_SIMPLE(Sprite, Color, Color*, p->color) DEF_ATTR_SIMPLE(Sprite, Tone, Tone*, p->tone) +#ifdef RGSS2 +DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width) +DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height) +DEF_ATTR_RD_SIMPLE(Sprite, WaveAmp, int, p->wave.amp) +DEF_ATTR_RD_SIMPLE(Sprite, WaveLength, int, p->wave.length) +DEF_ATTR_RD_SIMPLE(Sprite, WaveSpeed, int, p->wave.speed) +DEF_ATTR_RD_SIMPLE(Sprite, WavePhase, float, p->wave.phase) +#endif + void Sprite::setBitmap(Bitmap *bitmap) { GUARD_DISPOSED @@ -221,6 +359,10 @@ void Sprite::setBitmap(Bitmap *bitmap) *p->srcRect = bitmap->rect(); p->onSrcRectChange(); p->quad.setPosRect(p->srcRect->toFloatRect()); + +#ifdef RGSS2 + p->wave.dirty = true; +#endif } void Sprite::setSrcRect(Rect *rect) @@ -255,6 +397,10 @@ void Sprite::setY(int value) return; p->trans.setPosition(Vec2(getX(), value)); + +#ifdef RGSS2 + p->wave.dirty = true; +#endif } void Sprite::setOX(int value) @@ -296,6 +442,10 @@ void Sprite::setZoomY(float value) p->trans.setScale(Vec2(getZoomX(), value)); p->recomputeBushDepth(); + +#ifdef RGSS2 + p->wave.dirty = true; +#endif } void Sprite::setAngle(float value) @@ -349,6 +499,36 @@ void Sprite::setBlendType(int type) } } +#ifdef RGSS2 + +#define DEF_WAVE_SETTER(Name, name, type) \ + void Sprite::setWave##Name(type value) \ + { \ + GUARD_DISPOSED; \ + if (p->wave.name == value) \ + return; \ + p->wave.name = value; \ + p->wave.dirty = true; \ + } + +DEF_WAVE_SETTER(Amp, amp, int) +DEF_WAVE_SETTER(Length, length, int) +DEF_WAVE_SETTER(Speed, speed, int) +DEF_WAVE_SETTER(Phase, phase, float) + +#undef DEF_WAVE_SETTER + +/* Flashable */ +void Sprite::update() +{ + Flashable::update(); + + p->wave.phase += p->wave.speed / 180; + p->wave.dirty = true; +} + +#endif + /* Disposable */ void Sprite::releaseResources() { @@ -410,7 +590,14 @@ void Sprite::draw() p->bitmap->bindTex(*base); +#ifdef RGSS2 + if (p->wave.active) + p->wave.qArray.draw(); + else + p->quad.draw(); +#else p->quad.draw(); +#endif glState.blendMode.pop(); } diff --git a/src/sprite.h b/src/sprite.h index fd2f631..22ee697 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -41,9 +41,6 @@ public: Sprite(Viewport *viewport = 0); ~Sprite(); - int getWidth() const; - int getHeight() const; - DECL_ATTR( Bitmap, Bitmap* ) DECL_ATTR( SrcRect, Rect* ) DECL_ATTR( X, int ) @@ -61,6 +58,18 @@ public: DECL_ATTR( Color, Color* ) DECL_ATTR( Tone, Tone* ) +#ifdef RGSS2 + int getWidth() const; + int getHeight() const; + + DECL_ATTR( WaveAmp, int ) + DECL_ATTR( WaveLength, int ) + DECL_ATTR( WaveSpeed, int ) + DECL_ATTR( WavePhase, float ) + + void update(); +#endif + private: SpritePrivate *p; diff --git a/src/tileatlas.cpp b/src/tileatlas.cpp index 4f781ef..bbfe85c 100644 --- a/src/tileatlas.cpp +++ b/src/tileatlas.cpp @@ -21,8 +21,6 @@ #include "tileatlas.h" -#include - namespace TileAtlas { @@ -37,8 +35,7 @@ struct Column {} }; -// FIXME: this can be optimized to a vector -typedef std::list ColumnList; +typedef std::vector ColumnVec; /* Autotile area width */ static const int atAreaW = 96*4; @@ -79,9 +76,10 @@ Vec2i minSize(int tilesetH, int maxAtlasSize) return Vec2i(-1, -1); } -static ColumnList calcSrcCols(int tilesetH) +static ColumnVec calcSrcCols(int tilesetH) { - ColumnList cols; + ColumnVec cols; + cols.reserve(2); cols.push_back(Column(0, 0, tilesetH)); cols.push_back(Column(tsLaneW, 0, tilesetH)); @@ -89,9 +87,10 @@ static ColumnList calcSrcCols(int tilesetH) return cols; } -static ColumnList calcDstCols(int atlasW, int atlasH) +static ColumnVec calcDstCols(int atlasW, int atlasH) { - ColumnList cols; + ColumnVec cols; + cols.reserve(3); /* Columns below the autotile area */ const int underAt = atlasH - atAreaH; @@ -110,19 +109,21 @@ static ColumnList calcDstCols(int atlasW, int atlasH) return cols; } -static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) +static BlitVec calcBlitsInt(ColumnVec &srcCols, ColumnVec &dstCols) { - BlitList blits; + BlitVec blits; - while (!srcCols.empty()) + /* Using signed indices here is safer, as we + * might decrement dstI while it is zero. */ + int dstI = 0; + + for (size_t srcI = 0; srcI < srcCols.size(); ++srcI) { - Column srcCol = srcCols.front(); - srcCols.pop_front(); + Column &srcCol = srcCols[srcI]; - while (!dstCols.empty() && srcCol.h > 0) + for (; dstI < (int) dstCols.size() && srcCol.h > 0; ++dstI) { - Column dstCol = dstCols.front(); - dstCols.pop_front(); + Column &dstCol = dstCols[dstI]; if (srcCol.h > dstCol.h) { @@ -141,7 +142,10 @@ static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) dstCol.y += srcCol.h; dstCol.h -= srcCol.h; - dstCols.push_front(dstCol); + + /* Queue this column up again for processing */ + --dstI; + srcCol.h = 0; } else @@ -156,10 +160,10 @@ static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) return blits; } -BlitList calcBlits(int tilesetH, const Vec2i &atlasSize) +BlitVec calcBlits(int tilesetH, const Vec2i &atlasSize) { - ColumnList srcCols = calcSrcCols(tilesetH); - ColumnList dstCols = calcDstCols(atlasSize.x, atlasSize.y); + ColumnVec srcCols = calcSrcCols(tilesetH); + ColumnVec dstCols = calcDstCols(atlasSize.x, atlasSize.y); return calcBlitsInt(srcCols, dstCols); } diff --git a/src/tileatlas.h b/src/tileatlas.h index 458b9b5..04acf6a 100644 --- a/src/tileatlas.h +++ b/src/tileatlas.h @@ -46,16 +46,17 @@ struct Blit {} }; -typedef std::vector BlitList; +typedef std::vector BlitVec; /* Calculates the minimum atlas size required to hold * a tileset of height 'tilesetH'. If the required dimensions * exceed 'maxAtlasSize', Vec2i(-1, -1) is returned. */ Vec2i minSize(int tilesetH, int maxAtlasSize); -/* Calculates a series of blits necessary to fill dstRows - * with srcRows without wasting any space */ -BlitList calcBlits(int tilesetH, const Vec2i &atlasSize); +/* Calculates a series of blits necessary to fill an atlas + * of size 'atlasSize' with a tileset of height 'tilesetH'. + * Usually fed results from 'minSize()'. */ +BlitVec calcBlits(int tilesetH, const Vec2i &atlasSize); /* Translates a tile coordinate (not pixel!) to a physical * pixel coordinate in the atlas */ diff --git a/src/tilemap.cpp b/src/tilemap.cpp index e6b7fd1..2cdf6ba 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -48,13 +48,6 @@ extern const StaticRect autotileRects[]; typedef std::vector SVVector; typedef struct { SVVector v[4]; } TileVBuffer; -/* Check if [C]ontainer contains [V]alue */ -template -inline bool contains(const C &c, const V &v) -{ - return std::find(c.begin(), c.end(), v) != c.end(); -} - static const int tilesetW = 8 * 32; static const int autotileW = 3 * 32; static const int autotileH = 4 * 32; @@ -511,8 +504,6 @@ struct TilemapPrivate usableATs.push_back(i); - autotiles[i]->flush(); - if (autotiles[i]->width() > autotileW) animatedATs.push_back(i); } @@ -623,14 +614,9 @@ struct TilemapPrivate /* Assembles atlas from tileset and autotile bitmaps */ void buildAtlas() { - tileset->flush(); - updateAutotileInfo(); - for (size_t i = 0; i < atlas.usableATs.size(); ++i) - autotiles[atlas.usableATs[i]]->flush(); - - TileAtlas::BlitList blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size); + TileAtlas::BlitVec blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size); /* Clear atlas */ FBO::bind(atlas.gl.fbo, FBO::Draw); diff --git a/src/util.h b/src/util.h index 6a77ac2..17f76e4 100644 --- a/src/util.h +++ b/src/util.h @@ -24,6 +24,7 @@ #include #include +#include static inline int wrapRange(int value, int min, int max) @@ -93,6 +94,13 @@ inline void strReplace(std::string &str, str[i] = after; } +/* Check if [C]ontainer contains [V]alue */ +template +inline bool contains(const C &c, const V &v) +{ + return std::find(c.begin(), c.end(), v) != c.end(); +} + #define ARRAY_SIZE(obj) (sizeof(obj) / sizeof((obj)[0])) #define elementsN(obj) const int obj##N = ARRAY_SIZE(obj) diff --git a/src/window.cpp b/src/window.cpp index 0fd7198..4eb3a88 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -508,12 +508,6 @@ struct WindowPrivate { bool updateBaseQuadArray = false; - if (windowskin) - windowskin->flush(); - - if (contents) - contents->flush(); - if (baseVertDirty) { buildBaseVert();