diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f4ccfd..b1383ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,11 @@ set(MAIN_SOURCE src/fluid-fun.cpp ) +if(WIN32) + list(APPEND MAIN_HEADERS windows/resource.h) + list(APPEND MAIN_SOURCE windows/resource.rc) +endif() + source_group("MKXP Source" FILES ${MAIN_SOURCE} ${MAIN_HEADERS}) ## Setup embedded source ## @@ -405,6 +410,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE ) target_include_directories(${PROJECT_NAME} PRIVATE src + windows ${SIGCXX_INCLUDE_DIRS} ${PIXMAN_INCLUDE_DIRS} ${PHYSFS_INCLUDE_DIRS} diff --git a/binding-mri/audio-binding.cpp b/binding-mri/audio-binding.cpp index 883f958..29735c2 100644 --- a/binding-mri/audio-binding.cpp +++ b/binding-mri/audio-binding.cpp @@ -115,6 +115,15 @@ DEF_PLAY_STOP( se ) DEF_AUD_PROP_I(BGM_Volume) DEF_AUD_PROP_I(SFX_Volume) +RB_METHOD(audioSetupMidi) +{ + RB_UNUSED_PARAM; + + shState->audio().setupMidi(); + + return Qnil; +} + RB_METHOD(audioReset) { RB_UNUSED_PARAM; @@ -158,6 +167,8 @@ audioBindingInit() { BIND_POS( bgm ); BIND_POS( bgs ); + + _rb_define_module_function(module, "setup_midi", audioSetupMidi); } BIND_PLAY_STOP( se ) diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index d50e7e3..087b7fe 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -85,6 +85,7 @@ RB_METHOD(mriP); RB_METHOD(mkxpDataDirectory); RB_METHOD(mkxpPuts); RB_METHOD(mkxpRawKeyStates); +RB_METHOD(mkxpMouseInWindow); RB_METHOD(mriRgssMain); RB_METHOD(mriRgssStop); @@ -140,8 +141,18 @@ static void mriBindingInit() _rb_define_module_function(mod, "data_directory", mkxpDataDirectory); _rb_define_module_function(mod, "puts", mkxpPuts); _rb_define_module_function(mod, "raw_key_states", mkxpRawKeyStates); + _rb_define_module_function(mod, "mouse_in_window", mkxpMouseInWindow); + /* Load global constants */ rb_gv_set("MKXP", Qtrue); + + VALUE debug = rb_bool_new(shState->config().editor.debug); + if (rgssVer == 1) + rb_gv_set("DEBUG", debug); + else if (rgssVer >= 2) + rb_gv_set("TEST", debug); + + rb_gv_set("BTEST", rb_bool_new(shState->config().editor.battleTest)); } static void @@ -222,6 +233,13 @@ RB_METHOD(mkxpRawKeyStates) return str; } +RB_METHOD(mkxpMouseInWindow) +{ + RB_UNUSED_PARAM; + + return rb_bool_new(EventThread::mouseState.inWindow); +} + static VALUE rgssMainCb(VALUE block) { rb_funcall2(block, rb_intern("call"), 0, 0); @@ -455,8 +473,9 @@ static void runRMXPScripts(BacktraceData &btData) } /* Execute preloaded scripts */ - for (size_t i = 0; i < conf.preloadScripts.size(); ++i) - runCustomScript(conf.preloadScripts[i]); + for (std::set::iterator i = conf.preloadScripts.begin(); + i != conf.preloadScripts.end(); ++i) + runCustomScript(*i); VALUE exc = rb_gv_get("$!"); if (exc != Qnil) diff --git a/binding-mri/binding-util.h b/binding-mri/binding-util.h index 915c6d6..2a7ab56 100644 --- a/binding-mri/binding-util.h +++ b/binding-mri/binding-util.h @@ -67,7 +67,7 @@ raiseRbExc(const Exception &exc); /* 2.1 has added a new field (flags) to rb_data_type_t */ #include -#if RUBY_API_VERSION_MINOR > 0 +#if RUBY_API_VERSION_MAJOR >= 2 && RUBY_API_VERSION_MINOR >= 1 /* TODO: can mkxp use RUBY_TYPED_FREE_IMMEDIATELY here? */ #define DEF_TYPE_FLAGS 0 #else diff --git a/oneshot.conf.sample b/oneshot.conf.sample index cd628fb..0cb2b5e 100644 --- a/oneshot.conf.sample +++ b/oneshot.conf.sample @@ -51,6 +51,12 @@ # vsync=true +# Override the game window title +# (default: none) +# +# windowTitle=Custom Title + + # Enforce a static frame rate # (0 = disabled) # @@ -89,6 +95,26 @@ # subImageFix=false +# Enable framebuffer blitting if the driver is +# capable of it. Some drivers carry buggy +# implementations of this functionality, so +# disabling it can be used as a workaround +# (default: enabled) +# +# enableBlitting=true + + +# Limit the maximum size (width, height) of +# most textures mkxp will create (exceptions are +# rendering backbuffers and similar). +# If set to 0, the hardware maximum is used. +# This is useful for recording traces that can +# be played back on machines with lower specs. +# (default: 0) +# +# maxTextureSize=0 + + # Set the base path of the game to '/path/to/game' # (default: executable directory) # diff --git a/shader/hue.frag b/shader/hue.frag index 405c91b..61143ac 100644 --- a/shader/hue.frag +++ b/shader/hue.frag @@ -1,48 +1,40 @@ -uniform sampler2D inputTexture; -uniform float hueAdjust; +uniform sampler2D texture; +uniform mediump float hueAdjust; varying vec2 v_texCoord; +/* Source: gamedev.stackexchange.com/a/59808/24839 */ +vec3 rgb2hsv(vec3 c) +{ + const vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + + /* Avoid divide-by-zero situations by adding a very tiny delta. + * Since we always deal with underlying 8-Bit color values, this + * should never mask a real value */ + const float eps = 1.0e-10; + + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + eps)), d / (q.x + eps), q.x); +} + +vec3 hsv2rgb(vec3 c) +{ + const vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + void main () { - const vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0); - const vec4 kRGBToI = vec4 (0.596, -0.275, -0.321, 0.0); - const vec4 kRGBToQ = vec4 (0.212, -0.523, 0.311, 0.0); + vec4 color = texture2D (texture, v_texCoord.xy); + vec3 hsv = rgb2hsv(color.rgb); - const vec4 kYIQToR = vec4 (1.0, 0.956, 0.621, 0.0); - const vec4 kYIQToG = vec4 (1.0, -0.272, -0.647, 0.0); - const vec4 kYIQToB = vec4 (1.0, -1.107, 1.704, 0.0); + hsv.x += hueAdjust; + color.rgb = hsv2rgb(hsv); - /* Sample the input pixel */ - vec4 color = texture2D (inputTexture, v_texCoord.xy); - - /* Convert to YIQ */ - float YPrime = dot (color, kRGBToYPrime); - float I = dot (color, kRGBToI); - float Q = dot (color, kRGBToQ); - - /* Calculate the hue and chroma */ - float hue = atan (Q, I); - float chroma = sqrt (I * I + Q * Q); - - /* Make the user's adjustments */ - hue += hueAdjust; - - /* Remember old I and color */ - float IOriginal = I; - vec4 coOriginal = color; - - /* Convert back to YIQ */ - Q = chroma * sin (hue); - I = chroma * cos (hue); - - /* Convert back to RGB */ - vec4 yIQ = vec4 (YPrime, I, Q, 0.0); - color.r = dot (yIQ, kYIQToR); - color.g = dot (yIQ, kYIQToG); - color.b = dot (yIQ, kYIQToB); - - /* Save the result */ - gl_FragColor = (IOriginal == 0.0) ? coOriginal : color; + gl_FragColor = color; } diff --git a/src/bitmap.cpp b/src/bitmap.cpp index a904888..e9556e0 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -221,9 +221,9 @@ struct BitmapPrivate surf = surfConv; } - void onModified() + void onModified(bool freeSurface = true) { - if (surface) + if (surface && freeSurface) { SDL_FreeSurface(surface); surface = 0; @@ -768,6 +768,14 @@ void Bitmap::clear() p->onModified(); } +static uint32_t &getPixelAt(SDL_Surface *surf, SDL_PixelFormat *form, int x, int y) +{ + size_t offset = x*form->BytesPerPixel + y*surf->pitch; + uint8_t *bytes = (uint8_t*) surf->pixels + offset; + + return *((uint32_t*) bytes); +} + Color Bitmap::getPixel(int x, int y) const { guardDisposed(); @@ -790,9 +798,7 @@ Color Bitmap::getPixel(int x, int y) const glState.viewport.pop(); } - size_t offset = x*p->format->BytesPerPixel + y*p->surface->pitch; - uint8_t *bytes = (uint8_t*) p->surface->pixels + offset; - uint32_t pixel = *((uint32_t*) bytes); + uint32_t pixel = getPixelAt(p->surface, p->format, x, y); return Color((pixel >> p->format->Rshift) & 0xFF, (pixel >> p->format->Gshift) & 0xFF, @@ -819,7 +825,16 @@ void Bitmap::setPixel(int x, int y, const Color &color) p->addTaintedArea(IntRect(x, y, 1, 1)); - p->onModified(); + /* Setting just a single pixel is no reason to throw away the + * whole cached surface; we can just apply the same change */ + + if (p->surface) + { + uint32_t &surfPixel = getPixelAt(p->surface, p->format, x, y); + surfPixel = SDL_MapRGBA(p->format, pixel[0], pixel[1], pixel[2], pixel[3]); + } + + p->onModified(false); } void Bitmap::hueChange(int hue) @@ -839,13 +854,10 @@ void Bitmap::hueChange(int hue) quad.setTexPosRect(texRect, texRect); quad.setColor(Vec4(1, 1, 1, 1)); - /* Calculate hue parameter */ - hue = wrapRange(hue, 0, 359); - float hueAdj = -((M_PI * 2) / 360) * hue; - HueShader &shader = shState->shaders().hue; shader.bind(); - shader.setHueAdjust(hueAdj); + /* Shader expects normalized value */ + shader.setHueAdjust(wrapRange(hue, 0, 359) / 360.0f); FBO::bind(newTex.fbo); p->pushSetViewport(shader); diff --git a/src/config.cpp b/src/config.cpp index bf3ff78..ec6c8af 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -48,6 +48,12 @@ static std::string prefPath(const char *org, const char *app) return str; } +template +std::set setFromVec(const std::vector &vec) +{ + return std::set(vec.begin(), vec.end()); +} + typedef std::vector StringVec; namespace po = boost::program_options; @@ -66,11 +72,16 @@ void Config::read(int argc, char *argv[]) PO_DESC(fixedAspectRatio, bool, true) \ PO_DESC(smoothScaling, bool, true) \ PO_DESC(vsync, bool, true) \ + PO_DESC(defScreenW, int, 0) \ + PO_DESC(defScreenH, int, 0) \ + PO_DESC(windowTitle, std::string, "") \ PO_DESC(fixedFramerate, int, 0) \ PO_DESC(frameSkip, bool, true) \ PO_DESC(syncToRefreshrate, bool, false) \ PO_DESC(solidFonts, bool, false) \ PO_DESC(subImageFix, bool, false) \ + PO_DESC(enableBlitting, bool, true) \ + PO_DESC(maxTextureSize, int, 0) \ PO_DESC(gameFolder, std::string, ".") \ PO_DESC(allowSymlinks, bool, false) \ PO_DESC(iconPath, std::string, "") \ @@ -80,6 +91,27 @@ void Config::read(int argc, char *argv[]) // Not gonna take your shit boost #define GUARD_ALL( exp ) try { exp } catch(...) {} + editor.debug = false; + editor.battleTest = false; + + /* Read arguments sent from the editor */ + if (argc > 1) + { + std::string argv1 = argv[1]; + /* RGSS1 uses "debug", 2 and 3 use "test" */ + if (argv1 == "debug" || argv1 == "test") + editor.debug = true; + else if (argv1 == "btest") + editor.battleTest = true; + + /* Fix offset */ + if (editor.debug || editor.battleTest) + { + argc--; + argv++; + } + } + #define PO_DESC(key, type, def) (#key, po::value< type >()->default_value(def)) po::options_description podesc; @@ -125,7 +157,7 @@ void Config::read(int argc, char *argv[]) PO_DESC_ALL; - GUARD_ALL( preloadScripts = vm["preloadScript"].as(); ); + GUARD_ALL( preloadScripts = setFromVec(vm["preloadScript"].as()); ); GUARD_ALL( fontSubs = vm["fontSub"].as(); ); diff --git a/src/config.h b/src/config.h index 5e10cc2..270e16a 100644 --- a/src/config.h +++ b/src/config.h @@ -24,6 +24,7 @@ #include #include +#include struct Config { @@ -40,6 +41,7 @@ struct Config int defScreenW; int defScreenH; + std::string windowTitle; int fixedFramerate; bool frameSkip; @@ -48,6 +50,8 @@ struct Config bool solidFonts; bool subImageFix; + bool enableBlitting; + int maxTextureSize; std::string gameFolder; bool allowSymlinks; @@ -60,12 +64,22 @@ struct Config int sourceCount; } SE; - std::vector preloadScripts; + bool useScriptNames; + + std::string customScript; + std::set preloadScripts; + std::vector rtps; std::vector fontSubs; std::vector rubyLoadpaths; + /* Editor flags */ + struct { + bool debug; + bool battleTest; + } editor; + /* Game INI contents */ struct { std::string scripts; diff --git a/src/eventthread.cpp b/src/eventthread.cpp index ab1cd75..1d2a605 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,7 @@ enum REQUEST_SETCURSORVISIBLE, UPDATE_FPS, + UPDATE_SCREEN_RECT, EVENT_COUNT }; @@ -137,6 +139,8 @@ void EventThread::process(RGSSThreadData &rtData) bool displayingFPS = false; bool cursorInWindow = false; + /* Will be updated eventually */ + SDL_Rect gameScreen = { 0, 0, 0, 0 }; /* SDL doesn't send an initial FOCUS_GAINED event */ bool windowFocused = true; @@ -194,7 +198,7 @@ void EventThread::process(RGSSThreadData &rtData) delete sMenu; sMenu = 0; - updateCursorState(cursorInWindow && windowFocused); + updateCursorState(cursorInWindow && windowFocused, gameScreen); } continue; @@ -237,14 +241,14 @@ void EventThread::process(RGSSThreadData &rtData) case SDL_WINDOWEVENT_ENTER : cursorInWindow = true; mouseState.inWindow = true; - updateCursorState(cursorInWindow && windowFocused && !sMenu); + updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen); break; case SDL_WINDOWEVENT_LEAVE : cursorInWindow = false; mouseState.inWindow = false; - updateCursorState(cursorInWindow && windowFocused && !sMenu); + updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen); break; @@ -259,13 +263,13 @@ void EventThread::process(RGSSThreadData &rtData) case SDL_WINDOWEVENT_FOCUS_GAINED : windowFocused = true; - updateCursorState(cursorInWindow && windowFocused && !sMenu); + updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen); break; case SDL_WINDOWEVENT_FOCUS_LOST : windowFocused = false; - updateCursorState(cursorInWindow && windowFocused && !sMenu); + updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen); resetInputStates(); break; @@ -292,7 +296,7 @@ void EventThread::process(RGSSThreadData &rtData) if (!sMenu) { sMenu = new SettingsMenu(rtData); - updateCursorState(false); + updateCursorState(false, gameScreen); } sMenu->raise(); @@ -316,14 +320,14 @@ void EventThread::process(RGSSThreadData &rtData) if (fullscreen) { /* Prevent fullscreen flicker */ - strncpy(pendingTitle, rtData.config.game.title.c_str(), + strncpy(pendingTitle, rtData.config.windowTitle.c_str(), sizeof(pendingTitle)); havePendingTitle = true; break; } - SDL_SetWindowTitle(win, rtData.config.game.title.c_str()); + SDL_SetWindowTitle(win, rtData.config.windowTitle.c_str()); } break; @@ -440,6 +444,7 @@ void EventThread::process(RGSSThreadData &rtData) case SDL_MOUSEMOTION : mouseState.x = event.motion.x; mouseState.y = event.motion.y; + updateCursorState(cursorInWindow, gameScreen); break; case SDL_FINGERDOWN : @@ -471,7 +476,7 @@ void EventThread::process(RGSSThreadData &rtData) case REQUEST_MESSAGEBOX : SDL_ShowSimpleMessageBox(event.user.code, - rtData.config.game.title.c_str(), + rtData.config.windowTitle.c_str(), (const char*) event.user.data1, win); free(event.user.data1); msgBoxDone.set(); @@ -479,7 +484,7 @@ void EventThread::process(RGSSThreadData &rtData) case REQUEST_SETCURSORVISIBLE : showCursor = event.user.code; - updateCursorState(cursorInWindow); + updateCursorState(cursorInWindow, gameScreen); break; case UPDATE_FPS : @@ -490,7 +495,7 @@ void EventThread::process(RGSSThreadData &rtData) break; snprintf(buffer, sizeof(buffer), "%s - %d FPS", - rtData.config.game.title.c_str(), event.user.code); + rtData.config.windowTitle.c_str(), event.user.code); /* Updating the window title in fullscreen * mode seems to cause flickering */ @@ -504,6 +509,15 @@ void EventThread::process(RGSSThreadData &rtData) SDL_SetWindowTitle(win, buffer); break; + + case UPDATE_SCREEN_RECT : + gameScreen.x = event.user.windowID; + gameScreen.y = event.user.code; + gameScreen.w = reinterpret_cast(event.user.data1); + gameScreen.h = reinterpret_cast(event.user.data2); + updateCursorState(cursorInWindow, gameScreen); + + break; } } @@ -614,9 +628,13 @@ void EventThread::setFullscreen(SDL_Window *win, bool mode) fullscreen = mode; } -void EventThread::updateCursorState(bool inWindow) +void EventThread::updateCursorState(bool inWindow, + const SDL_Rect &screen) { - if (inWindow) + SDL_Point pos = { mouseState.x, mouseState.y }; + bool inScreen = inWindow && SDL_PointInRect(&pos, &screen); + + if (inScreen) SDL_ShowCursor(showCursor ? SDL_TRUE : SDL_FALSE); else SDL_ShowCursor(SDL_TRUE); @@ -722,6 +740,19 @@ void EventThread::notifyFrame() SDL_PushEvent(&event); } +void EventThread::notifyGameScreenChange(const SDL_Rect &screen) +{ + /* We have to get a bit hacky here to fit the rectangle + * data into the user event struct */ + SDL_Event event; + event.type = usrIdStart + UPDATE_SCREEN_RECT; + event.user.windowID = screen.x; + event.user.code = screen.y; + event.user.data1 = reinterpret_cast(screen.w); + event.user.data2 = reinterpret_cast(screen.h); + SDL_PushEvent(&event); +} + void SyncPoint::haltThreads() { if (mainSync.locked) diff --git a/src/eventthread.h b/src/eventthread.h index 510d5f4..729cc67 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -106,12 +106,16 @@ public: /* RGSS thread calls this once per frame */ void notifyFrame(); + /* Called on game screen (size / offset) changes */ + void notifyGameScreenChange(const SDL_Rect &screen); + private: static int eventFilter(void *, SDL_Event*); void resetInputStates(); void setFullscreen(SDL_Window *, bool mode); - void updateCursorState(bool inWindow); + void updateCursorState(bool inWindow, + const SDL_Rect &screen); bool fullscreen; bool showCursor; diff --git a/src/filesystem.cpp b/src/filesystem.cpp index add0d62..ebd7b4c 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -465,7 +465,7 @@ struct FontSetsCBData }; static PHYSFS_EnumerateCallbackResult -fontSetEnumCB(void *data, const char *, const char *fname) +fontSetEnumCB (void *data, const char *dir, const char *fname) { FontSetsCBData *d = static_cast(data); @@ -486,7 +486,7 @@ fontSetEnumCB(void *data, const char *, const char *fname) return PHYSFS_ENUM_STOP; char filename[512]; - snprintf(filename, sizeof(filename), "Fonts/%s", fname); + snprintf(filename, sizeof(filename), "%s/%s", dir, fname); PHYSFS_File *handle = PHYSFS_openRead(filename); @@ -503,6 +503,26 @@ fontSetEnumCB(void *data, const char *, const char *fname) return PHYSFS_ENUM_OK; } +/* Basically just a case-insensitive search + * for the folder "Fonts"... */ +static PHYSFS_EnumerateCallbackResult +findFontsFolderCB(void *data, const char *, const char *fname) +{ + size_t i = 0; + char buffer[512]; + const char *s = fname; + + while (*s && i < sizeof(buffer)) + buffer[i++] = tolower(*s++); + + buffer[i] = '\0'; + + if (strcmp(buffer, "fonts") == 0) + PHYSFS_enumerate(fname, fontSetEnumCB, data); + + return PHYSFS_ENUM_OK; +} + void FileSystem::initFontSets(SharedFontState &sfs) { FontSetsCBData d = { p, &sfs }; diff --git a/src/glstate.cpp b/src/glstate.cpp index 73aec31..ff88de7 100644 --- a/src/glstate.cpp +++ b/src/glstate.cpp @@ -23,6 +23,7 @@ #include "shader.h" #include "etc.h" #include "gl-fun.h" +#include "config.h" #include @@ -111,7 +112,7 @@ GLState::Caps::Caps() gl.GetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); } -GLState::GLState() +GLState::GLState(const Config &conf) { gl.Disable(GL_DEPTH_TEST); @@ -121,4 +122,7 @@ GLState::GLState() scissorTest.init(false); scissorBox.init(IntRect(0, 0, 640, 480)); program.init(0); + + if (conf.maxTextureSize > 0) + caps.maxTexSize = conf.maxTextureSize; } diff --git a/src/glstate.h b/src/glstate.h index b8be5c5..eff0257 100644 --- a/src/glstate.h +++ b/src/glstate.h @@ -27,6 +27,8 @@ #include #include +struct Config; + template struct GLProperty { @@ -130,7 +132,7 @@ public: } caps; - GLState(); + GLState(const Config &conf); }; #endif // GLSTATE_H diff --git a/src/graphics.cpp b/src/graphics.cpp index ddbbcf0..a9be445 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -101,11 +101,9 @@ struct PingPong { screenW = width; screenH = height; + for (int i = 0; i < 2; ++i) - { - TEX::bind(rt[i].tex); - TEX::allocEmpty(width, height); - } + TEXFBO::allocEmpty(rt[i], width, height); } void startRender() @@ -486,9 +484,7 @@ struct GraphicsPrivate bool frozen; TEXFBO frozenScene; - TEXFBO currentScene; Quad screenQuad; - TEXFBO transBuffer; /* Global list of all live Disposables * (disposed on reset) */ @@ -516,17 +512,9 @@ struct GraphicsPrivate TEXFBO::allocEmpty(frozenScene, scRes.x, scRes.y); TEXFBO::linkFBO(frozenScene); - TEXFBO::init(currentScene); - TEXFBO::allocEmpty(currentScene, scRes.x, scRes.y); - TEXFBO::linkFBO(currentScene); - FloatRect screenRect(0, 0, scRes.x, scRes.y); screenQuad.setTexPosRect(screenRect, screenRect); - TEXFBO::init(transBuffer); - TEXFBO::allocEmpty(transBuffer, scRes.x, scRes.y); - TEXFBO::linkFBO(transBuffer); - fpsLimiter.resetFrameAdjust(); obscuredTex = TEX::gen(); @@ -539,9 +527,6 @@ struct GraphicsPrivate ~GraphicsPrivate() { TEXFBO::fini(frozenScene); - TEXFBO::fini(currentScene); - - TEXFBO::fini(transBuffer); } void updateScreenResoRatio(RGSSThreadData *rtData) @@ -584,6 +569,9 @@ struct GraphicsPrivate glState.viewport.refresh(); recalculateScreenSize(threadData); updateScreenResoRatio(threadData); + + SDL_Rect screen = { scOffset.x, scOffset.y, scSize.x, scSize.y }; + threadData->ethread->notifyGameScreenChange(screen); } } @@ -755,8 +743,15 @@ void Graphics::transition(int duration, setBrightness(255); + /* The PP frontbuffer will hold the current scene after the + * composition step. Since the backbuffer is unused during + * the transition, we can reuse it as the target buffer for + * the final rendered image. */ + TEXFBO ¤tScene = p->screen.getPP().frontBuffer(); + TEXFBO &transBuffer = p->screen.getPP().backBuffer(); + /* Capture new scene */ - p->compositeToBuffer(p->currentScene); + p->screen.composite(); /* If no transition bitmap is provided, * we can use a simplified shader */ @@ -769,7 +764,7 @@ void Graphics::transition(int duration, shader.bind(); shader.applyViewportProj(); shader.setFrozenScene(p->frozenScene.tex); - shader.setCurrentScene(p->currentScene.tex); + shader.setCurrentScene(currentScene.tex); shader.setTransMap(transMap->getGLTypes().tex); shader.setVague(vague / 256.0f); shader.setTexSize(p->scRes); @@ -780,7 +775,7 @@ void Graphics::transition(int duration, shader.bind(); shader.applyViewportProj(); shader.setFrozenScene(p->frozenScene.tex); - shader.setCurrentScene(p->currentScene.tex); + shader.setCurrentScene(currentScene.tex); shader.setTexSize(p->scRes); } @@ -830,7 +825,7 @@ void Graphics::transition(int duration, /* Draw the composed frame to a buffer first * (we need this because we're skipping PingPong) */ - FBO::bind(p->transBuffer.fbo); + FBO::bind(transBuffer.fbo); FBO::clear(); p->screenQuad.draw(); @@ -841,7 +836,7 @@ void Graphics::transition(int duration, FBO::clear(); GLMeta::blitBeginScreen(Vec2i(p->winSize)); - GLMeta::blitSource(p->transBuffer); + GLMeta::blitSource(transBuffer); p->metaBlitBufferFlippedScaled(); GLMeta::blitEnd(); @@ -984,17 +979,11 @@ void Graphics::resizeScreen(int width, int height) p->screen.setResolution(width, height); - TEX::bind(p->frozenScene.tex); - TEX::allocEmpty(width, height); - TEX::bind(p->currentScene.tex); - TEX::allocEmpty(width, height); + TEXFBO::allocEmpty(p->frozenScene, width, height); FloatRect screenRect(0, 0, width, height); p->screenQuad.setTexPosRect(screenRect, screenRect); - TEX::bind(p->transBuffer.tex); - TEX::allocEmpty(width, height); - shState->eThread().requestWindowResize(width, height); } diff --git a/src/input.cpp b/src/input.cpp index d73b706..e1ed663 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -742,9 +742,6 @@ int Input::mouseX() { RGSSThreadData &rtData = shState->rtData(); - if (!EventThread::mouseState.inWindow) - return -20; - return (EventThread::mouseState.x - rtData.screenOffset.x) * rtData.sizeResoRatio.x; } @@ -752,9 +749,6 @@ int Input::mouseY() { RGSSThreadData &rtData = shState->rtData(); - if (!EventThread::mouseState.inWindow) - return -20; - return (EventThread::mouseState.y - rtData.screenOffset.y) * rtData.sizeResoRatio.y; } diff --git a/src/main.cpp b/src/main.cpp index 1f7e4c6..ee52d25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,10 @@ #include "binding.h" +#ifdef __WINDOWS__ +#include "resource.h" +#endif + #include "icon.png.xxd" #ifdef STEAM @@ -106,6 +110,9 @@ int rgssThreadFun(void *userdata) return 0; } + if (!conf.enableBlitting) + gl.BlitFramebuffer = 0; + gl.ClearColor(0, 0, 0, 1); gl.Clear(GL_COLOR_BUFFER_BIT); SDL_GL_SwapWindow(win); @@ -165,6 +172,24 @@ static void showInitError(const std::string &msg) SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OneShot", msg.c_str(), 0); } +static void setupWindowIcon(const Config &conf, SDL_Window *win) +{ + SDL_RWops *iconSrc; + + if (conf.iconPath.empty()) + iconSrc = SDL_RWFromConstMem(assets_icon_png, assets_icon_png_len); + else + iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb"); + + SDL_Surface *iconImg = IMG_Load_RW(iconSrc, SDL_TRUE); + + if (iconImg) + { + SDL_SetWindowIcon(win, iconImg); + SDL_FreeSurface(iconImg); + } +} + int main(int argc, char *argv[]) { SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); @@ -218,7 +243,10 @@ int main(int argc, char *argv[]) if (conf.screenMode) return screenMain(conf); - int imgFlags = IMG_INIT_PNG; + if (conf.windowTitle.empty()) + conf.windowTitle = conf.game.title; + + int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG; if (IMG_Init(imgFlags) != imgFlags) { showInitError(std::string("Error initializing SDL_image: ") + SDL_GetError()); @@ -246,23 +274,13 @@ int main(int argc, char *argv[]) return 0; } - /* Setup application icon */ - SDL_RWops *iconSrc; - - if (conf.iconPath.empty()) - iconSrc = SDL_RWFromConstMem(assets_icon_png, assets_icon_png_len); - else - iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb"); - - SDL_Surface *iconImg = IMG_Load_RW(iconSrc, SDL_TRUE); - SDL_Window *win; Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_RESIZABLE; //| SDL_WINDOW_ALLOW_HIGHDPI; if (conf.fullscreen) winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - win = SDL_CreateWindow(conf.game.title.c_str(), + win = SDL_CreateWindow(conf.windowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, conf.defScreenW, conf.defScreenH, winFlags); @@ -272,11 +290,13 @@ int main(int argc, char *argv[]) return 0; } - if (iconImg) - { - SDL_SetWindowIcon(win, iconImg); - SDL_FreeSurface(iconImg); - } + /* OSX and Windows have their own native ways of + * dealing with icons; don't interfere with them */ +#ifdef __LINUX__ + setupWindowIcon(conf, win); +#else + (void) setupWindowIcon; +#endif ALCdevice *alcDev = alcOpenDevice(0); @@ -345,13 +365,13 @@ int main(int argc, char *argv[]) if (rtData.rqTermAck) SDL_WaitThread(rgssThread, 0); else - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(), + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.windowTitle.c_str(), "The RGSS script seems to be stuck and mkxp will now force quit", win); if (!rtData.rgssErrorMsg.empty()) { Debug() << rtData.rgssErrorMsg; - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(), + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.windowTitle.c_str(), rtData.rgssErrorMsg.c_str(), win); } diff --git a/src/rgssad.cpp b/src/rgssad.cpp index 4b51472..ac8772a 100644 --- a/src/rgssad.cpp +++ b/src/rgssad.cpp @@ -297,6 +297,8 @@ processDirectories(RGSS_archiveData *data, BoostSet &topLevel, if (slash) nameBuf[i] = '/'; + + break; } /* Check for more entries */ diff --git a/src/shader.cpp b/src/shader.cpp index 14ec4ff..6d763c2 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -551,7 +551,6 @@ HueShader::HueShader() ShaderBase::init(); GET_U(hueAdjust); - GET_U(inputTexture); } void HueShader::setHueAdjust(float value) @@ -559,11 +558,6 @@ void HueShader::setHueAdjust(float value) gl.Uniform1f(u_hueAdjust, value); } -void HueShader::setInputTexture(TEX::ID tex) -{ - setTexUniform(u_inputTexture, 0, tex); -} - SimpleMatrixShader::SimpleMatrixShader() { diff --git a/src/shader.h b/src/shader.h index b9ab4d9..f2c9f25 100644 --- a/src/shader.h +++ b/src/shader.h @@ -241,10 +241,9 @@ public: HueShader(); void setHueAdjust(float value); - void setInputTexture(TEX::ID tex); private: - GLint u_hueAdjust, u_inputTexture; + GLint u_hueAdjust; }; class SimpleMatrixShader : public ShaderBase diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index dbfbe30..7f4f455 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -102,6 +102,7 @@ struct SharedStatePrivate input(*threadData), audio(*threadData), oneshot(*threadData), + _glState(threadData->config), fontState(threadData->config), stampCounter(0) { diff --git a/src/sprite.cpp b/src/sprite.cpp index 51b57e4..95b0c85 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -126,7 +126,7 @@ struct SpritePrivate void recomputeBushDepth() { - if (!bitmap) + if (nullOrDisposed(bitmap)) return; /* Calculate effective (normalized) bush depth */ @@ -139,12 +139,20 @@ struct SpritePrivate void onSrcRectChange() { - if (mirrored) - quad.setTexRect(srcRect->toFloatRect().hFlipped()); - else - quad.setTexRect(srcRect->toFloatRect()); + FloatRect rect = srcRect->toFloatRect(); + Vec2i bmSize; - quad.setPosRect(IntRect(0, 0, srcRect->width, srcRect->height)); + if (!nullOrDisposed(bitmap)) + bmSize = Vec2i(bitmap->width(), bitmap->height()); + + /* Clamp the rectangle so it doesn't reach outside + * the bitmap bounds */ + rect.w = clamp(rect.w, 0, bmSize.x-rect.x); + rect.h = clamp(rect.h, 0, bmSize.y-rect.y); + + quad.setTexRect(mirrored ? rect.hFlipped() : rect); + + quad.setPosRect(FloatRect(0, 0, rect.w, rect.h)); recomputeBushDepth(); wave.dirty = true; diff --git a/windows/icon.ico b/windows/icon.ico new file mode 100644 index 0000000..2a10780 Binary files /dev/null and b/windows/icon.ico differ diff --git a/windows/resource.h b/windows/resource.h new file mode 100644 index 0000000..32015bd --- /dev/null +++ b/windows/resource.h @@ -0,0 +1 @@ +#define IDI_APPICON 101 diff --git a/windows/resource.rc b/windows/resource.rc new file mode 100644 index 0000000..293e38d --- /dev/null +++ b/windows/resource.rc @@ -0,0 +1,4 @@ +#include +#include "resource.h" + +IDI_APPICON ICON "icon.ico"