From 66685a2ade060834b260d3508690cea8d8b92c97 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Wed, 9 Dec 2015 22:19:41 +0100 Subject: [PATCH] =?UTF-8?q?Updated=20L=C3=96VE=20source=20to=20revision=20?= =?UTF-8?q?cff5bc8604bd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jni/love/src/common/Module.h | 1 + jni/love/src/common/Object.h | 10 + jni/love/src/common/Stream.h | 64 + jni/love/src/common/Variant.cpp | 8 +- jni/love/src/common/Variant.h | 2 +- jni/love/src/common/config.h | 2 + jni/love/src/common/runtime.cpp | 38 +- jni/love/src/common/runtime.h | 19 +- jni/love/src/common/types.cpp | 104 +- jni/love/src/common/types.h | 10 +- jni/love/src/common/version.h | 2 +- jni/love/src/common/wrap_Data.cpp | 2 +- jni/love/src/common/wrap_Data.h | 4 +- .../Box2D/Collision/Shapes/b2ChainShape.cpp | 9 +- .../Box2D/Collision/Shapes/b2ChainShape.h | 3 + .../Box2D/Collision/Shapes/b2PolygonShape.cpp | 10 +- jni/love/src/libraries/Box2D/Common/b2Draw.h | 6 +- jni/love/src/libraries/Box2D/Common/b2Math.h | 1 - .../src/libraries/Box2D/Common/b2Settings.cpp | 2 +- .../Dynamics/Contacts/b2ContactSolver.cpp | 41 +- .../Dynamics/Joints/b2PrismaticJoint.cpp | 3 + .../Box2D/Dynamics/Joints/b2WeldJoint.cpp | 18 +- .../Box2D/Dynamics/Joints/b2WheelJoint.h | 5 +- .../src/libraries/Box2D/Dynamics/b2World.cpp | 38 +- .../src/libraries/Box2D/Dynamics/b2World.h | 2 +- jni/love/src/libraries/stb/stb_image.h | 268 +- jni/love/src/love.cpp | 13 +- jni/love/src/modules/audio/Source.h | 3 +- jni/love/src/modules/audio/null/Source.cpp | 5 + jni/love/src/modules/audio/null/Source.h | 1 + jni/love/src/modules/audio/openal/Pool.cpp | 6 + jni/love/src/modules/audio/openal/Pool.h | 1 + jni/love/src/modules/audio/openal/Source.cpp | 61 +- jni/love/src/modules/audio/openal/Source.h | 9 + jni/love/src/modules/audio/wrap_Audio.h | 23 - jni/love/src/modules/audio/wrap_Source.cpp | 18 +- jni/love/src/modules/audio/wrap_Source.h | 35 - jni/love/src/modules/event/sdl/Event.cpp | 6 - jni/love/src/modules/event/wrap_Event.cpp | 2 +- jni/love/src/modules/event/wrap_Event.h | 7 - .../modules/filesystem/physfs/Filesystem.cpp | 21 +- .../modules/filesystem/wrap_DroppedFile.cpp | 24 +- jni/love/src/modules/filesystem/wrap_File.cpp | 4 +- jni/love/src/modules/filesystem/wrap_File.h | 18 +- .../src/modules/filesystem/wrap_FileData.cpp | 7 +- .../src/modules/filesystem/wrap_FileData.h | 2 - .../modules/filesystem/wrap_Filesystem.cpp | 19 +- .../src/modules/filesystem/wrap_Filesystem.h | 39 +- .../font/freetype/TrueTypeRasterizer.cpp | 8 +- jni/love/src/modules/font/wrap_GlyphData.cpp | 9 +- jni/love/src/modules/font/wrap_GlyphData.h | 9 - jni/love/src/modules/font/wrap_Rasterizer.cpp | 4 +- jni/love/src/modules/font/wrap_Rasterizer.h | 8 - jni/love/src/modules/graphics/Color.h | 15 + jni/love/src/modules/graphics/Graphics.cpp | 2 +- jni/love/src/modules/graphics/Graphics.h | 13 +- .../src/modules/graphics/ParticleSystem.cpp | 986 +++ .../src/modules/graphics/ParticleSystem.h | 672 ++ jni/love/src/modules/graphics/Quad.cpp | 28 +- jni/love/src/modules/graphics/Quad.h | 12 +- jni/love/src/modules/graphics/Texture.cpp | 13 +- jni/love/src/modules/graphics/Texture.h | 1 + .../src/modules/graphics/opengl/Canvas.cpp | 19 +- jni/love/src/modules/graphics/opengl/Canvas.h | 15 +- jni/love/src/modules/graphics/opengl/Font.cpp | 563 +- jni/love/src/modules/graphics/opengl/Font.h | 53 +- .../src/modules/graphics/opengl/GLBuffer.cpp | 7 +- .../src/modules/graphics/opengl/Graphics.cpp | 100 +- .../src/modules/graphics/opengl/Graphics.h | 17 +- .../src/modules/graphics/opengl/Image.cpp | 223 +- jni/love/src/modules/graphics/opengl/Mesh.cpp | 122 +- jni/love/src/modules/graphics/opengl/Mesh.h | 11 +- .../src/modules/graphics/opengl/OpenGL.cpp | 71 +- jni/love/src/modules/graphics/opengl/OpenGL.h | 10 +- .../graphics/opengl/ParticleSystem.cpp | 961 +- .../modules/graphics/opengl/ParticleSystem.h | 638 +- .../src/modules/graphics/opengl/Shader.cpp | 60 + jni/love/src/modules/graphics/opengl/Shader.h | 8 + .../modules/graphics/opengl/SpriteBatch.cpp | 84 +- .../src/modules/graphics/opengl/SpriteBatch.h | 17 + jni/love/src/modules/graphics/opengl/Text.cpp | 81 +- jni/love/src/modules/graphics/opengl/Text.h | 18 +- .../src/modules/graphics/opengl/Video.cpp | 212 + jni/love/src/modules/graphics/opengl/Video.h | 79 + .../modules/graphics/opengl/wrap_Canvas.cpp | 13 +- .../src/modules/graphics/opengl/wrap_Canvas.h | 4 - .../src/modules/graphics/opengl/wrap_Font.cpp | 4 +- .../src/modules/graphics/opengl/wrap_Font.h | 12 - .../modules/graphics/opengl/wrap_Graphics.cpp | 87 +- .../modules/graphics/opengl/wrap_Graphics.h | 80 +- .../modules/graphics/opengl/wrap_Graphics.lua | 53 +- .../modules/graphics/opengl/wrap_Image.cpp | 13 +- .../src/modules/graphics/opengl/wrap_Image.h | 6 - .../src/modules/graphics/opengl/wrap_Mesh.cpp | 36 +- .../src/modules/graphics/opengl/wrap_Mesh.h | 19 - .../graphics/opengl/wrap_ParticleSystem.cpp | 4 +- .../graphics/opengl/wrap_ParticleSystem.h | 60 - .../modules/graphics/opengl/wrap_Shader.cpp | 118 +- .../src/modules/graphics/opengl/wrap_Shader.h | 8 - .../graphics/opengl/wrap_SpriteBatch.cpp | 15 +- .../graphics/opengl/wrap_SpriteBatch.h | 12 - .../src/modules/graphics/opengl/wrap_Text.cpp | 83 +- .../src/modules/graphics/opengl/wrap_Text.h | 10 +- .../modules/graphics/opengl/wrap_Video.cpp | 157 + .../src/modules/graphics/opengl/wrap_Video.h | 38 + .../modules/graphics/opengl/wrap_Video.lua | 69 + jni/love/src/modules/graphics/wrap_Quad.cpp | 16 +- jni/love/src/modules/graphics/wrap_Quad.h | 2 - .../src/modules/graphics/wrap_Texture.cpp | 17 + jni/love/src/modules/graphics/wrap_Texture.h | 10 +- .../src/modules/image/CompressedImageData.cpp | 22 +- .../src/modules/image/CompressedImageData.h | 22 +- .../src/modules/image/magpie/ASTCHandler.cpp | 166 + .../src/modules/image/magpie/ASTCHandler.h | 54 + jni/love/src/modules/image/magpie/Image.cpp | 20 +- .../src/modules/image/magpie/ImageData.cpp | 4 +- .../src/modules/image/magpie/KTXHandler.cpp | 89 +- .../src/modules/image/magpie/PVRHandler.cpp | 209 +- .../image/wrap_CompressedImageData.cpp | 9 +- .../modules/image/wrap_CompressedImageData.h | 5 - jni/love/src/modules/image/wrap_Image.h | 3 - jni/love/src/modules/image/wrap_ImageData.cpp | 25 +- jni/love/src/modules/image/wrap_ImageData.h | 8 - jni/love/src/modules/image/wrap_ImageData.lua | 379 +- jni/love/src/modules/joystick/sdl/Joystick.h | 14 +- .../src/modules/joystick/wrap_Joystick.cpp | 4 +- jni/love/src/modules/joystick/wrap_Joystick.h | 17 - .../modules/joystick/wrap_JoystickModule.h | 5 - jni/love/src/modules/keyboard/wrap_Keyboard.h | 9 - jni/love/src/modules/love/love.cpp | 10 + jni/love/src/modules/math/Compressor.cpp | 150 +- jni/love/src/modules/math/Compressor.h | 23 +- jni/love/src/modules/math/MathModule.cpp | 20 +- jni/love/src/modules/math/MathModule.h | 2 - jni/love/src/modules/math/RandomGenerator.cpp | 9 +- .../src/modules/math/wrap_BezierCurve.cpp | 4 +- jni/love/src/modules/math/wrap_BezierCurve.h | 14 - .../src/modules/math/wrap_CompressedData.cpp | 9 +- .../src/modules/math/wrap_CompressedData.h | 1 - jni/love/src/modules/math/wrap_Math.cpp | 112 +- jni/love/src/modules/math/wrap_Math.h | 15 - jni/love/src/modules/math/wrap_Math.lua | 100 + .../src/modules/math/wrap_RandomGenerator.cpp | 4 +- .../src/modules/math/wrap_RandomGenerator.h | 6 - jni/love/src/modules/mouse/wrap_Cursor.cpp | 4 +- jni/love/src/modules/mouse/wrap_Cursor.h | 1 - jni/love/src/modules/mouse/wrap_Mouse.h | 18 - jni/love/src/modules/physics/box2d/Body.cpp | 4 +- jni/love/src/modules/physics/box2d/Body.h | 10 +- jni/love/src/modules/physics/box2d/Joint.cpp | 4 +- jni/love/src/modules/physics/box2d/World.cpp | 10 +- .../src/modules/physics/box2d/wrap_Body.cpp | 4 +- .../src/modules/physics/box2d/wrap_Body.h | 58 - .../modules/physics/box2d/wrap_ChainShape.cpp | 12 +- .../modules/physics/box2d/wrap_ChainShape.h | 8 - .../physics/box2d/wrap_CircleShape.cpp | 12 +- .../modules/physics/box2d/wrap_CircleShape.h | 4 - .../modules/physics/box2d/wrap_Contact.cpp | 44 +- .../src/modules/physics/box2d/wrap_Contact.h | 16 - .../physics/box2d/wrap_DistanceJoint.cpp | 15 +- .../physics/box2d/wrap_DistanceJoint.h | 6 - .../modules/physics/box2d/wrap_EdgeShape.cpp | 12 +- .../modules/physics/box2d/wrap_EdgeShape.h | 1 - .../modules/physics/box2d/wrap_Fixture.cpp | 4 +- .../src/modules/physics/box2d/wrap_Fixture.h | 27 - .../physics/box2d/wrap_FrictionJoint.cpp | 15 +- .../physics/box2d/wrap_FrictionJoint.h | 4 - .../modules/physics/box2d/wrap_GearJoint.cpp | 15 +- .../modules/physics/box2d/wrap_GearJoint.h | 3 - .../src/modules/physics/box2d/wrap_Joint.cpp | 4 +- .../src/modules/physics/box2d/wrap_Joint.h | 12 +- .../modules/physics/box2d/wrap_MotorJoint.cpp | 15 +- .../modules/physics/box2d/wrap_MotorJoint.h | 10 - .../modules/physics/box2d/wrap_MouseJoint.cpp | 15 +- .../modules/physics/box2d/wrap_MouseJoint.h | 8 - .../src/modules/physics/box2d/wrap_Physics.h | 22 - .../physics/box2d/wrap_PolygonShape.cpp | 12 +- .../modules/physics/box2d/wrap_PolygonShape.h | 2 - .../physics/box2d/wrap_PrismaticJoint.cpp | 15 +- .../physics/box2d/wrap_PrismaticJoint.h | 17 - .../physics/box2d/wrap_PulleyJoint.cpp | 15 +- .../modules/physics/box2d/wrap_PulleyJoint.h | 4 - .../physics/box2d/wrap_RevoluteJoint.cpp | 15 +- .../physics/box2d/wrap_RevoluteJoint.h | 17 - .../modules/physics/box2d/wrap_RopeJoint.cpp | 15 +- .../modules/physics/box2d/wrap_RopeJoint.h | 1 - .../src/modules/physics/box2d/wrap_Shape.cpp | 4 +- .../src/modules/physics/box2d/wrap_Shape.h | 11 +- .../modules/physics/box2d/wrap_WeldJoint.cpp | 15 +- .../modules/physics/box2d/wrap_WeldJoint.h | 6 - .../modules/physics/box2d/wrap_WheelJoint.cpp | 15 +- .../modules/physics/box2d/wrap_WheelJoint.h | 13 - .../src/modules/physics/box2d/wrap_World.cpp | 4 +- .../src/modules/physics/box2d/wrap_World.h | 21 - jni/love/src/modules/sound/Decoder.h | 6 + .../sound/lullaby/CoreAudioDecoder.cpp | 20 + .../modules/sound/lullaby/CoreAudioDecoder.h | 3 + .../src/modules/sound/lullaby/FLACDecoder.cpp | 5 + .../src/modules/sound/lullaby/FLACDecoder.h | 1 + .../src/modules/sound/lullaby/GmeDecoder.cpp | 5 + .../src/modules/sound/lullaby/GmeDecoder.h | 1 + .../modules/sound/lullaby/ModPlugDecoder.cpp | 17 + .../modules/sound/lullaby/ModPlugDecoder.h | 4 + .../modules/sound/lullaby/Mpg123Decoder.cpp | 214 +- .../src/modules/sound/lullaby/Mpg123Decoder.h | 21 +- jni/love/src/modules/sound/lullaby/Sound.cpp | 2 +- .../modules/sound/lullaby/VorbisDecoder.cpp | 17 +- .../src/modules/sound/lullaby/VorbisDecoder.h | 2 + .../src/modules/sound/lullaby/WaveDecoder.cpp | 5 + .../src/modules/sound/lullaby/WaveDecoder.h | 3 +- jni/love/src/modules/sound/wrap_Decoder.cpp | 12 +- jni/love/src/modules/sound/wrap_Decoder.h | 3 - jni/love/src/modules/sound/wrap_Sound.cpp | 40 +- jni/love/src/modules/sound/wrap_Sound.h | 2 - jni/love/src/modules/sound/wrap_SoundData.cpp | 21 +- jni/love/src/modules/sound/wrap_SoundData.h | 7 - jni/love/src/modules/sound/wrap_SoundData.lua | 39 +- jni/love/src/modules/system/wrap_System.h | 7 - jni/love/src/modules/thread/wrap_Channel.cpp | 5 +- jni/love/src/modules/thread/wrap_Channel.h | 11 +- .../src/modules/thread/wrap_LuaThread.cpp | 5 +- jni/love/src/modules/thread/wrap_LuaThread.h | 5 - .../src/modules/thread/wrap_ThreadModule.cpp | 3 +- .../src/modules/thread/wrap_ThreadModule.h | 3 - jni/love/src/modules/timer/Timer.cpp | 11 +- jni/love/src/modules/timer/Timer.h | 4 +- jni/love/src/modules/timer/wrap_Timer.h | 6 - jni/love/src/modules/touch/wrap_Touch.h | 3 - jni/love/src/modules/video/Video.h | 53 + jni/love/src/modules/video/VideoStream.cpp | 168 + jni/love/src/modules/video/VideoStream.h | 131 + jni/love/src/modules/video/theora/Video.cpp | 122 + jni/love/src/modules/video/theora/Video.h | 81 + .../src/modules/video/theora/VideoStream.cpp | 402 + .../src/modules/video/theora/VideoStream.h | 101 + jni/love/src/modules/video/wrap_Video.cpp | 86 + jni/love/src/modules/video/wrap_Video.h | 38 + .../src/modules/video/wrap_VideoStream.cpp | 131 + jni/love/src/modules/video/wrap_VideoStream.h | 35 + jni/love/src/modules/window/Window.h | 3 + jni/love/src/modules/window/sdl/Window.cpp | 16 + jni/love/src/modules/window/sdl/Window.h | 3 + jni/love/src/modules/window/wrap_Window.cpp | 14 + jni/love/src/modules/window/wrap_Window.h | 26 - jni/love/src/scripts/boot.lua | 6 +- jni/love/src/scripts/boot.lua.h | 11 +- jni/love/src/scripts/nogame.lua | 1899 ++-- jni/love/src/scripts/nogame.lua.h | 7738 ++++++++--------- 248 files changed, 11591 insertions(+), 9219 deletions(-) create mode 100644 jni/love/src/common/Stream.h create mode 100644 jni/love/src/modules/graphics/ParticleSystem.cpp create mode 100644 jni/love/src/modules/graphics/ParticleSystem.h create mode 100644 jni/love/src/modules/graphics/opengl/Video.cpp create mode 100644 jni/love/src/modules/graphics/opengl/Video.h create mode 100644 jni/love/src/modules/graphics/opengl/wrap_Video.cpp create mode 100644 jni/love/src/modules/graphics/opengl/wrap_Video.h create mode 100644 jni/love/src/modules/graphics/opengl/wrap_Video.lua create mode 100644 jni/love/src/modules/image/magpie/ASTCHandler.cpp create mode 100644 jni/love/src/modules/image/magpie/ASTCHandler.h create mode 100644 jni/love/src/modules/math/wrap_Math.lua create mode 100644 jni/love/src/modules/video/Video.h create mode 100644 jni/love/src/modules/video/VideoStream.cpp create mode 100644 jni/love/src/modules/video/VideoStream.h create mode 100644 jni/love/src/modules/video/theora/Video.cpp create mode 100644 jni/love/src/modules/video/theora/Video.h create mode 100644 jni/love/src/modules/video/theora/VideoStream.cpp create mode 100644 jni/love/src/modules/video/theora/VideoStream.h create mode 100644 jni/love/src/modules/video/wrap_Video.cpp create mode 100644 jni/love/src/modules/video/wrap_Video.h create mode 100644 jni/love/src/modules/video/wrap_VideoStream.cpp create mode 100644 jni/love/src/modules/video/wrap_VideoStream.h diff --git a/jni/love/src/common/Module.h b/jni/love/src/common/Module.h index cac50a3c..bb77af76 100644 --- a/jni/love/src/common/Module.h +++ b/jni/love/src/common/Module.h @@ -53,6 +53,7 @@ public: M_TIMER, M_TOUCH, M_WINDOW, + M_VIDEO, M_MAX_ENUM }; diff --git a/jni/love/src/common/Object.h b/jni/love/src/common/Object.h index 624957a9..dd06ca81 100644 --- a/jni/love/src/common/Object.h +++ b/jni/love/src/common/Object.h @@ -117,6 +117,16 @@ public: return object; } + operator bool() const + { + return object != nullptr; + } + + operator T*() const + { + return object; + } + void set(T *obj) { if (obj) obj->retain(); diff --git a/jni/love/src/common/Stream.h b/jni/love/src/common/Stream.h new file mode 100644 index 00000000..4b9b1db2 --- /dev/null +++ b/jni/love/src/common/Stream.h @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_STREAM_H +#define LOVE_STREAM_H + +// LOVE +#include +#include "Object.h" + +namespace love +{ + +class Stream : public Object +{ +public: + virtual ~Stream() {} + + // getData and getSize are assumed to talk about + // the buffer + + /** + * A callback, gets called when some Stream consumer exhausts the data + **/ + virtual void fillBackBuffer() {} + + /** + * Get the front buffer, Streams are supposed to be (at least) double-buffered + **/ + virtual const void *getFrontBuffer() const = 0; + + /** + * Get the size of any (and in particular the front) buffer + **/ + virtual size_t getSize() const = 0; + + /** + * Swap buffers. Returns true if there is new data in the front buffer, + * false otherwise. + * NOTE: If there is no back buffer ready, this call must be ignored + **/ + virtual bool swapBuffers() = 0; +}; // Stream + +} // love + +#endif // LOVE_STREAM_H diff --git a/jni/love/src/common/Variant.cpp b/jni/love/src/common/Variant.cpp index 69d49ae7..3d48017b 100644 --- a/jni/love/src/common/Variant.cpp +++ b/jni/love/src/common/Variant.cpp @@ -24,8 +24,6 @@ namespace love { -extern StringMap types; - static love::Type extractudatatype(lua_State *L, int idx) { Type t = INVALID_ID; @@ -36,7 +34,7 @@ static love::Type extractudatatype(lua_State *L, int idx) lua_pushvalue(L, idx); int result = lua_pcall(L, 1, 1, 0); if (result == 0) - types.find(lua_tostring(L, -1), t); + getTypeName(lua_tostring(L, -1), t); if (result == 0 || result == LUA_ERRRUN) lua_pop(L, 1); return t; @@ -165,7 +163,7 @@ Variant *Variant::fromLua(lua_State *L, int n, bool allowTables) if (allowTables) { bool success = true; - std::vector > *table = new std::vector >(); + std::vector> *table = new std::vector>(); lua_pushnil(L); while (lua_next(L, n)) { @@ -230,7 +228,7 @@ void Variant::toLua(lua_State *L) // I can do (at the moment). break; case TABLE: - lua_newtable(L); + lua_createtable(L, 0, (int) data.table->size()); for (size_t i = 0; i < data.table->size(); ++i) { std::pair &kv = data.table->at(i); diff --git a/jni/love/src/common/Variant.h b/jni/love/src/common/Variant.h index 1a578686..7d5a7dc7 100644 --- a/jni/love/src/common/Variant.h +++ b/jni/love/src/common/Variant.h @@ -71,7 +71,7 @@ public: size_t len; } string; void *userdata; - std::vector > *table; + std::vector> *table; } data; private: diff --git a/jni/love/src/common/config.h b/jni/love/src/common/config.h index 4e13a1bc..232174d6 100644 --- a/jni/love/src/common/config.h +++ b/jni/love/src/common/config.h @@ -141,6 +141,8 @@ # define LOVE_ENABLE_TOUCH # define LOVE_ENABLE_TOUCH_SDL # define LOVE_ENABLE_UTF8 +# define LOVE_ENABLE_VIDEO +# define LOVE_ENABLE_VIDEO_THEORA # define LOVE_ENABLE_WINDOW # define LOVE_ENABLE_WINDOW_SDL # define LOVE_ENABLE_WUFF diff --git a/jni/love/src/common/runtime.cpp b/jni/love/src/common/runtime.cpp index 728f4c30..dca9bb2d 100644 --- a/jni/love/src/common/runtime.cpp +++ b/jni/love/src/common/runtime.cpp @@ -216,6 +216,8 @@ int luax_require(lua_State *L, const char *name) int luax_register_module(lua_State *L, const WrappedModule &m) { + love::addTypeName(m.type, m.name); + // Put a reference to the C++ module in Lua. luax_insistregistry(L, REGISTRY_MODULES); @@ -270,12 +272,9 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name) return 0; } -int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pushmetatable) +int luax_register_type(lua_State *L, love::Type type, const char *name, ...) { - // Verify that this type name has a matching Type ID and type name mapping. - const char *tname = "Invalid"; - if (!love::getType(type, tname)) - printf("Missing type name entry for type ID %d\n", type); + love::addTypeName(type, name); // Get the place for storing and re-using instantiated love types. luax_getregistry(L, REGISTRY_OBJECTS); @@ -302,7 +301,7 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu else lua_pop(L, 1); - luaL_newmetatable(L, tname); + luaL_newmetatable(L, name); // m.__index = m lua_pushvalue(L, -1); @@ -317,12 +316,12 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu lua_setfield(L, -2, "__eq"); // Add tostring function. - lua_pushstring(L, tname); + lua_pushstring(L, name); lua_pushcclosure(L, w__tostring, 1); lua_setfield(L, -2, "__tostring"); // Add type - lua_pushstring(L, tname); + lua_pushstring(L, name); lua_pushcclosure(L, w__type, 1); lua_setfield(L, -2, "type"); @@ -330,16 +329,25 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu lua_pushcfunction(L, w__typeOf); lua_setfield(L, -2, "typeOf"); - if (f != nullptr) + va_list fs; + va_start(fs, name); + for (const luaL_Reg *f = va_arg(fs, const luaL_Reg *); f; f = va_arg(fs, const luaL_Reg *)) luax_setfuncs(L, f); - - if (pushmetatable) - return 1; // leave the metatable on the stack. + va_end(fs); lua_pop(L, 1); // Pops metatable. return 0; } +void luax_gettypemetatable(lua_State *L, love::Type type) +{ + const char *name = nullptr; + if (getTypeName(type, name)) + lua_getfield(L, LUA_REGISTRYINDEX, name); + else + lua_pushnil(L); +} + int luax_table_insert(lua_State *L, int tindex, int vindex, int pos) { if (tindex < 0) @@ -403,7 +411,7 @@ void luax_rawnewtype(lua_State *L, love::Type type, love::Object *object) u->type = type; const char *name = "Invalid"; - getType(type, name); + getTypeName(type, name); luaL_newmetatable(L, name); lua_setmetatable(L, -2); @@ -670,7 +678,7 @@ extern "C" int luax_typerror(lua_State *L, int narg, const char *tname) // Non-love userdata might have a type metamethod which doesn't // describe its type properly, so we only use it for love types. love::Type t; - if (!love::getType(argtname, t)) + if (!love::getTypeName(argtname, t)) argtname = 0; } } @@ -707,7 +715,7 @@ void luax_register(lua_State *L, const char *name, const luaL_Reg *l) Type luax_type(lua_State *L, int idx) { Type t = INVALID_ID; - getType(luaL_checkstring(L, idx), t); + getTypeName(luaL_checkstring(L, idx), t); return t; } diff --git a/jni/love/src/common/runtime.h b/jni/love/src/common/runtime.h index 6d7f8cde..7d8b2653 100644 --- a/jni/love/src/common/runtime.h +++ b/jni/love/src/common/runtime.h @@ -247,10 +247,15 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name); /** * Register a new type. * @param type The type. - * @param f The list of member functions for the type. - * @param pushmetatable Whether to push the type's metatable to the stack. + * @param name The type's human-readable name + * @param ... The list of lists of member functions for the type. (of type luaL_Reg*) **/ -int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f = nullptr, bool pushmetatable = false); +int luax_register_type(lua_State *L, love::Type type, const char *name, ...); + +/** + * Pushes the metatable of the specified type onto the stack. +**/ +void luax_gettypemetatable(lua_State *L, love::Type type); /** * Do a table.insert from C @@ -434,7 +439,7 @@ T *luax_checktype(lua_State *L, int idx, love::Type type) if (lua_type(L, idx) != LUA_TUSERDATA) { const char *name = "Invalid"; - getType(type, name); + getTypeName(type, name); luax_typerror(L, idx, name); } @@ -443,7 +448,7 @@ T *luax_checktype(lua_State *L, int idx, love::Type type) if (!typeFlags[u->type][type]) { const char *name = "Invalid"; - getType(type, name); + getTypeName(type, name); luax_typerror(L, idx, name); } @@ -454,7 +459,7 @@ template T *luax_getmodule(lua_State *L, love::Type type) { const char *name = "Invalid"; - getType(type, name); + getTypeName(type, name); luax_insistregistry(L, REGISTRY_MODULES); lua_getfield(L, -1, name); @@ -476,7 +481,7 @@ template T *luax_optmodule(lua_State *L, love::Type type) { const char *name = "Invalid"; - getType(type, name); + getTypeName(type, name); luax_insistregistry(L, REGISTRY_MODULES); lua_getfield(L, -1, name); diff --git a/jni/love/src/common/types.cpp b/jni/love/src/common/types.cpp index 7fdf0f01..87f8cf94 100644 --- a/jni/love/src/common/types.cpp +++ b/jni/love/src/common/types.cpp @@ -34,6 +34,7 @@ static const TypeBits *createTypeFlags() b[OBJECT_ID] = one << OBJECT_ID; b[DATA_ID] = (one << DATA_ID) | b[OBJECT_ID]; b[MODULE_ID] = (one << MODULE_ID) | b[OBJECT_ID]; + b[STREAM_ID] = (one << STREAM_ID) | b[OBJECT_ID]; // Filesystem. b[FILESYSTEM_FILE_ID] = (one << FILESYSTEM_FILE_ID) | b[OBJECT_ID]; @@ -55,6 +56,7 @@ static const TypeBits *createTypeFlags() b[GRAPHICS_SHADER_ID] = (one << GRAPHICS_SHADER_ID) | b[OBJECT_ID]; b[GRAPHICS_MESH_ID] = (one << GRAPHICS_MESH_ID) | b[GRAPHICS_DRAWABLE_ID]; b[GRAPHICS_TEXT_ID] = (one << GRAPHICS_TEXT_ID) | b[GRAPHICS_DRAWABLE_ID]; + b[GRAPHICS_VIDEO_ID] = (one << GRAPHICS_VIDEO_ID) | b[GRAPHICS_DRAWABLE_ID]; // Image. b[IMAGE_IMAGE_DATA_ID] = (one << IMAGE_IMAGE_DATA_ID) | b[DATA_ID]; @@ -105,6 +107,9 @@ static const TypeBits *createTypeFlags() b[THREAD_THREAD_ID] = (one << THREAD_THREAD_ID) | b[OBJECT_ID]; b[THREAD_CHANNEL_ID] = (one << THREAD_CHANNEL_ID) | b[OBJECT_ID]; + // Video + b[VIDEO_VIDEO_STREAM_ID] = (one << VIDEO_VIDEO_STREAM_ID) | b[STREAM_ID]; + // Modules. b[MODULE_FILESYSTEM_ID] = (one << MODULE_FILESYSTEM_ID) | b[MODULE_ID]; b[MODULE_GRAPHICS_ID] = (one << MODULE_GRAPHICS_ID) | b[MODULE_ID]; @@ -116,102 +121,21 @@ static const TypeBits *createTypeFlags() const TypeBits *typeFlags = createTypeFlags(); -StringMap::Entry typeEntries[] = +static StringMap types(nullptr, 0); + +void addTypeName(Type type, const char *name) { - {"Invalid", INVALID_ID}, + const char *n; + if (!types.find(type, n)) + types.add(name, type); +} - {"Object", OBJECT_ID}, - {"Data", DATA_ID}, - {"Module", MODULE_ID}, - - // Filesystem - {"File", FILESYSTEM_FILE_ID}, - {"DroppedFile", FILESYSTEM_DROPPED_FILE_ID}, - {"FileData", FILESYSTEM_FILE_DATA_ID}, - - // Font - {"GlyphData", FONT_GLYPH_DATA_ID}, - {"Rasterizer", FONT_RASTERIZER_ID}, - - // Graphics - {"Drawable", GRAPHICS_DRAWABLE_ID}, - {"Texture", GRAPHICS_TEXTURE_ID}, - {"Image", GRAPHICS_IMAGE_ID}, - {"Quad", GRAPHICS_QUAD_ID}, - {"Font", GRAPHICS_FONT_ID}, - {"ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_ID}, - {"SpriteBatch", GRAPHICS_SPRITE_BATCH_ID}, - {"Canvas", GRAPHICS_CANVAS_ID}, - {"Shader", GRAPHICS_SHADER_ID}, - {"Mesh", GRAPHICS_MESH_ID}, - {"Text", GRAPHICS_TEXT_ID}, - - // Image - {"ImageData", IMAGE_IMAGE_DATA_ID}, - {"CompressedImageData", IMAGE_COMPRESSED_IMAGE_DATA_ID}, - - // Joystick - {"Joystick", JOYSTICK_JOYSTICK_ID}, - - // Math - {"RandomGenerator", MATH_RANDOM_GENERATOR_ID}, - {"BezierCurve", MATH_BEZIER_CURVE_ID}, - {"CompressedData", MATH_COMPRESSED_DATA_ID}, - - // Audio - {"Source", AUDIO_SOURCE_ID}, - - // Sound - {"SoundData", SOUND_SOUND_DATA_ID}, - {"Decoder", SOUND_DECODER_ID}, - - // Mouse - {"Cursor", MOUSE_CURSOR_ID}, - - // Physics - {"World", PHYSICS_WORLD_ID}, - {"Contact", PHYSICS_CONTACT_ID}, - {"Body", PHYSICS_BODY_ID}, - {"Fixture", PHYSICS_FIXTURE_ID}, - {"Shape", PHYSICS_SHAPE_ID}, - {"CircleShape", PHYSICS_CIRCLE_SHAPE_ID}, - {"PolygonShape", PHYSICS_POLYGON_SHAPE_ID}, - {"EdgeShape", PHYSICS_EDGE_SHAPE_ID}, - {"ChainShape", PHYSICS_CHAIN_SHAPE_ID}, - {"Joint", PHYSICS_JOINT_ID}, - {"MouseJoint", PHYSICS_MOUSE_JOINT_ID}, - {"DistanceJoint", PHYSICS_DISTANCE_JOINT_ID}, - {"PrismaticJoint", PHYSICS_PRISMATIC_JOINT_ID}, - {"RevoluteJoint", PHYSICS_REVOLUTE_JOINT_ID}, - {"PulleyJoint", PHYSICS_PULLEY_JOINT_ID}, - {"GearJoint", PHYSICS_GEAR_JOINT_ID}, - {"FrictionJoint", PHYSICS_FRICTION_JOINT_ID}, - {"WeldJoint", PHYSICS_WELD_JOINT_ID}, - {"RopeJoint", PHYSICS_ROPE_JOINT_ID}, - {"WheelJoint", PHYSICS_WHEEL_JOINT_ID}, - {"MotorJoint", PHYSICS_MOTOR_JOINT_ID}, - - // Thread - {"Thread", THREAD_THREAD_ID}, - {"Channel", THREAD_CHANNEL_ID}, - - // The modules themselves. Only add abstracted modules here. - {"filesystem", MODULE_FILESYSTEM_ID}, - {"graphics", MODULE_GRAPHICS_ID}, - {"image", MODULE_IMAGE_ID}, - {"sound", MODULE_SOUND_ID}, -}; - -StringMap types(typeEntries, sizeof(typeEntries)); - -static_assert((sizeof(typeEntries) / sizeof(typeEntries[0])) == TYPE_MAX_ENUM, "Type name array size doesn't match the total number of type IDs!"); - -bool getType(const char *in, love::Type &out) +bool getTypeName(const char *in, love::Type &out) { return types.find(in, out); } -bool getType(love::Type in, const char *&out) +bool getTypeName(love::Type in, const char *&out) { return types.find(in, out); } diff --git a/jni/love/src/common/types.h b/jni/love/src/common/types.h index 0e801bdd..dbfe3f64 100644 --- a/jni/love/src/common/types.h +++ b/jni/love/src/common/types.h @@ -34,6 +34,7 @@ enum Type OBJECT_ID, DATA_ID, MODULE_ID, + STREAM_ID, // Filesystem. FILESYSTEM_FILE_ID, @@ -56,6 +57,7 @@ enum Type GRAPHICS_SHADER_ID, GRAPHICS_MESH_ID, GRAPHICS_TEXT_ID, + GRAPHICS_VIDEO_ID, // Image IMAGE_IMAGE_DATA_ID, @@ -106,6 +108,9 @@ enum Type THREAD_THREAD_ID, THREAD_CHANNEL_ID, + // Video + VIDEO_VIDEO_STREAM_ID, + // The modules themselves. Only add abstracted modules here. MODULE_FILESYSTEM_ID, MODULE_GRAPHICS_ID, @@ -123,8 +128,9 @@ typedef std::bitset TypeBits; **/ extern const TypeBits *typeFlags; -bool getType(const char *in, Type &out); -bool getType(Type in, const char *&out); +void addTypeName(Type type, const char *name); +bool getTypeName(const char *in, Type &out); +bool getTypeName(Type in, const char *&out); } // love diff --git a/jni/love/src/common/version.h b/jni/love/src/common/version.h index 54bdc191..333d9733 100644 --- a/jni/love/src/common/version.h +++ b/jni/love/src/common/version.h @@ -31,7 +31,7 @@ static const int VERSION_MINOR = 10; static const int VERSION_REV = 0; static const char *VERSION = LOVE_VERSION_STRING; static const char *VERSION_COMPATIBILITY[] = { VERSION, 0 }; -static const char *VERSION_CODENAME = ""; +static const char *VERSION_CODENAME = "Super Toast"; } // love diff --git a/jni/love/src/common/wrap_Data.cpp b/jni/love/src/common/wrap_Data.cpp index 646d09b7..6a18eb59 100644 --- a/jni/love/src/common/wrap_Data.cpp +++ b/jni/love/src/common/wrap_Data.cpp @@ -59,7 +59,7 @@ const luaL_Reg w_Data_functions[] = int w_Data_open(lua_State *L) { - luax_register_type(L, DATA_ID, w_Data_functions); + luax_register_type(L, DATA_ID, "Data", w_Data_functions, nullptr); return 0; } diff --git a/jni/love/src/common/wrap_Data.h b/jni/love/src/common/wrap_Data.h index 50c944fe..46f61a62 100644 --- a/jni/love/src/common/wrap_Data.h +++ b/jni/love/src/common/wrap_Data.h @@ -29,10 +29,8 @@ namespace love { Data *luax_checkdata(lua_State *L, int idx); -int w_Data_getString(lua_State *L); -int w_Data_getPointer(lua_State *L); -int w_Data_getSize(lua_State *L); int w_Data_open(lua_State *L); +extern const luaL_Reg w_Data_functions[]; } // love diff --git a/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.cpp b/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.cpp index f1ed2cca..0c7282cc 100755 --- a/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.cpp +++ b/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.cpp @@ -22,6 +22,11 @@ #include b2ChainShape::~b2ChainShape() +{ + Clear(); +} + +void b2ChainShape::Clear() { b2Free(m_vertices); m_vertices = NULL; @@ -56,10 +61,8 @@ void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count) b2Assert(count >= 2); for (int32 i = 1; i < count; ++i) { - b2Vec2 v1 = vertices[i-1]; - b2Vec2 v2 = vertices[i]; // If the code crashes here, it means your vertices are too close together. - b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop); + b2Assert(b2DistanceSquared(vertices[i-1], vertices[i]) > b2_linearSlop * b2_linearSlop); } m_count = count; diff --git a/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.h b/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.h index e836dfef..44493c43 100755 --- a/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.h +++ b/jni/love/src/libraries/Box2D/Collision/Shapes/b2ChainShape.h @@ -37,6 +37,9 @@ public: /// The destructor frees the vertices using b2Free. ~b2ChainShape(); + /// Clear all data. + void Clear(); + /// Create a loop. This automatically adjusts connectivity. /// @param vertices an array of vertices, these are copied /// @param count the vertex count diff --git a/jni/love/src/libraries/Box2D/Collision/Shapes/b2PolygonShape.cpp b/jni/love/src/libraries/Box2D/Collision/Shapes/b2PolygonShape.cpp index 2f32e9fe..26ba7b3a 100755 --- a/jni/love/src/libraries/Box2D/Collision/Shapes/b2PolygonShape.cpp +++ b/jni/love/src/libraries/Box2D/Collision/Shapes/b2PolygonShape.cpp @@ -138,7 +138,7 @@ void b2PolygonShape::Set(const b2Vec2* vertices, int32 count) bool unique = true; for (int32 j = 0; j < tempCount; ++j) { - if (b2DistanceSquared(v, ps[j]) < 0.5f * b2_linearSlop) + if (b2DistanceSquared(v, ps[j]) < ((0.5f * b2_linearSlop) * (0.5f * b2_linearSlop))) { unique = false; break; @@ -217,6 +217,14 @@ void b2PolygonShape::Set(const b2Vec2* vertices, int32 count) } } + if (m < 3) + { + // Polygon is degenerate. + b2Assert(false); + SetAsBox(1.0f, 1.0f); + return; + } + m_count = m; // Copy vertices. diff --git a/jni/love/src/libraries/Box2D/Common/b2Draw.h b/jni/love/src/libraries/Box2D/Common/b2Draw.h index 20359c91..be65fb0f 100755 --- a/jni/love/src/libraries/Box2D/Common/b2Draw.h +++ b/jni/love/src/libraries/Box2D/Common/b2Draw.h @@ -25,9 +25,9 @@ struct b2Color { b2Color() {} - b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {} - void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; } - float32 r, g, b; + b2Color(float32 r, float32 g, float32 b, float32 a = 1.0f) : r(r), g(g), b(b), a(a) {} + void Set(float32 ri, float32 gi, float32 bi, float32 ai = 1.0f) { r = ri; g = gi; b = bi; a = ai; } + float32 r, g, b, a; }; /// Implement and register this class with a b2World to provide debug drawing of physics diff --git a/jni/love/src/libraries/Box2D/Common/b2Math.h b/jni/love/src/libraries/Box2D/Common/b2Math.h index 713100df..755c519b 100755 --- a/jni/love/src/libraries/Box2D/Common/b2Math.h +++ b/jni/love/src/libraries/Box2D/Common/b2Math.h @@ -21,7 +21,6 @@ #include #include -#include /// This function is used to ensure that a floating point number is not a NaN or infinity. inline bool b2IsValid(float32 x) diff --git a/jni/love/src/libraries/Box2D/Common/b2Settings.cpp b/jni/love/src/libraries/Box2D/Common/b2Settings.cpp index e2ceefc1..ed6d83e0 100755 --- a/jni/love/src/libraries/Box2D/Common/b2Settings.cpp +++ b/jni/love/src/libraries/Box2D/Common/b2Settings.cpp @@ -23,7 +23,7 @@ #include "common/Exception.h" -b2Version b2_version = {2, 3, 0}; +b2Version b2_version = {2, 3, 2}; // Memory allocators. Modify these to use your own allocator. void* b2Alloc(int32 size) diff --git a/jni/love/src/libraries/Box2D/Dynamics/Contacts/b2ContactSolver.cpp b/jni/love/src/libraries/Box2D/Dynamics/Contacts/b2ContactSolver.cpp index 8224fdc2..e31b6c23 100755 --- a/jni/love/src/libraries/Box2D/Dynamics/Contacts/b2ContactSolver.cpp +++ b/jni/love/src/libraries/Box2D/Dynamics/Contacts/b2ContactSolver.cpp @@ -26,6 +26,8 @@ #define B2_DEBUG_SOLVER 0 +bool g_blockSolve = true; + struct b2ContactPositionConstraint { b2Vec2 localPoints[b2_maxManifoldPoints]; @@ -213,7 +215,7 @@ void b2ContactSolver::InitializeVelocityConstraints() } // If we have two points, then prepare the block solver. - if (vc->pointCount == 2) + if (vc->pointCount == 2 && g_blockSolve) { b2VelocityConstraintPoint* vcp1 = vc->points + 0; b2VelocityConstraintPoint* vcp2 = vc->points + 1; @@ -341,29 +343,32 @@ void b2ContactSolver::SolveVelocityConstraints() } // Solve normal constraints - if (vc->pointCount == 1) + if (pointCount == 1 || g_blockSolve == false) { - b2VelocityConstraintPoint* vcp = vc->points + 0; + for (int32 i = 0; i < pointCount; ++i) + { + b2VelocityConstraintPoint* vcp = vc->points + i; - // Relative velocity at contact - b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA); + // Relative velocity at contact + b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA); - // Compute normal impulse - float32 vn = b2Dot(dv, normal); - float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias); + // Compute normal impulse + float32 vn = b2Dot(dv, normal); + float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias); - // b2Clamp the accumulated impulse - float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f); - lambda = newImpulse - vcp->normalImpulse; - vcp->normalImpulse = newImpulse; + // b2Clamp the accumulated impulse + float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f); + lambda = newImpulse - vcp->normalImpulse; + vcp->normalImpulse = newImpulse; - // Apply contact impulse - b2Vec2 P = lambda * normal; - vA -= mA * P; - wA -= iA * b2Cross(vcp->rA, P); + // Apply contact impulse + b2Vec2 P = lambda * normal; + vA -= mA * P; + wA -= iA * b2Cross(vcp->rA, P); - vB += mB * P; - wB += iB * b2Cross(vcp->rB, P); + vB += mB * P; + wB += iB * b2Cross(vcp->rB, P); + } } else { diff --git a/jni/love/src/libraries/Box2D/Dynamics/Joints/b2PrismaticJoint.cpp b/jni/love/src/libraries/Box2D/Dynamics/Joints/b2PrismaticJoint.cpp index 26f53968..217e1450 100755 --- a/jni/love/src/libraries/Box2D/Dynamics/Joints/b2PrismaticJoint.cpp +++ b/jni/love/src/libraries/Box2D/Dynamics/Joints/b2PrismaticJoint.cpp @@ -174,6 +174,9 @@ void b2PrismaticJoint::InitVelocityConstraints(const b2SolverData& data) m_s1 = b2Cross(d + rA, m_perp); m_s2 = b2Cross(rB, m_perp); + float32 s1test; + s1test = b2Cross(rA, m_perp); + float32 k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2; float32 k12 = iA * m_s1 + iB * m_s2; float32 k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2; diff --git a/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WeldJoint.cpp b/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WeldJoint.cpp index 253f7216..ffdd386b 100755 --- a/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WeldJoint.cpp +++ b/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WeldJoint.cpp @@ -129,6 +129,12 @@ void b2WeldJoint::InitVelocityConstraints(const b2SolverData& data) invM += m_gamma; m_mass.ez.z = invM != 0.0f ? 1.0f / invM : 0.0f; } + else if (K.ez.z == 0.0f) + { + K.GetInverse22(&m_mass); + m_gamma = 0.0f; + m_bias = 0.0f; + } else { K.GetSymInverse33(&m_mass); @@ -271,7 +277,17 @@ bool b2WeldJoint::SolvePositionConstraints(const b2SolverData& data) b2Vec3 C(C1.x, C1.y, C2); - b2Vec3 impulse = -K.Solve33(C); + b2Vec3 impulse; + if (K.ez.z > 0.0f) + { + impulse = -K.Solve33(C); + } + else + { + b2Vec2 impulse2 = -K.Solve22(C1); + impulse.Set(impulse2.x, impulse2.y, 0.0f); + } + b2Vec2 P(impulse.x, impulse.y); cA -= mA * P; diff --git a/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WheelJoint.h b/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WheelJoint.h index ca59d7a6..a33c77f4 100755 --- a/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WheelJoint.h +++ b/jni/love/src/libraries/Box2D/Dynamics/Joints/b2WheelJoint.h @@ -72,9 +72,8 @@ struct b2WheelJointDef : public b2JointDef }; /// A wheel joint. This joint provides two degrees of freedom: translation -/// along an axis fixed in bodyA and rotation in the plane. You can use a -/// joint limit to restrict the range of motion and a joint motor to drive -/// the rotation or to model rotational friction. +/// along an axis fixed in bodyA and rotation in the plane. In other words, it is a point to +/// line constraint with a rotational motor and a linear spring/damper. /// This joint is designed for vehicle suspensions. class b2WheelJoint : public b2Joint { diff --git a/jni/love/src/libraries/Box2D/Dynamics/b2World.cpp b/jni/love/src/libraries/Box2D/Dynamics/b2World.cpp index a3a6a1e4..c6cf2957 100755 --- a/jni/love/src/libraries/Box2D/Dynamics/b2World.cpp +++ b/jni/love/src/libraries/Box2D/Dynamics/b2World.cpp @@ -37,7 +37,7 @@ b2World::b2World(const b2Vec2& gravity) { m_destructionListener = NULL; - m_debugDraw = NULL; + g_debugDraw = NULL; m_bodyList = NULL; m_jointList = NULL; @@ -101,7 +101,7 @@ void b2World::SetContactListener(b2ContactListener* listener) void b2World::SetDebugDraw(b2Draw* debugDraw) { - m_debugDraw = debugDraw; + g_debugDraw = debugDraw; } b2Body* b2World::CreateBody(const b2BodyDef* def) @@ -1040,7 +1040,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color float32 radius = circle->m_radius; b2Vec2 axis = b2Mul(xf.q, b2Vec2(1.0f, 0.0f)); - m_debugDraw->DrawSolidCircle(center, radius, axis, color); + g_debugDraw->DrawSolidCircle(center, radius, axis, color); } break; @@ -1049,7 +1049,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color b2EdgeShape* edge = (b2EdgeShape*)fixture->GetShape(); b2Vec2 v1 = b2Mul(xf, edge->m_vertex1); b2Vec2 v2 = b2Mul(xf, edge->m_vertex2); - m_debugDraw->DrawSegment(v1, v2, color); + g_debugDraw->DrawSegment(v1, v2, color); } break; @@ -1063,8 +1063,8 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color for (int32 i = 1; i < count; ++i) { b2Vec2 v2 = b2Mul(xf, vertices[i]); - m_debugDraw->DrawSegment(v1, v2, color); - m_debugDraw->DrawCircle(v1, 0.05f, color); + g_debugDraw->DrawSegment(v1, v2, color); + g_debugDraw->DrawCircle(v1, 0.05f, color); v1 = v2; } } @@ -1082,7 +1082,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color vertices[i] = b2Mul(xf, poly->m_vertices[i]); } - m_debugDraw->DrawSolidPolygon(vertices, vertexCount, color); + g_debugDraw->DrawSolidPolygon(vertices, vertexCount, color); } break; @@ -1107,7 +1107,7 @@ void b2World::DrawJoint(b2Joint* joint) switch (joint->GetType()) { case e_distanceJoint: - m_debugDraw->DrawSegment(p1, p2, color); + g_debugDraw->DrawSegment(p1, p2, color); break; case e_pulleyJoint: @@ -1115,9 +1115,9 @@ void b2World::DrawJoint(b2Joint* joint) b2PulleyJoint* pulley = (b2PulleyJoint*)joint; b2Vec2 s1 = pulley->GetGroundAnchorA(); b2Vec2 s2 = pulley->GetGroundAnchorB(); - m_debugDraw->DrawSegment(s1, p1, color); - m_debugDraw->DrawSegment(s2, p2, color); - m_debugDraw->DrawSegment(s1, s2, color); + g_debugDraw->DrawSegment(s1, p1, color); + g_debugDraw->DrawSegment(s2, p2, color); + g_debugDraw->DrawSegment(s1, s2, color); } break; @@ -1126,20 +1126,20 @@ void b2World::DrawJoint(b2Joint* joint) break; default: - m_debugDraw->DrawSegment(x1, p1, color); - m_debugDraw->DrawSegment(p1, p2, color); - m_debugDraw->DrawSegment(x2, p2, color); + g_debugDraw->DrawSegment(x1, p1, color); + g_debugDraw->DrawSegment(p1, p2, color); + g_debugDraw->DrawSegment(x2, p2, color); } } void b2World::DrawDebugData() { - if (m_debugDraw == NULL) + if (g_debugDraw == NULL) { return; } - uint32 flags = m_debugDraw->GetFlags(); + uint32 flags = g_debugDraw->GetFlags(); if (flags & b2Draw::e_shapeBit) { @@ -1191,7 +1191,7 @@ void b2World::DrawDebugData() //b2Vec2 cA = fixtureA->GetAABB().GetCenter(); //b2Vec2 cB = fixtureB->GetAABB().GetCenter(); - //m_debugDraw->DrawSegment(cA, cB, color); + //g_debugDraw->DrawSegment(cA, cB, color); } } @@ -1219,7 +1219,7 @@ void b2World::DrawDebugData() vs[2].Set(aabb.upperBound.x, aabb.upperBound.y); vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y); - m_debugDraw->DrawPolygon(vs, 4, color); + g_debugDraw->DrawPolygon(vs, 4, color); } } } @@ -1231,7 +1231,7 @@ void b2World::DrawDebugData() { b2Transform xf = b->GetTransform(); xf.p = b->GetWorldCenter(); - m_debugDraw->DrawTransform(xf); + g_debugDraw->DrawTransform(xf); } } } diff --git a/jni/love/src/libraries/Box2D/Dynamics/b2World.h b/jni/love/src/libraries/Box2D/Dynamics/b2World.h index 4f94f6ef..3968cc78 100755 --- a/jni/love/src/libraries/Box2D/Dynamics/b2World.h +++ b/jni/love/src/libraries/Box2D/Dynamics/b2World.h @@ -247,7 +247,7 @@ private: bool m_allowSleep; b2DestructionListener* m_destructionListener; - b2Draw* m_debugDraw; + b2Draw* g_debugDraw; // This is used to compute the time step ratio to // support a variable time step. diff --git a/jni/love/src/libraries/stb/stb_image.h b/jni/love/src/libraries/stb/stb_image.h index 79f076d7..a46521db 100644 --- a/jni/love/src/libraries/stb/stb_image.h +++ b/jni/love/src/libraries/stb/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.05 - public domain image loader - http://nothings.org/stb_image.h +/* stb_image - v2.07 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: @@ -25,13 +25,16 @@ TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels) + PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) @@ -143,6 +146,10 @@ Latest revision history: + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) additional corruption checking @@ -175,38 +182,41 @@ Tom Seddon (pic) the Horde3D community Thatcher Ulrich (psd) Janez Zemva Ken Miller (pgm, ppm) Jonathan Blow - Laurent Gomila + urraka@github (animated gif) Laurent Gomila Aruelien Pocheville - Extensions, features Ryamond Barbiero - Jetro Lauha (stbi_info) David Woo - Martin "SpartanJ" Golini (stbi_info) Martin Golini - James "moose2000" Brown (iPhone PNG) Roy Eltham - Ben "Disch" Wenger (io callbacks) Luke Graham - Omar Cornut (1/2/4-bit PNG) Thomas Ruf - Nicolas Guillemot (vertical flip) John Bartholomew - Ken Hamada - Optimizations & bugfixes Cort Stratton - Fabian "ryg" Giesen Blazej Dariusz Roszkowski - Arseny Kapoulkine Thibault Reuille + Ryamond Barbiero + David Woo + Extensions, features Martin Golini + Jetro Lauha (stbi_info) Roy Eltham + Martin "SpartanJ" Golini (stbi_info) Luke Graham + James "moose2000" Brown (iPhone PNG) Thomas Ruf + Ben "Disch" Wenger (io callbacks) John Bartholomew + Omar Cornut (1/2/4-bit PNG) Ken Hamada + Nicolas Guillemot (vertical flip) Cort Stratton + Richard Mitton (16-bit PSD) Blazej Dariusz Roszkowski + Thibault Reuille Paul Du Bois Guillaume George - If your name should be here but Jerry Jansson - isn't, let Sean know. Hayaki Saito + Jerry Jansson + Hayaki Saito Johan Duparc Ronny Chevalier - Michal Cichon - Tero Hanninen - Sergio Gonzalez + Optimizations & bugfixes Michal Cichon + Fabian "ryg" Giesen Tero Hanninen + Arseny Kapoulkine Sergio Gonzalez Cass Everitt Engin Manap - Martins Mozeiko - Joseph Thomson + If your name should be here but Martins Mozeiko + isn't, let Sean know. Joseph Thomson Phil Jordan + Nathan Reed + Michaelangel007@github -License: - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy - and modify this file however you want. +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. */ @@ -748,7 +758,7 @@ typedef struct stbi_uc buffer_start[128]; stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original; + stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; @@ -760,7 +770,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) s->io.read = NULL; s->read_from_callbacks = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = (stbi_uc *) buffer+len; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } // initialize a callback-based context @@ -772,6 +782,7 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->read_from_callbacks = 1; s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO @@ -813,6 +824,7 @@ static void stbi__rewind(stbi__context *s) // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; } #ifndef STBI_NO_JPEG @@ -900,8 +912,8 @@ static void *stbi__malloc(size_t size) #define stbi__err(x,y) stbi__err(x) #endif -#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { @@ -988,6 +1000,7 @@ static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *com return result; } +#ifndef STBI_NO_HDR static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1008,7 +1021,7 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in } } } - +#endif #ifndef STBI_NO_STDIO @@ -1152,6 +1165,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f) stbi__start_file(&s,f); return stbi__hdr_test(&s); #else + STBI_NOTUSED(f); return 0; #endif } @@ -1164,6 +1178,8 @@ STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); return 0; #endif } @@ -1278,23 +1294,29 @@ static int stbi__get16be(stbi__context *s) return (z << 8) + stbi__get8(s); } +#if !defined(STBI_NO_PNG) || !defined(STBI_NO_PSD) static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif +#if !defined(STBI_NO_BMP) || !defined(STBI_NO_TGA) || !defined(STBI_NO_GIF) static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } +#endif +#ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } +#endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings @@ -2738,7 +2760,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (z->img_comp[i].raw_data == NULL) { for(--i; i >= 0; --i) { STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; + z->img_comp[i].raw_data = NULL; } return stbi__err("outofmem", "Out of memory"); } @@ -3500,10 +3522,10 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { - int k = stbi__bit_reverse(next_code[s],s); - while (k < (1 << STBI__ZFAST_BITS)) { - z->fast[k] = fastv; - k += (1 << s); + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); } } ++next_code[s]; @@ -3542,7 +3564,7 @@ static void stbi__fill_bits(stbi__zbuf *z) { do { STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= stbi__zget8(z) << z->num_bits; + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } @@ -4108,21 +4130,21 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { + int q; // insert alpha = 255 - stbi_uc *cur = a->out + stride*j; - int i; + cur = a->out + stride*j; if (img_n == 1) { - for (i=x-1; i >= 0; --i) { - cur[i*2+1] = 255; - cur[i*2+0] = cur[i]; + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); - for (i=x-1; i >= 0; --i) { - cur[i*4+3] = 255; - cur[i*4+2] = cur[i*3+2]; - cur[i*4+1] = cur[i*3+1]; - cur[i*4+0] = cur[i*3+0]; + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; } } } @@ -4577,7 +4599,7 @@ static int stbi__shiftsigned(int v, int shift, int bits) static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a=255; stbi_uc pal[256][4]; int psize=0,i,j,compress=0,width; int bpp, flip_vertically, pad, target, offset, hsz; @@ -4626,8 +4648,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int mg = 0xffu << 8; mb = 0xffu << 0; ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - STBI_NOTUSED(fake_a); + all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { mr = 31u << 10; mg = 31u << 5; @@ -4738,6 +4759,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; if (target == 4) out[z++] = a; } } else { @@ -4748,12 +4770,19 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { @@ -4907,8 +4936,8 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int if ( !tga_indexed && !tga_is_RLE) { for (i=0; i < tga_height; ++i) { - int y = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { @@ -5053,6 +5082,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int int pixelCount; int channelCount, compression; int channel, i, count, len; + int bitdepth; int w,h; stbi_uc *out; @@ -5077,8 +5107,9 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int w = stbi__get32be(s); // Make sure the depth is 8 bits. - if (stbi__get16be(s) != 8) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); // Make sure the color mode is RGB. // Valid options are: @@ -5139,7 +5170,8 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int p = out+channel; if (channel >= channelCount) { // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. count = 0; @@ -5185,11 +5217,17 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int p = out + channel; if (channel > channelCount) { // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + for (i = 0; i < pixelCount; i++, p += 4) + *p = channel == 3 ? 255 : 0; } else { // Read the data. - for (i = 0; i < pixelCount; i++) - *p = stbi__get8(s), p += 4; + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get16be(s) >> 8; + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } } } } @@ -5199,7 +5237,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int if (out == NULL) return out; // stbi__convert_format frees input on failure } - if (comp) *comp = channelCount; + if (comp) *comp = 4; *y = h; *x = w; @@ -5347,7 +5385,6 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c if (count >= 128) { // Repeated stbi_uc value[4]; - int i; if (count==128) count = stbi__get16be(s); @@ -5435,8 +5472,8 @@ typedef struct typedef struct { int w,h; - stbi_uc *out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; stbi__gif_lzw codes[4096]; @@ -5554,7 +5591,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; - stbi__int32 len, code; + stbi__int32 len, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; @@ -5567,10 +5604,10 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (stbi_uc) code; - g->codes[code].suffix = (stbi_uc) code; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; } // support no starting clear code @@ -5631,17 +5668,18 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) } } -static void stbi__fill_gif_background(stbi__gif *g) +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) { - int i; + int x, y; stbi_uc *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - stbi_uc *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } } } @@ -5649,27 +5687,40 @@ static void stbi__fill_gif_background(stbi__gif *g) static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) { int i; - stbi_uc *old_out = 0; + stbi_uc *prev_out = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - stbi__fill_gif_background(g); - } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); - } + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; } for (;;) { switch (stbi__get8(s)) { case 0x2C: /* Image Descriptor */ { + int prev_trans = -1; stbi__int32 x, y, w, h; stbi_uc *o; @@ -5702,10 +5753,10 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; g->pal[g->transparent][3] = 0; + } g->color_table = (stbi_uc *) g->pal; } else return stbi__errpuc("missing color table", "Corrupt GIF"); @@ -5713,8 +5764,9 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; - if (req_comp && req_comp != 4) - o = stbi__convert_format(o, 4, req_comp, g->w, g->h); + if (prev_trans != -1) + g->pal[g->transparent][3] = prev_trans; + return o; } @@ -5725,7 +5777,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); - stbi__get16le(s); // delay + g->delay = stbi__get16le(s); g->transparent = stbi__get8(s); } else { stbi__skip(s, len); @@ -5757,7 +5809,11 @@ static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int if (u) { *x = g.w; *y = g.h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } + else if (g.out) + STBI_FREE(g.out); return u; } @@ -6059,14 +6115,22 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) int act_comp=0,num_packets=0,chained; stbi__pic_packet packets[10]; - stbi__skip(s, 92); + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); - if (stbi__at_eof(s)) return 0; + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } stbi__skip(s, 8); @@ -6292,6 +6356,12 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int /* revision history: + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bit PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) extra corruption checking (mmozeiko) diff --git a/jni/love/src/love.cpp b/jni/love/src/love.cpp index 632f2d69..4d08b8d0 100644 --- a/jni/love/src/love.cpp +++ b/jni/love/src/love.cpp @@ -37,6 +37,7 @@ extern "C" { #ifdef LOVE_MACOSX #include "common/macosx.h" +#include #endif // LOVE_MACOSX #ifdef LOVE_IOS @@ -110,12 +111,16 @@ static void get_app_arguments(int argc, char **argv, int &new_argc, char **&new_ } #ifdef LOVE_MACOSX - // Check for a drop file string. - std::string dropfilestr = love::macosx::checkDropEvents(); + // Check for a drop file string, if the app wasn't launched in a terminal. + // Checking for the terminal is a pretty big hack, but works around an issue + // where OS X will switch Spaces if the terminal launching love is in its + // own full-screen Space. + std::string dropfilestr; + if (!isatty(STDIN_FILENO)) + dropfilestr = love::macosx::checkDropEvents(); + if (!dropfilestr.empty()) - { temp_argv.insert(temp_argv.begin() + 1, dropfilestr); - } else #endif { diff --git a/jni/love/src/modules/audio/Source.h b/jni/love/src/modules/audio/Source.h index 4c0fef64..01a06c5f 100644 --- a/jni/love/src/modules/audio/Source.h +++ b/jni/love/src/modules/audio/Source.h @@ -39,7 +39,7 @@ public: TYPE_STATIC, TYPE_STREAM, TYPE_MAX_ENUM - }; // Type + }; enum Unit { @@ -71,6 +71,7 @@ public: virtual void seek(float offset, Unit unit) = 0; virtual float tell(Unit unit) = 0; + virtual double getDuration(Unit unit) = 0; // all float * v must be of size 3 virtual void setPosition(float *v) = 0; diff --git a/jni/love/src/modules/audio/null/Source.cpp b/jni/love/src/modules/audio/null/Source.cpp index aa682a62..8b70bf8b 100644 --- a/jni/love/src/modules/audio/null/Source.cpp +++ b/jni/love/src/modules/audio/null/Source.cpp @@ -112,6 +112,11 @@ float Source::tell(Source::Unit) return 0.0f; } +double Source::getDuration(Unit) +{ + return -1.0f; +} + void Source::setPosition(float *) { } diff --git a/jni/love/src/modules/audio/null/Source.h b/jni/love/src/modules/audio/null/Source.h index bbab20a4..5b126581 100644 --- a/jni/love/src/modules/audio/null/Source.h +++ b/jni/love/src/modules/audio/null/Source.h @@ -54,6 +54,7 @@ public: virtual float getVolume() const; virtual void seek(float offset, Unit unit); virtual float tell(Unit unit); + virtual double getDuration(Unit unit); virtual void setPosition(float *v); virtual void getPosition(float *v) const; virtual void setVelocity(float *v); diff --git a/jni/love/src/modules/audio/openal/Pool.cpp b/jni/love/src/modules/audio/openal/Pool.cpp index a1018f55..0f9e9a5a 100644 --- a/jni/love/src/modules/audio/openal/Pool.cpp +++ b/jni/love/src/modules/audio/openal/Pool.cpp @@ -263,6 +263,12 @@ float Pool::tell(Source *source, void *unit) return source->tellAtomic(unit); } +double Pool::getDuration(Source *source, void *unit) +{ + thread::Lock lock(mutex); + return source->getDurationAtomic(unit); +} + ALuint Pool::findi(const Source *source) const { std::map::const_iterator i = playing.find((Source *)source); diff --git a/jni/love/src/modules/audio/openal/Pool.h b/jni/love/src/modules/audio/openal/Pool.h index e875b4f5..d054315c 100644 --- a/jni/love/src/modules/audio/openal/Pool.h +++ b/jni/love/src/modules/audio/openal/Pool.h @@ -93,6 +93,7 @@ public: void softRewind(Source *source); void seek(Source *source, float offset, void *unit); float tell(Source *source, void *unit); + double getDuration(Source *source, void *unit); private: diff --git a/jni/love/src/modules/audio/openal/Source.cpp b/jni/love/src/modules/audio/openal/Source.cpp index 7132ad8f..04f42bd8 100644 --- a/jni/love/src/modules/audio/openal/Source.cpp +++ b/jni/love/src/modules/audio/openal/Source.cpp @@ -65,6 +65,7 @@ Ensure the Source is not multi-channel before calling this function.") }; StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) + : size(size) { alGenBuffers(1, &buffer); alBufferData(buffer, format, data, size, freq); @@ -95,6 +96,7 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData) , offsetSeconds(0) , sampleRate(soundData->getSampleRate()) , channels(soundData->getChannels()) + , bitDepth(soundData->getBitDepth()) , decoder(nullptr) , toLoop(0) { @@ -134,6 +136,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder) , offsetSeconds(0) , sampleRate(decoder->getSampleRate()) , channels(decoder->getChannels()) + , bitDepth(decoder->getBitDepth()) , decoder(decoder) , toLoop(0) { @@ -169,6 +172,7 @@ Source::Source(const Source &s) , offsetSeconds(0) , sampleRate(s.sampleRate) , channels(s.channels) + , bitDepth(s.bitDepth) , decoder(nullptr) , toLoop(0) { @@ -308,8 +312,11 @@ bool Source::update() offsetSamples += (curOffsetSamples - newOffsetSamples); offsetSeconds += (curOffsetSecs - newOffsetSecs); - streamAtomic(buffer, decoder.get()); - alSourceQueueBuffers(source, 1, &buffer); + // FIXME: We should put freed buffers into a list that we later + // consume here, so we can keep track of all free buffers even if we + // tried to stream data to one but the decoder didn't have data for it. + if (streamAtomic(buffer, decoder.get()) > 0) + alSourceQueueBuffers(source, 1, &buffer); } return true; @@ -438,6 +445,36 @@ float Source::tell(Source::Unit unit) return pool->tell(this, &unit); } +double Source::getDurationAtomic(void *vunit) +{ + Unit unit = *(Unit *) vunit; + + if (type == TYPE_STREAM) + { + double seconds = decoder->getDuration(); + + if (unit == UNIT_SECONDS) + return seconds; + else + return seconds * decoder->getSampleRate(); + } + else + { + ALsizei size = staticBuffer->getSize(); + ALsizei samples = (size / channels) / (bitDepth / 8); + + if (unit == UNIT_SAMPLES) + return (double) samples; + else + return (double) samples / (double) sampleRate; + } +} + +double Source::getDuration(Unit unit) +{ + return pool->getDuration(this, &unit); +} + void Source::setPosition(float *v) { if (channels > 1) @@ -575,8 +612,11 @@ bool Source::playAtomic() for (unsigned int i = 0; i < MAX_BUFFERS; i++) { - streamAtomic(streamBuffers[i], decoder.get()); + if (streamAtomic(streamBuffers[i], decoder.get()) == 0) + break; + ++usedBuffers; + if (decoder->isFinished()) break; } @@ -736,13 +776,18 @@ ALenum Source::getFormat(int channels, int bitDepth) const int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d) { // Get more sound data. - int decoded = d->decode(); - decoded = decoded >= 0 ? decoded : 0; + int decoded = std::max(d->decode(), 0); - int fmt = getFormat(d->getChannels(), d->getBitDepth()); + // OpenAL implementations are allowed to ignore 0-size alBufferData calls. + if (decoded > 0) + { + int fmt = getFormat(d->getChannels(), d->getBitDepth()); - if (fmt != 0) - alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate()); + if (fmt != 0) + alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate()); + else + decoded = 0; + } if (decoder->isFinished() && isLooping()) { diff --git a/jni/love/src/modules/audio/openal/Source.h b/jni/love/src/modules/audio/openal/Source.h index 28937243..e94b28e1 100644 --- a/jni/love/src/modules/audio/openal/Source.h +++ b/jni/love/src/modules/audio/openal/Source.h @@ -65,9 +65,15 @@ public: return buffer; } + inline ALsizei getSize() const + { + return size; + } + private: ALuint buffer; + ALsizei size; }; // StaticDataBuffer @@ -98,6 +104,8 @@ public: virtual void seek(float offset, Unit unit); virtual float tellAtomic(void *unit) const; virtual float tell(Unit unit); + virtual double getDurationAtomic(void *unit); + virtual double getDuration(Unit unit); virtual void setPosition(float *v); virtual void getPosition(float *v) const; virtual void setVelocity(float *v); @@ -180,6 +188,7 @@ private: int sampleRate; int channels; + int bitDepth; StrongRef decoder; diff --git a/jni/love/src/modules/audio/wrap_Audio.h b/jni/love/src/modules/audio/wrap_Audio.h index 40dc807d..5673d9b7 100644 --- a/jni/love/src/modules/audio/wrap_Audio.h +++ b/jni/love/src/modules/audio/wrap_Audio.h @@ -32,29 +32,6 @@ namespace love namespace audio { -int w_getSourceCount(lua_State *L); -int w_newSource(lua_State *L); -int w_play(lua_State *L); -int w_stop(lua_State *L); -int w_pause(lua_State *L); -int w_resume(lua_State *L); -int w_rewind(lua_State *L); -int w_setVolume(lua_State *L); -int w_getVolume(lua_State *L); -int w_setPosition(lua_State *L); -int w_getPosition(lua_State *L); -int w_setOrientation(lua_State *L); -int w_getOrientation(lua_State *L); -int w_setVelocity(lua_State *L); -int w_getVelocity(lua_State *L); -int w_setDopplerScale(lua_State *L); -int w_getDopplerScale(lua_State *L); -int w_record(lua_State *L); -int w_getRecordedData(lua_State *L); -int w_stopRecording(lua_State *L); -int w_canRecord(lua_State *L); -int w_setDistanceModel(lua_State *L); -int w_getDistanceModel(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_audio(lua_State *L); } // audio diff --git a/jni/love/src/modules/audio/wrap_Source.cpp b/jni/love/src/modules/audio/wrap_Source.cpp index c4fe1700..ceb86863 100644 --- a/jni/love/src/modules/audio/wrap_Source.cpp +++ b/jni/love/src/modules/audio/wrap_Source.cpp @@ -140,6 +140,19 @@ int w_Source_tell(lua_State *L) return 1; } +int w_Source_getDuration(lua_State *L) +{ + Source *t = luax_checksource(L, 1); + + Source::Unit u = Source::UNIT_SECONDS; + const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2); + if (unit && !t->getConstant(unit, u)) + return luaL_error(L, "Invalid Source time unit: %s", unit); + + lua_pushnumber(L, t->getDuration(u)); + return 1; +} + int w_Source_setPosition(lua_State *L) { Source *t = luax_checksource(L, 1); @@ -357,7 +370,7 @@ int w_Source_getType(lua_State *L) return 1; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Source_functions[] = { { "clone", w_Source_clone }, @@ -373,6 +386,7 @@ static const luaL_Reg functions[] = { "getVolume", w_Source_getVolume }, { "seek", w_Source_seek }, { "tell", w_Source_tell }, + { "getDuration", w_Source_getDuration }, { "setPosition", w_Source_setPosition }, { "getPosition", w_Source_getPosition }, { "setVelocity", w_Source_setVelocity }, @@ -406,7 +420,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_source(lua_State *L) { - return luax_register_type(L, AUDIO_SOURCE_ID, functions); + return luax_register_type(L, AUDIO_SOURCE_ID, "Source", w_Source_functions, nullptr); } } // audio diff --git a/jni/love/src/modules/audio/wrap_Source.h b/jni/love/src/modules/audio/wrap_Source.h index 93dc52e2..c7b8514d 100644 --- a/jni/love/src/modules/audio/wrap_Source.h +++ b/jni/love/src/modules/audio/wrap_Source.h @@ -30,41 +30,6 @@ namespace audio { Source *luax_checksource(lua_State *L, int idx); -int w_Source_clone(lua_State *L); -int w_Source_play(lua_State *L); -int w_Source_stop(lua_State *L); -int w_Source_pause(lua_State *L); -int w_Source_resume(lua_State *L); -int w_Source_rewind(lua_State *L); -int w_Source_setPitch(lua_State *L); -int w_Source_getPitch(lua_State *L); -int w_Source_setVolume(lua_State *L); -int w_Source_getVolume(lua_State *L); -int w_Source_seek(lua_State *L); -int w_Source_tell(lua_State *L); -int w_Source_setPosition(lua_State *L); -int w_Source_getPosition(lua_State *L); -int w_Source_setVelocity(lua_State *L); -int w_Source_getVelocity(lua_State *L); -int w_Source_setDirection(lua_State *L); -int w_Source_getDirection(lua_State *L); -int w_Source_setCone(lua_State *L); -int w_Source_getCone(lua_State *L); -int w_Source_setRelative(lua_State *L); -int w_Source_isRelative(lua_State *L); -int w_Source_setLooping(lua_State *L); -int w_Source_isLooping(lua_State *L); -int w_Source_isStopped(lua_State *L); -int w_Source_isPaused(lua_State *L); -int w_Source_isPlaying(lua_State *L); -int w_Source_setVolumeLimits(lua_State *L); -int w_Source_getVolumeLimits(lua_State *L); -int w_Source_setAttenuationDistances(lua_State *L); -int w_Source_getAttenuationDistances(lua_State *L); -int w_Source_setRolloff(lua_State *L); -int w_Source_getRolloff(lua_State *L); -int w_Source_getChannels(lua_State *L); -int w_Source_getType(lua_State *L); extern "C" int luaopen_source(lua_State *L); } // audio diff --git a/jni/love/src/modules/event/sdl/Event.cpp b/jni/love/src/modules/event/sdl/Event.cpp index c7decc61..815bb51b 100644 --- a/jni/love/src/modules/event/sdl/Event.cpp +++ b/jni/love/src/modules/event/sdl/Event.cpp @@ -556,12 +556,6 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const { case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_LOST: - // Users won't expect the screensaver to activate if a game is in - // focus. Also, joystick input may not delay the screensaver timer. - if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) - SDL_DisableScreenSaver(); - else - SDL_EnableScreenSaver(); vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)); msg = new Message("focus", vargs); break; diff --git a/jni/love/src/modules/event/wrap_Event.cpp b/jni/love/src/modules/event/wrap_Event.cpp index 74741707..8f9af72c 100644 --- a/jni/love/src/modules/event/wrap_Event.cpp +++ b/jni/love/src/modules/event/wrap_Event.cpp @@ -141,7 +141,7 @@ extern "C" int luaopen_love_event(lua_State *L) w.name = "event"; w.type = MODULE_ID; w.functions = functions; - w.types = 0; + w.types = nullptr; return luax_register_module(L, w); } diff --git a/jni/love/src/modules/event/wrap_Event.h b/jni/love/src/modules/event/wrap_Event.h index 05a25f7c..ed0f8f7e 100644 --- a/jni/love/src/modules/event/wrap_Event.h +++ b/jni/love/src/modules/event/wrap_Event.h @@ -30,13 +30,6 @@ namespace love namespace event { -int w_pump(lua_State *L); -int w_poll(lua_State *L); -int w_wait(lua_State *L); -int w_push(lua_State *L); -int w_clear(lua_State *L); -int w_quit(lua_State *L); - extern "C" LOVE_EXPORT int luaopen_love_event(lua_State *L); } // event diff --git a/jni/love/src/modules/filesystem/physfs/Filesystem.cpp b/jni/love/src/modules/filesystem/physfs/Filesystem.cpp index fcf73417..bf5f09e8 100644 --- a/jni/love/src/modules/filesystem/physfs/Filesystem.cpp +++ b/jni/love/src/modules/filesystem/physfs/Filesystem.cpp @@ -125,12 +125,8 @@ void Filesystem::init(const char *arg0) if (!PHYSFS_init(arg0)) throw love::Exception("%s", PHYSFS_getLastError()); - PHYSFS_Version version = {}; - PHYSFS_getLinkedVersion(&version); - - // FIXME: This is a workaround for a bug in PHYSFS_enumerateFiles in 2.1-alpha. - if (version.major == 2 && version.minor == 1) - PHYSFS_permitSymbolicLinks(1); + // Enable symlinks by default. Also fixes an issue in PhysFS 2.1-alpha. + setSymlinksEnabled(true); } void Filesystem::setFused(bool fused) @@ -223,7 +219,7 @@ bool Filesystem::setSource(const char *source) std::string new_search_path = source; #ifdef LOVE_ANDROID - if (!love::android::createStorageDirectories ()) + if (!love::android::createStorageDirectories()) SDL_Log("Error creating storage directories!"); char* game_archive_ptr = NULL; @@ -664,6 +660,17 @@ int64 Filesystem::getSize(const char *filename) const void Filesystem::setSymlinksEnabled(bool enable) { + if (!enable) + { + PHYSFS_Version version = {}; + PHYSFS_getLinkedVersion(&version); + + // FIXME: This is a workaround for a bug in PHYSFS_enumerateFiles in + // PhysFS 2.1-alpha. + if (version.major == 2 && version.minor == 1) + return; + } + PHYSFS_permitSymbolicLinks(enable ? 1 : 0); } diff --git a/jni/love/src/modules/filesystem/wrap_DroppedFile.cpp b/jni/love/src/modules/filesystem/wrap_DroppedFile.cpp index b3898255..dfd82c80 100644 --- a/jni/love/src/modules/filesystem/wrap_DroppedFile.cpp +++ b/jni/love/src/modules/filesystem/wrap_DroppedFile.cpp @@ -31,31 +31,9 @@ DroppedFile *luax_checkdroppedfile(lua_State *L, int idx) return luax_checktype(L, idx, FILESYSTEM_DROPPED_FILE_ID); } -static const luaL_Reg functions[] = -{ - // Inherits from File. - { "getSize", w_File_getSize }, - { "open", w_File_open }, - { "close", w_File_close }, - { "isOpen", w_File_isOpen }, - { "read", w_File_read }, - { "write", w_File_write }, - { "flush", w_File_flush }, - { "isEOF", w_File_isEOF }, - { "tell", w_File_tell }, - { "seek", w_File_seek }, - { "lines", w_File_lines }, - { "setBuffer", w_File_setBuffer }, - { "getBuffer", w_File_getBuffer }, - { "getMode", w_File_getMode }, - { "getFilename", w_File_getFilename }, - { "getExtension", w_File_getExtension }, - { 0, 0 } -}; - extern "C" int luaopen_droppedfile(lua_State *L) { - return luax_register_type(L, FILESYSTEM_DROPPED_FILE_ID, functions); + return luax_register_type(L, FILESYSTEM_DROPPED_FILE_ID, "DroppedFile", w_File_functions, nullptr); } } // filesystem diff --git a/jni/love/src/modules/filesystem/wrap_File.cpp b/jni/love/src/modules/filesystem/wrap_File.cpp index f8b0fe0f..234fad73 100644 --- a/jni/love/src/modules/filesystem/wrap_File.cpp +++ b/jni/love/src/modules/filesystem/wrap_File.cpp @@ -413,7 +413,7 @@ int w_File_getExtension(lua_State *L) return 1; } -static const luaL_Reg functions[] = +const luaL_Reg w_File_functions[] = { { "getSize", w_File_getSize }, { "open", w_File_open }, @@ -436,7 +436,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_file(lua_State *L) { - return luax_register_type(L, FILESYSTEM_FILE_ID, functions); + return luax_register_type(L, FILESYSTEM_FILE_ID, "File", w_File_functions, nullptr); } } // filesystem diff --git a/jni/love/src/modules/filesystem/wrap_File.h b/jni/love/src/modules/filesystem/wrap_File.h index 50ee0f13..65bd21d3 100644 --- a/jni/love/src/modules/filesystem/wrap_File.h +++ b/jni/love/src/modules/filesystem/wrap_File.h @@ -34,25 +34,11 @@ namespace filesystem int luax_ioError(lua_State *L, const char *fmt, ...); File *luax_checkfile(lua_State *L, int idx); -int w_File_getSize(lua_State *L); -int w_File_open(lua_State *L); -int w_File_close(lua_State *L); -int w_File_isOpen(lua_State *L); -int w_File_read(lua_State *L); -int w_File_write(lua_State *L); -int w_File_flush(lua_State *L); -int w_File_isEOF(lua_State *L); -int w_File_tell(lua_State *L); -int w_File_seek(lua_State *L); int w_File_lines_i(lua_State *L); -int w_File_lines(lua_State *L); -int w_File_setBuffer(lua_State *L); -int w_File_getBuffer(lua_State *L); -int w_File_getMode(lua_State *L); -int w_File_getFilename(lua_State *L); -int w_File_getExtension(lua_State *L); extern "C" int luaopen_file(lua_State *L); +extern const luaL_Reg w_File_functions[]; + } // filesystem } // love diff --git a/jni/love/src/modules/filesystem/wrap_FileData.cpp b/jni/love/src/modules/filesystem/wrap_FileData.cpp index 83a2af9e..3e508668 100644 --- a/jni/love/src/modules/filesystem/wrap_FileData.cpp +++ b/jni/love/src/modules/filesystem/wrap_FileData.cpp @@ -48,11 +48,6 @@ int w_FileData_getExtension(lua_State *L) static const luaL_Reg w_FileData_functions[] = { - // Data - { "getString", w_Data_getString }, - { "getPointer", w_Data_getPointer }, - { "getSize", w_Data_getSize }, - { "getFilename", w_FileData_getFilename }, { "getExtension", w_FileData_getExtension }, @@ -61,7 +56,7 @@ static const luaL_Reg w_FileData_functions[] = extern "C" int luaopen_filedata(lua_State *L) { - return luax_register_type(L, FILESYSTEM_FILE_DATA_ID, w_FileData_functions); + return luax_register_type(L, FILESYSTEM_FILE_DATA_ID, "FileData", w_Data_functions, w_FileData_functions, nullptr); } } // filesystem diff --git a/jni/love/src/modules/filesystem/wrap_FileData.h b/jni/love/src/modules/filesystem/wrap_FileData.h index 703e4ce4..bbd38232 100644 --- a/jni/love/src/modules/filesystem/wrap_FileData.h +++ b/jni/love/src/modules/filesystem/wrap_FileData.h @@ -31,8 +31,6 @@ namespace filesystem { FileData *luax_checkfiledata(lua_State *L, int idx); -int w_FileData_getFilename(lua_State *L); -int w_FileData_getExtension(lua_State *L); extern "C" int luaopen_filedata(lua_State *L); } // filesystem diff --git a/jni/love/src/modules/filesystem/wrap_Filesystem.cpp b/jni/love/src/modules/filesystem/wrap_Filesystem.cpp index 8e8941d5..9b22b60c 100644 --- a/jni/love/src/modules/filesystem/wrap_Filesystem.cpp +++ b/jni/love/src/modules/filesystem/wrap_Filesystem.cpp @@ -155,19 +155,28 @@ int w_newFile(lua_State *L) return 1; } -FileData *luax_getfiledata(lua_State *L, int idx) +File *luax_getfile(lua_State *L, int idx) { - FileData *data = nullptr; File *file = nullptr; - if (lua_isstring(L, idx)) { const char *filename = luaL_checkstring(L, idx); file = instance()->newFile(filename); } - else if (luax_istype(L, idx, FILESYSTEM_FILE_ID)) - { + else file = luax_checkfile(L, idx); + + return file; +} + +FileData *luax_getfiledata(lua_State *L, int idx) +{ + FileData *data = nullptr; + File *file = nullptr; + + if (lua_isstring(L, idx) || luax_istype(L, idx, FILESYSTEM_FILE_ID)) + { + file = luax_getfile(L, idx); file->retain(); } else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID)) diff --git a/jni/love/src/modules/filesystem/wrap_Filesystem.h b/jni/love/src/modules/filesystem/wrap_Filesystem.h index 38ffa0b6..3af3c7b3 100644 --- a/jni/love/src/modules/filesystem/wrap_Filesystem.h +++ b/jni/love/src/modules/filesystem/wrap_Filesystem.h @@ -23,6 +23,7 @@ // LOVE #include "common/runtime.h" +#include "File.h" #include "FileData.h" namespace love @@ -38,45 +39,9 @@ namespace filesystem * May trigger a Lua error. **/ FileData *luax_getfiledata(lua_State *L, int idx); +File *luax_getfile(lua_State *L, int idx); bool hack_setupWriteDirectory(); -int w_init(lua_State *L); -int w_setFused(lua_State *L); -int w_isFused(lua_State *L); -int w_setIdentity(lua_State *L); -int w_getIdentity(lua_State *L); -int w_setSource(lua_State *L); -int w_getSource(lua_State *L); -int w_mount(lua_State *L); -int w_unmount(lua_State *L); -int w_newFile(lua_State *L); -int w_newFileData(lua_State *L); -int w_getWorkingDirectory(lua_State *L); -int w_getUserDirectory(lua_State *L); -int w_getAppdataDirectory(lua_State *L); -int w_getSaveDirectory(lua_State *L); -int w_getSourceBaseDirectory(lua_State *L); -int w_getRealDirectory(lua_State *L); -int w_getExecutablePath(lua_State *L); -int w_isDirectory(lua_State *L); -int w_isFile(lua_State *L); -int w_createDirectory(lua_State *L); -int w_remove(lua_State *L); -int w_open(lua_State *L); -int w_close(lua_State *L); -int w_read(lua_State *L); -int w_write(lua_State *L); -int w_append(lua_State *L); -int w_getDirectoryItems(lua_State *L); -int w_lines(lua_State *L); -int w_load(lua_State *L); -int w_getLastModified(lua_State *L); -int w_getSize(lua_State *L); -int w_setSymlinksEnabled(lua_State *L); -int w_areSymlinksEnabled(lua_State *L); -int w_isSymlink(lua_State *L); -int w_getRequirePath(lua_State *L); -int w_setRequirePath(lua_State *L); int loader(lua_State *L); int extloader(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_filesystem(lua_State *L); diff --git a/jni/love/src/modules/font/freetype/TrueTypeRasterizer.cpp b/jni/love/src/modules/font/freetype/TrueTypeRasterizer.cpp index 29c77fdd..8d1f4bb5 100644 --- a/jni/love/src/modules/font/freetype/TrueTypeRasterizer.cpp +++ b/jni/love/src/modules/font/freetype/TrueTypeRasterizer.cpp @@ -116,9 +116,9 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const // We treat the luminance of the FreeType bitmap as alpha in the GlyphData. if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { - for (int y = 0; y < bitmap.rows; y++) + for (int y = 0; y < (int) bitmap.rows; y++) { - for (int x = 0; x < bitmap.width; x++) + for (int x = 0; x < (int) bitmap.width; x++) { // Extract the 1-bit value and convert it to uint8. uint8 v = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0; @@ -131,9 +131,9 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const } else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { - for (int y = 0; y < bitmap.rows; y++) + for (int y = 0; y < (int) bitmap.rows; y++) { - for (int x = 0; x < bitmap.width; x++) + for (int x = 0; x < (int) bitmap.width; x++) { dest[2 * (y * bitmap.width + x) + 0] = 255; dest[2 * (y * bitmap.width + x) + 1] = pixels[x]; diff --git a/jni/love/src/modules/font/wrap_GlyphData.cpp b/jni/love/src/modules/font/wrap_GlyphData.cpp index 494d9e5e..9cd369f3 100644 --- a/jni/love/src/modules/font/wrap_GlyphData.cpp +++ b/jni/love/src/modules/font/wrap_GlyphData.cpp @@ -115,13 +115,8 @@ int w_GlyphData_getFormat(lua_State *L) return 1; } -static const luaL_Reg functions[] = +const luaL_Reg w_GlyphData_functions[] = { - // Data - { "getString", w_Data_getString }, - { "getPointer", w_Data_getPointer }, - { "getSize", w_Data_getSize }, - { "getWidth", w_GlyphData_getWidth }, { "getHeight", w_GlyphData_getHeight }, { "getDimensions", w_GlyphData_getDimensions }, @@ -136,7 +131,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_glyphdata(lua_State *L) { - return luax_register_type(L, FONT_GLYPH_DATA_ID, functions); + return luax_register_type(L, FONT_GLYPH_DATA_ID, "GlyphData", w_Data_functions, w_GlyphData_functions, nullptr); } } // font diff --git a/jni/love/src/modules/font/wrap_GlyphData.h b/jni/love/src/modules/font/wrap_GlyphData.h index 3d485dc3..ce3b0c5e 100644 --- a/jni/love/src/modules/font/wrap_GlyphData.h +++ b/jni/love/src/modules/font/wrap_GlyphData.h @@ -33,15 +33,6 @@ namespace font { GlyphData *luax_checkglyphdata(lua_State *L, int idx); -int w_GlyphData_getWidth(lua_State *L); -int w_GlyphData_getHeight(lua_State *L); -int w_GlyphData_getDimensions(lua_State *L); -int w_GlyphData_getGlyph(lua_State *L); -int w_GlyphData_getGlyphString(lua_State *L); -int w_GlyphData_getAdvance(lua_State *L); -int w_GlyphData_getBearing(lua_State *L); -int w_GlyphData_getBoundingBox(lua_State *L); -int w_GlyphData_getFormat(lua_State *L); extern "C" int luaopen_glyphdata(lua_State *L); } // font diff --git a/jni/love/src/modules/font/wrap_Rasterizer.cpp b/jni/love/src/modules/font/wrap_Rasterizer.cpp index 59a93495..7fa04d88 100644 --- a/jni/love/src/modules/font/wrap_Rasterizer.cpp +++ b/jni/love/src/modules/font/wrap_Rasterizer.cpp @@ -124,7 +124,7 @@ int w_Rasterizer_hasGlyphs(lua_State *L) return 1; } -static const luaL_Reg functions[] = +const luaL_Reg w_Rasterizer_functions[] = { { "getHeight", w_Rasterizer_getHeight }, { "getAdvance", w_Rasterizer_getAdvance }, @@ -139,7 +139,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_rasterizer(lua_State *L) { - return luax_register_type(L, FONT_RASTERIZER_ID, functions); + return luax_register_type(L, FONT_RASTERIZER_ID, "Rasterizer", w_Rasterizer_functions, nullptr); } } // font diff --git a/jni/love/src/modules/font/wrap_Rasterizer.h b/jni/love/src/modules/font/wrap_Rasterizer.h index b8d6eae1..9c132d18 100644 --- a/jni/love/src/modules/font/wrap_Rasterizer.h +++ b/jni/love/src/modules/font/wrap_Rasterizer.h @@ -31,14 +31,6 @@ namespace font { Rasterizer *luax_checkrasterizer(lua_State *L, int idx); -int w_Rasterizer_getHeight(lua_State *L); -int w_Rasterizer_getAdvance(lua_State *L); -int w_Rasterizer_getAscent(lua_State *L); -int w_Rasterizer_getDescent(lua_State *L); -int w_Rasterizer_getLineHeight(lua_State *L); -int w_Rasterizer_getGlyphData(lua_State *L); -int w_Rasterizer_getGlyphCount(lua_State *L); -int w_Rasterizer_hasGlyphs(lua_State *L); extern "C" int luaopen_rasterizer(lua_State *L); } // font diff --git a/jni/love/src/modules/graphics/Color.h b/jni/love/src/modules/graphics/Color.h index 1019e0cd..2c652213 100644 --- a/jni/love/src/modules/graphics/Color.h +++ b/jni/love/src/modules/graphics/Color.h @@ -44,11 +44,26 @@ struct ColorT a = a_; } + bool operator==(const ColorT &other) const; + bool operator!=(const ColorT &other) const; + ColorT operator+=(const ColorT &other); ColorT operator*=(T s); ColorT operator/=(T s); }; +template +bool ColorT::operator==(const ColorT &other) const +{ + return r == other.r && g == other.g && b == other.b && a == other.a; +} + +template +bool ColorT::operator!=(const ColorT &other) const +{ + return !(operator==(other)); +} + template ColorT ColorT::operator+=(const ColorT &other) { diff --git a/jni/love/src/modules/graphics/Graphics.cpp b/jni/love/src/modules/graphics/Graphics.cpp index 245f8260..8dbfd21d 100644 --- a/jni/love/src/modules/graphics/Graphics.cpp +++ b/jni/love/src/modules/graphics/Graphics.cpp @@ -181,8 +181,8 @@ StringMap Graphics::lineJoins( StringMap::Entry Graphics::supportEntries[] = { - { "multicanvas", SUPPORT_MULTI_CANVAS }, { "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS }, + { "clampzero", SUPPORT_CLAMP_ZERO }, }; StringMap Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); diff --git a/jni/love/src/modules/graphics/Graphics.h b/jni/love/src/modules/graphics/Graphics.h index 0e587732..ede4eba0 100644 --- a/jni/love/src/modules/graphics/Graphics.h +++ b/jni/love/src/modules/graphics/Graphics.h @@ -98,8 +98,8 @@ public: enum Support { - SUPPORT_MULTI_CANVAS, SUPPORT_MULTI_CANVAS_FORMATS, + SUPPORT_CLAMP_ZERO, SUPPORT_MAX_ENUM }; @@ -178,6 +178,17 @@ public: } }; + struct ScissorRect + { + int x, y; + int w, h; + + bool operator == (const ScissorRect &rhs) const + { + return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; + } + }; + virtual ~Graphics(); // Implements Module. diff --git a/jni/love/src/modules/graphics/ParticleSystem.cpp b/jni/love/src/modules/graphics/ParticleSystem.cpp new file mode 100644 index 00000000..f14fba8b --- /dev/null +++ b/jni/love/src/modules/graphics/ParticleSystem.cpp @@ -0,0 +1,986 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +//LOVE +#include "common/config.h" +#include "ParticleSystem.h" + +#include "common/math.h" +#include "modules/math/RandomGenerator.h" + +// STD +#include +#include +#include + +namespace love +{ +namespace graphics +{ + +namespace +{ + +love::math::RandomGenerator rng; + +float calculate_variation(float inner, float outer, float var) +{ + float low = inner - (outer/2.0f)*var; + float high = inner + (outer/2.0f)*var; + float r = (float) rng.random(); + return low*(1-r)+high*r; +} + +} // anonymous namespace + +ParticleSystem::ParticleSystem(Texture *texture, uint32 size) + : pMem(nullptr) + , pFree(nullptr) + , pHead(nullptr) + , pTail(nullptr) + , texture(texture) + , active(true) + , insertMode(INSERT_MODE_TOP) + , maxParticles(0) + , activeParticles(0) + , emissionRate(0) + , emitCounter(0) + , areaSpreadDistribution(DISTRIBUTION_NONE) + , lifetime(-1) + , life(0) + , particleLifeMin(0) + , particleLifeMax(0) + , direction(0) + , spread(0) + , speedMin(0) + , speedMax(0) + , linearAccelerationMin(0, 0) + , linearAccelerationMax(0, 0) + , radialAccelerationMin(0) + , radialAccelerationMax(0) + , tangentialAccelerationMin(0) + , tangentialAccelerationMax(0) + , linearDampingMin(0.0f) + , linearDampingMax(0.0f) + , sizeVariation(0) + , rotationMin(0) + , rotationMax(0) + , spinStart(0) + , spinEnd(0) + , spinVariation(0) + , offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f) + , defaultOffset(true) + , relativeRotation(false) +{ + if (size == 0 || size > MAX_PARTICLES) + throw love::Exception("Invalid ParticleSystem size."); + + sizes.push_back(1.0f); + colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); + setBufferSize(size); +} + +ParticleSystem::ParticleSystem(const ParticleSystem &p) + : pMem(nullptr) + , pFree(nullptr) + , pHead(nullptr) + , pTail(nullptr) + , texture(p.texture) + , active(p.active) + , insertMode(p.insertMode) + , maxParticles(p.maxParticles) + , activeParticles(0) + , emissionRate(p.emissionRate) + , emitCounter(0.0f) + , position(p.position) + , prevPosition(p.prevPosition) + , areaSpreadDistribution(p.areaSpreadDistribution) + , areaSpread(p.areaSpread) + , lifetime(p.lifetime) + , life(p.lifetime) // Initialize with the maximum life time. + , particleLifeMin(p.particleLifeMin) + , particleLifeMax(p.particleLifeMax) + , direction(p.direction) + , spread(p.spread) + , speedMin(p.speedMin) + , speedMax(p.speedMax) + , linearAccelerationMin(p.linearAccelerationMin) + , linearAccelerationMax(p.linearAccelerationMax) + , radialAccelerationMin(p.radialAccelerationMin) + , radialAccelerationMax(p.radialAccelerationMax) + , tangentialAccelerationMin(p.tangentialAccelerationMin) + , tangentialAccelerationMax(p.tangentialAccelerationMax) + , linearDampingMin(p.linearDampingMin) + , linearDampingMax(p.linearDampingMax) + , sizes(p.sizes) + , sizeVariation(p.sizeVariation) + , rotationMin(p.rotationMin) + , rotationMax(p.rotationMax) + , spinStart(p.spinStart) + , spinEnd(p.spinEnd) + , spinVariation(p.spinVariation) + , offset(p.offset) + , defaultOffset(p.defaultOffset) + , colors(p.colors) + , quads(p.quads) + , relativeRotation(p.relativeRotation) +{ + setBufferSize(maxParticles); +} + +ParticleSystem::~ParticleSystem() +{ + deleteBuffers(); +} + +void ParticleSystem::resetOffset() +{ + if (quads.empty()) + offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f); + else + { + Quad::Viewport v = quads[0]->getViewport(); + offset = love::Vector(v.x*0.5f, v.y*0.5f); + } +} + +void ParticleSystem::createBuffers(size_t size) +{ + try + { + pFree = pMem = new Particle[size]; + maxParticles = (uint32) size; + } + catch (std::bad_alloc &) + { + deleteBuffers(); + throw love::Exception("Out of memory"); + } +} + +void ParticleSystem::deleteBuffers() +{ + // Clean up for great gracefulness! + delete[] pMem; + + pMem = nullptr; + maxParticles = 0; + activeParticles = 0; +} + +void ParticleSystem::setBufferSize(uint32 size) +{ + if (size == 0 || size > MAX_PARTICLES) + throw love::Exception("Invalid buffer size"); + deleteBuffers(); + createBuffers(size); + reset(); +} + +uint32 ParticleSystem::getBufferSize() const +{ + return maxParticles; +} + +void ParticleSystem::addParticle(float t) +{ + if (isFull()) + return; + + // Gets a free particle and updates the allocation pointer. + Particle *p = pFree++; + initParticle(p, t); + + switch (insertMode) + { + default: + case INSERT_MODE_TOP: + insertTop(p); + break; + case INSERT_MODE_BOTTOM: + insertBottom(p); + break; + case INSERT_MODE_RANDOM: + insertRandom(p); + break; + } + + activeParticles++; +} + +void ParticleSystem::initParticle(Particle *p, float t) +{ + float min,max; + + // Linearly interpolate between the previous and current emitter position. + love::Vector pos = prevPosition + (position - prevPosition) * t; + + min = particleLifeMin; + max = particleLifeMax; + if (min == max) + p->life = min; + else + p->life = (float) rng.random(min, max); + p->lifetime = p->life; + + p->position = pos; + + switch (areaSpreadDistribution) + { + case DISTRIBUTION_UNIFORM: + p->position.x += (float) rng.random(-areaSpread.getX(), areaSpread.getX()); + p->position.y += (float) rng.random(-areaSpread.getY(), areaSpread.getY()); + break; + case DISTRIBUTION_NORMAL: + p->position.x += (float) rng.randomNormal(areaSpread.getX()); + p->position.y += (float) rng.randomNormal(areaSpread.getY()); + break; + case DISTRIBUTION_NONE: + default: + break; + } + + p->origin = pos; + + min = speedMin; + max = speedMax; + float speed = (float) rng.random(min, max); + + min = direction - spread/2.0f; + max = direction + spread/2.0f; + float dir = (float) rng.random(min, max); + + p->velocity = love::Vector(cosf(dir), sinf(dir)) * speed; + + p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x); + p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y); + + min = radialAccelerationMin; + max = radialAccelerationMax; + p->radialAcceleration = (float) rng.random(min, max); + + min = tangentialAccelerationMin; + max = tangentialAccelerationMax; + p->tangentialAcceleration = (float) rng.random(min, max); + + min = linearDampingMin; + max = linearDampingMax; + p->linearDamping = (float) rng.random(min, max); + + p->sizeOffset = (float) rng.random(sizeVariation); // time offset for size change + p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset; + p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)]; + + min = rotationMin; + max = rotationMax; + p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation); + p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation); + p->rotation = (float) rng.random(min, max); + + p->angle = p->rotation; + if (relativeRotation) + p->angle += atan2f(p->velocity.y, p->velocity.x); + + p->color = colors[0]; + + p->quadIndex = 0; +} + +void ParticleSystem::insertTop(Particle *p) +{ + if (pHead == nullptr) + { + pHead = p; + p->prev = nullptr; + } + else + { + pTail->next = p; + p->prev = pTail; + } + p->next = nullptr; + pTail = p; +} + +void ParticleSystem::insertBottom(Particle *p) +{ + if (pTail == nullptr) + { + pTail = p; + p->next = nullptr; + } + else + { + pHead->prev = p; + p->next = pHead; + } + p->prev = nullptr; + pHead = p; +} + +void ParticleSystem::insertRandom(Particle *p) +{ + // Nonuniform, but 64-bit is so large nobody will notice. Hopefully. + uint64 pos = rng.rand() % ((int64) activeParticles + 1); + + // Special case where the particle gets inserted before the head. + if (pos == activeParticles) + { + Particle *pA = pHead; + if (pA) + pA->prev = p; + p->prev = nullptr; + p->next = pA; + pHead = p; + return; + } + + // Inserts the particle after the randomly selected particle. + Particle *pA = pMem + pos; + Particle *pB = pA->next; + pA->next = p; + if (pB) + pB->prev = p; + else + pTail = p; + p->prev = pA; + p->next = pB; +} + +ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p) +{ + // The linked list is updated in this function and old pointers may be + // invalidated. The returned pointer will inform the caller of the new + // pointer to the next particle. + Particle *pNext = nullptr; + + // Removes the particle from the linked list. + if (p->prev) + p->prev->next = p->next; + else + pHead = p->next; + + if (p->next) + { + p->next->prev = p->prev; + pNext = p->next; + } + else + pTail = p->prev; + + // The (in memory) last particle can now be moved into the free slot. + // It will skip the moving if it happens to be the removed particle. + pFree--; + if (p != pFree) + { + *p = *pFree; + if (pNext == pFree) + pNext = p; + + if (p->prev) + p->prev->next = p; + else + pHead = p; + + if (p->next) + p->next->prev = p; + else + pTail = p; + } + + activeParticles--; + return pNext; +} + +void ParticleSystem::setTexture(Texture *tex) +{ + texture.set(tex); + + if (defaultOffset) + resetOffset(); +} + +Texture *ParticleSystem::getTexture() const +{ + return texture.get(); +} + +void ParticleSystem::setInsertMode(InsertMode mode) +{ + insertMode = mode; +} + +ParticleSystem::InsertMode ParticleSystem::getInsertMode() const +{ + return insertMode; +} + +void ParticleSystem::setEmissionRate(float rate) +{ + if (rate < 0.0f) + throw love::Exception("Invalid emission rate"); + emissionRate = rate; +} + +float ParticleSystem::getEmissionRate() const +{ + return emissionRate; +} + +void ParticleSystem::setEmitterLifetime(float life) +{ + this->life = lifetime = life; +} + +float ParticleSystem::getEmitterLifetime() const +{ + return lifetime; +} + +void ParticleSystem::setParticleLifetime(float min, float max) +{ + particleLifeMin = min; + if (max == 0) + particleLifeMax = min; + else + particleLifeMax = max; +} + +void ParticleSystem::getParticleLifetime(float &min, float &max) const +{ + min = particleLifeMin; + max = particleLifeMax; +} + +void ParticleSystem::setPosition(float x, float y) +{ + position = love::Vector(x, y); + prevPosition = position; +} + +const love::Vector &ParticleSystem::getPosition() const +{ + return position; +} + +void ParticleSystem::moveTo(float x, float y) +{ + position = love::Vector(x, y); +} + +void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y) +{ + areaSpread = love::Vector(x, y); + areaSpreadDistribution = distribution; +} + +ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const +{ + return areaSpreadDistribution; +} + +const love::Vector &ParticleSystem::getAreaSpreadParameters() const +{ + return areaSpread; +} + +void ParticleSystem::setDirection(float direction) +{ + this->direction = direction; +} + +float ParticleSystem::getDirection() const +{ + return direction; +} + +void ParticleSystem::setSpread(float spread) +{ + this->spread = spread; +} + +float ParticleSystem::getSpread() const +{ + return spread; +} + +void ParticleSystem::setSpeed(float speed) +{ + speedMin = speedMax = speed; +} + +void ParticleSystem::setSpeed(float min, float max) +{ + speedMin = min; + speedMax = max; +} + +void ParticleSystem::getSpeed(float &min, float &max) const +{ + min = speedMin; + max = speedMax; +} + +void ParticleSystem::setLinearAcceleration(float x, float y) +{ + linearAccelerationMin.x = linearAccelerationMax.x = x; + linearAccelerationMin.y = linearAccelerationMax.y = y; +} + +void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax) +{ + linearAccelerationMin = love::Vector(xmin, ymin); + linearAccelerationMax = love::Vector(xmax, ymax); +} + +void ParticleSystem::getLinearAcceleration(love::Vector &min, love::Vector &max) const +{ + min = linearAccelerationMin; + max = linearAccelerationMax; +} + +void ParticleSystem::setRadialAcceleration(float acceleration) +{ + radialAccelerationMin = radialAccelerationMax = acceleration; +} + +void ParticleSystem::setRadialAcceleration(float min, float max) +{ + radialAccelerationMin = min; + radialAccelerationMax = max; +} + +void ParticleSystem::getRadialAcceleration(float &min, float &max) const +{ + min = radialAccelerationMin; + max = radialAccelerationMax; +} + +void ParticleSystem::setTangentialAcceleration(float acceleration) +{ + tangentialAccelerationMin = tangentialAccelerationMax = acceleration; +} + +void ParticleSystem::setTangentialAcceleration(float min, float max) +{ + tangentialAccelerationMin = min; + tangentialAccelerationMax = max; +} + +void ParticleSystem::getTangentialAcceleration(float &min, float &max) const +{ + min = tangentialAccelerationMin; + max = tangentialAccelerationMax; +} + +void ParticleSystem::setLinearDamping(float min, float max) +{ + linearDampingMin = min; + linearDampingMax = max; +} + +void ParticleSystem::getLinearDamping(float &min, float &max) const +{ + min = linearDampingMin; + max = linearDampingMax; +} + +void ParticleSystem::setSize(float size) +{ + sizes.resize(1); + sizes[0] = size; +} + +void ParticleSystem::setSizes(const std::vector &newSizes) +{ + sizes = newSizes; +} + +const std::vector &ParticleSystem::getSizes() const +{ + return sizes; +} + +void ParticleSystem::setSizeVariation(float variation) +{ + sizeVariation = variation; +} + +float ParticleSystem::getSizeVariation() const +{ + return sizeVariation; +} + +void ParticleSystem::setRotation(float rotation) +{ + rotationMin = rotationMax = rotation; +} + +void ParticleSystem::setRotation(float min, float max) +{ + rotationMin = min; + rotationMax = max; +} + +void ParticleSystem::getRotation(float &min, float &max) const +{ + min = rotationMin; + max = rotationMax; +} + +void ParticleSystem::setSpin(float spin) +{ + spinStart = spin; + spinEnd = spin; +} + +void ParticleSystem::setSpin(float start, float end) +{ + spinStart = start; + spinEnd = end; +} + +void ParticleSystem::getSpin(float &start, float &end) const +{ + start = spinStart; + end = spinEnd; +} + +void ParticleSystem::setSpinVariation(float variation) +{ + spinVariation = variation; +} + +float ParticleSystem::getSpinVariation() const +{ + return spinVariation; +} + +void ParticleSystem::setOffset(float x, float y) +{ + offset = love::Vector(x, y); + defaultOffset = false; +} + +love::Vector ParticleSystem::getOffset() const +{ + return offset; +} + +void ParticleSystem::setColor(const std::vector &newColors) +{ + colors = newColors; + + for (Colorf &c : colors) + { + // We want to store the colors as [0, 1], rather than [0, 255]. + c.r /= 255.0f; + c.g /= 255.0f; + c.b /= 255.0f; + c.a /= 255.0f; + } +} + +std::vector ParticleSystem::getColor() const +{ + // The particle system stores colors in the range of [0, 1]... + std::vector ncolors(colors); + + for (Colorf &c : ncolors) + { + c.r *= 255.0f; + c.g *= 255.0f; + c.b *= 255.0f; + c.a *= 255.0f; + } + + return ncolors; +} + +void ParticleSystem::setQuads(const std::vector &newQuads) +{ + std::vector> quadlist; + quadlist.reserve(newQuads.size()); + + for (Quad *q : newQuads) + quadlist.push_back(q); + + quads = quadlist; + + if (defaultOffset) + resetOffset(); +} + +void ParticleSystem::setQuads() +{ + quads.clear(); +} + +std::vector ParticleSystem::getQuads() const +{ + std::vector quadlist; + quadlist.reserve(quads.size()); + + for (const StrongRef &q : quads) + quadlist.push_back(q.get()); + + return quadlist; +} + +void ParticleSystem::setRelativeRotation(bool enable) +{ + relativeRotation = enable; +} + +bool ParticleSystem::hasRelativeRotation() const +{ + return relativeRotation; +} + +uint32 ParticleSystem::getCount() const +{ + return activeParticles; +} + +void ParticleSystem::start() +{ + active = true; +} + +void ParticleSystem::stop() +{ + active = false; + life = lifetime; + emitCounter = 0; +} + +void ParticleSystem::pause() +{ + active = false; +} + +void ParticleSystem::reset() +{ + if (pMem == nullptr) + return; + + pFree = pMem; + pHead = nullptr; + pTail = nullptr; + activeParticles = 0; + life = lifetime; + emitCounter = 0; +} + +void ParticleSystem::emit(uint32 num) +{ + if (!active) + return; + + num = std::min(num, maxParticles - activeParticles); + + while (num--) + addParticle(1.0f); +} + +bool ParticleSystem::isActive() const +{ + return active; +} + +bool ParticleSystem::isPaused() const +{ + return !active && life < lifetime; +} + +bool ParticleSystem::isStopped() const +{ + return !active && life >= lifetime; +} + +bool ParticleSystem::isEmpty() const +{ + return activeParticles == 0; +} + +bool ParticleSystem::isFull() const +{ + return activeParticles == maxParticles; +} + +void ParticleSystem::update(float dt) +{ + if (pMem == nullptr || dt == 0.0f) + return; + + // Traverse all particles and update. + Particle *p = pHead; + + while (p) + { + // Decrease lifespan. + p->life -= dt; + + if (p->life <= 0) + p = removeParticle(p); + else + { + // Temp variables. + love::Vector radial, tangential; + love::Vector ppos = p->position; + + // Get vector from particle center to particle. + radial = ppos - p->origin; + radial.normalize(); + tangential = radial; + + // Resize radial acceleration. + radial *= p->radialAcceleration; + + // Calculate tangential acceleration. + { + float a = tangential.getX(); + tangential.setX(-tangential.getY()); + tangential.setY(a); + } + + // Resize tangential. + tangential *= p->tangentialAcceleration; + + // Update velocity. + p->velocity += (radial + tangential + p->linearAcceleration) * dt; + + // Apply damping. + p->velocity *= 1.0f / (1.0f + p->linearDamping * dt); + + // Modify position. + ppos += p->velocity * dt; + + p->position = ppos; + + const float t = 1.0f - p->life / p->lifetime; + + // Rotate. + p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt; + + p->angle = p->rotation; + + if (relativeRotation) + p->angle += atan2f(p->velocity.y, p->velocity.x); + + // Change size according to given intervals: + // i = 0 1 2 3 n-1 + // |-------|-------|------|--- ... ---| + // t = 0 1/(n-1) 3/(n-1) 1 + // + // `s' is the interpolation variable scaled to the current + // interval width, e.g. if n = 5 and t = 0.3, then the current + // indices are 1,2 and s = 0.3 - 0.25 = 0.05 + float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation + s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size() + size_t i = (size_t)s; + size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f) + s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1 + p->size = sizes[i] * (1.0f - s) + sizes[k] * s; + + // Update color according to given intervals (as above) + s = t * (float)(colors.size() - 1); + i = (size_t)s; + k = (i == colors.size() - 1) ? i : i + 1; + s -= (float)i; // 0 <= s <= 1 + p->color = colors[i] * (1.0f - s) + colors[k] * s; + + // Update the quad index. + k = quads.size(); + if (k > 0) + { + s = t * (float) k; // [0:numquads-1] (clamped below) + i = (s > 0.0f) ? (size_t) s : 0; + p->quadIndex = (int) ((i < k) ? i : k - 1); + } + + // Next particle. + p = p->next; + } + } + + // Make some more particles. + if (active) + { + float rate = 1.0f / emissionRate; // the amount of time between each particle emit + emitCounter += dt; + float total = emitCounter - rate; + while (emitCounter > rate) + { + addParticle(1.0f - (emitCounter - rate) / total); + emitCounter -= rate; + } + /*int particles = (int)(emissionRate * dt); + for (int i = 0; i != particles; i++) + add();*/ + + life -= dt; + if (lifetime != -1 && life < 0) + stop(); + } + + prevPosition = position; +} + +bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out) +{ + return distributions.find(in, out); +} + +bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out) +{ + return distributions.find(in, out); +} + +bool ParticleSystem::getConstant(const char *in, InsertMode &out) +{ + return insertModes.find(in, out); +} + +bool ParticleSystem::getConstant(InsertMode in, const char *&out) +{ + return insertModes.find(in, out); +} + +StringMap::Entry ParticleSystem::distributionsEntries[] = +{ + { "none", DISTRIBUTION_NONE }, + { "uniform", DISTRIBUTION_UNIFORM }, + { "normal", DISTRIBUTION_NORMAL }, +}; + +StringMap ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries)); + +StringMap::Entry ParticleSystem::insertModesEntries[] = +{ + { "top", INSERT_MODE_TOP }, + { "bottom", INSERT_MODE_BOTTOM }, + { "random", INSERT_MODE_RANDOM }, +}; + +StringMap ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries)); + +} // graphics +} // love diff --git a/jni/love/src/modules/graphics/ParticleSystem.h b/jni/love/src/modules/graphics/ParticleSystem.h new file mode 100644 index 00000000..9fe1ecd8 --- /dev/null +++ b/jni/love/src/modules/graphics/ParticleSystem.h @@ -0,0 +1,672 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_GRAPHICS_PARTICLE_SYSTEM_H +#define LOVE_GRAPHICS_PARTICLE_SYSTEM_H + +// LOVE +#include "common/int.h" +#include "common/math.h" +#include "common/Vector.h" +#include "Drawable.h" +#include "Color.h" +#include "Quad.h" +#include "Texture.h" + +// STL +#include + +namespace love +{ +namespace graphics +{ + +/** + * A class for creating, moving and drawing particles. + * A big thanks to bobthebloke.org + **/ +class ParticleSystem : public Drawable +{ +public: + /** + * Type of distribution new particles are drawn from: None, uniform, normal. + */ + enum AreaSpreadDistribution + { + DISTRIBUTION_NONE, + DISTRIBUTION_UNIFORM, + DISTRIBUTION_NORMAL, + DISTRIBUTION_MAX_ENUM + }; + + /** + * Insertion modes of new particles in the list: top, bottom, random. + */ + enum InsertMode + { + INSERT_MODE_TOP, + INSERT_MODE_BOTTOM, + INSERT_MODE_RANDOM, + INSERT_MODE_MAX_ENUM + }; + + /** + * Maximum numbers of particles in a ParticleSystem. + * This limit comes from the fact that a quad requires four vertices and the + * OpenGL API where GLsizei is a signed int. + **/ + static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4; + + /** + * Creates a particle system with the specified buffer size and texture. + **/ + ParticleSystem(Texture *texture, uint32 buffer); + ParticleSystem(const ParticleSystem &p); + + /** + * Deletes any allocated memory. + **/ + virtual ~ParticleSystem(); + + /** + * Creates an identical copy of this ParticleSystem. The clone does not + * duplicate any existing particles from this ParticleSystem, just the + * settable parameters. + **/ + virtual ParticleSystem *clone() = 0; + + /** + * Sets the texture used in the particle system. + * @param texture The new texture. + **/ + void setTexture(Texture *texture); + + /** + * Returns the texture used when drawing the particle system. + **/ + Texture *getTexture() const; + + /** + * Clears the current buffer and allocates the appropriate amount of space for the buffer. + * @param size The new buffer size. + **/ + virtual void setBufferSize(uint32 size); + + /** + * Returns the total amount of particles this ParticleSystem can have active + * at any given point in time. + **/ + uint32 getBufferSize() const; + + /** + * Sets the insert mode for new particles. + * @param mode The new insert mode. + */ + void setInsertMode(InsertMode mode); + + /** + * Returns the current insert mode. + */ + InsertMode getInsertMode() const; + + /** + * Sets the emission rate. + * @param rate The amount of particles per second. + **/ + void setEmissionRate(float rate); + + /** + * Returns the number of particles created per second. + **/ + float getEmissionRate() const; + + /** + * Sets the lifetime of the particle emitter (-1 means eternal) + * @param life The lifetime (in seconds). + **/ + void setEmitterLifetime(float life); + + /** + * Returns the lifetime of the particle emitter. + **/ + float getEmitterLifetime() const; + + /** + * Sets the life range of the particles. + * @param min The minimum life. + * @param max The maximum life (if 0, then becomes the same as minimum life). + **/ + void setParticleLifetime(float min, float max = 0); + + /** + * Gets the lifetime of a particle. + * @param[out] min The minimum life. + * @param[out] max The maximum life. + **/ + void getParticleLifetime(float &min, float &max) const; + + /** + * Sets the position of the center of the emitter. + * Used to move the emitter without changing the position of already existing particles. + * @param x The x-coordinate. + * @param y The y-coordinate. + **/ + void setPosition(float x, float y); + + /** + * Returns the position of the emitter. + **/ + const love::Vector &getPosition() const; + + /** + * Moves the position of the center of the emitter. + * When update is called, newly spawned particles will appear in a line + * between the old emitter position and where the emitter was moved to, + * resulting in a smoother-feeling particle system if moveTo is called + * repeatedly. + **/ + void moveTo(float x, float y); + + /** + * Sets the emission area spread parameters and distribution type. The interpretation of + * the parameters depends on the distribution type: + * + * * None: Parameters are ignored. No area spread. + * * Uniform: Parameters denote maximal (symmetric) displacement from emitter position. + * * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated. + * @param x First parameter. Interpretation depends on distribution type. + * @param y Second parameter. Interpretation depends on distribution type. + * @param distribution Distribution type + **/ + void setAreaSpread(AreaSpreadDistribution distribution, float x, float y); + + /** + * Returns area spread distribution type. + **/ + AreaSpreadDistribution getAreaSpreadDistribution() const; + + /** + * Returns area spread parameters. + **/ + const love::Vector &getAreaSpreadParameters() const; + + /** + * Sets the direction of the particle emitter. + * @param direction The direction (in degrees). + **/ + void setDirection(float direction); + + /** + * Returns the direction of the particle emitter (in radians). + **/ + float getDirection() const; + + /** + * Sets the spread of the particle emitter. + * @param spread The spread (in radians). + **/ + void setSpread(float spread); + + /** + * Returns the directional spread of the emitter (in radians). + **/ + float getSpread() const; + + /** + * Sets the speed of the particles. + * @param speed The speed. + **/ + void setSpeed(float speed); + + /** + * Sets the speed of the particles. + * @param min The minimum speed. + * @param max The maximum speed. + **/ + void setSpeed(float min, float max); + + /** + * Gets the speed of the particles. + * @param[out] min The minimum speed. + * @param[out] max The maximum speed. + **/ + void getSpeed(float &min, float &max) const; + + /** + * Sets the linear acceleration (the acceleration along the x and y axes). + * @param x The acceleration along the x-axis. + * @param y The acceleration along the y-axis. + **/ + void setLinearAcceleration(float x, float y); + + /** + * Sets the linear acceleration (the acceleration along the x and y axes). + * @param xmin The minimum amount of acceleration along the x-axis. + * @param ymin The minimum amount of acceleration along the y-axis. + * @param xmax The maximum amount of acceleration along the x-axis. + * @param ymax The maximum amount of acceleration along the y-axis. + **/ + void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax); + + /** + * Gets the linear acceleration of the particles. + * @param[out] min The minimum acceleration. + * @param[out] max The maximum acceleration. + **/ + void getLinearAcceleration(love::Vector &min, love::Vector &max) const; + + /** + * Sets the radial acceleration (the acceleration towards the particle emitter). + * @param acceleration The amount of acceleration. + **/ + void setRadialAcceleration(float acceleration); + + /** + * Sets the radial acceleration (the acceleration towards the particle emitter). + * @param min The minimum acceleration. + * @param max The maximum acceleration. + **/ + void setRadialAcceleration(float min, float max); + + /** + * Gets the radial acceleration. + * @param[out] min The minimum amount of radial acceleration. + * @param[out] max The maximum amount of radial acceleration. + **/ + void getRadialAcceleration(float &min, float &max) const; + + /** + * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction). + * @param acceleration The amount of acceleration. + **/ + void setTangentialAcceleration(float acceleration); + + /** + * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction). + * @param min The minimum acceleration. + * @param max The maximum acceleration. + **/ + void setTangentialAcceleration(float min, float max); + + /** + * Gets the tangential acceleration. + * @param[out] min The minimum tangential acceleration. + * @param[out] max The maximum tangential acceleration. + **/ + void getTangentialAcceleration(float &min, float &max) const; + + /** + * Sets the amount of linear damping. Damping reduces the velocity of + * particles over time. A value of 0 corresponds to no damping. + **/ + void setLinearDamping(float min, float max); + + /** + * Gets the current amount of linear damping. + **/ + void getLinearDamping(float &min, float &max) const; + + /** + * Sets the size of the sprite (1.0 being the default size). + * @param size The size of the sprite. + **/ + void setSize(float size); + + /** + * Sets the sizes of the sprite upon creation and upon death (1.0 being the default size). + * @param newSizes Array of sizes + **/ + void setSizes(const std::vector &newSizes); + + /** + * Returns the sizes of the particle sprites. + **/ + const std::vector &getSizes() const; + + /** + * Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end). + * @param variation The amount of variation. + **/ + void setSizeVariation(float variation); + + /** + * Returns the amount of initial size variation between particles. + **/ + float getSizeVariation() const; + + /** + * Sets the amount of rotation a sprite starts out with. + * @param rotation The amount of rotation. + **/ + void setRotation(float rotation); + + /** + * Sets the amount of rotation a sprite starts out with (a random value between min and max). + * @param min The minimum amount of rotation. + * @param max The maximum amount of rotation. + **/ + void setRotation(float min, float max); + + /** + * Gets the initial amount of rotation of a particle, in radians. + * @param[out] min The minimum initial rotation. + * @param[out] max The maximum initial rotation. + **/ + void getRotation(float &min, float &max) const; + + /** + * Sets the spin of the sprite. + * @param spin The spin of the sprite (in degrees). + **/ + void setSpin(float spin); + + /** + * Sets the spin of the sprite upon particle creation and death. + * @param start The spin of the sprite upon creation (in radians / second). + * @param end The spin of the sprite upon death (in radians / second). + **/ + void setSpin(float start, float end); + + /** + * Gets the amount of spin of a particle during its lifetime. + * @param[out] start The initial spin, in radians / s. + * @param[out] end The final spin, in radians / s. + **/ + void getSpin(float &start, float &end) const; + + /** + * Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end). + * @param variation The variation. + **/ + void setSpinVariation(float variation); + + /** + * Returns the amount of variation of the start spin of a particle. + **/ + float getSpinVariation() const; + + /** + * Sets the particles' offsets for rotation. + * @param x The x offset. + * @param y The y offset. + **/ + void setOffset(float x, float y); + + /** + * Returns of the particle offset. + **/ + love::Vector getOffset() const; + + /** + * Sets the color of the particles. + * @param newColors Array of colors + **/ + void setColor(const std::vector &newColors); + + /** + * Returns the color of the particles. + **/ + std::vector getColor() const; + + /** + * Sets a list of Quads to use for particles over their lifetime. + **/ + void setQuads(const std::vector &newQuads); + void setQuads(); + + /** + * Gets the Quads used when drawing the particles. + **/ + std::vector getQuads() const; + + /** + * sets whether particle angles & rotations are relative to their velocities. + **/ + void setRelativeRotation(bool enable); + bool hasRelativeRotation() const; + + /** + * Returns the amount of particles that are currently active in the system. + **/ + uint32 getCount() const; + + /** + * Starts/resumes the particle emitter. + **/ + void start(); + + /** + * Stops the particle emitter and resets. + **/ + void stop(); + + /** + * Pauses the particle emitter. + **/ + void pause(); + + /** + * Resets the particle emitter. + **/ + void reset(); + + /** + * Instantly emits a number of particles. + * @param num The number of particles to emit. + **/ + void emit(uint32 num); + + /** + * Returns whether the particle emitter is active. + **/ + bool isActive() const; + + /** + * Returns whether the particle emitter is paused. + **/ + bool isPaused() const; + + bool isStopped() const; + + /** + * Returns whether the particle system is empty of particles or not. + **/ + bool isEmpty() const; + + /** + * Returns whether the amount of particles has reached the buffer limit or not. + **/ + bool isFull() const; + + /** + * Updates the particle system. + * @param dt Time since last update. + **/ + void update(float dt); + + static bool getConstant(const char *in, AreaSpreadDistribution &out); + static bool getConstant(AreaSpreadDistribution in, const char *&out); + + static bool getConstant(const char *in, InsertMode &out); + static bool getConstant(InsertMode in, const char *&out); + +protected: + + // Represents a single particle. + struct Particle + { + Particle *prev; + Particle *next; + + float lifetime; + float life; + + love::Vector position; + + // Particles gravitate towards this point. + love::Vector origin; + + love::Vector velocity; + love::Vector linearAcceleration; + float radialAcceleration; + float tangentialAcceleration; + + float linearDamping; + + float size; + float sizeOffset; + float sizeIntervalSize; + + float rotation; // Amount of rotation applied to the final angle. + float angle; + float spinStart; + float spinEnd; + + Colorf color; + + int quadIndex; + }; + + // Pointer to the beginning of the allocated memory. + Particle *pMem; + + // Pointer to a free particle. + Particle *pFree; + + // Pointer to the start of the linked list. + Particle *pHead; + + // Pointer to the end of the linked list. + Particle *pTail; + + // The texture to be drawn. + StrongRef texture; + + // Whether the particle emitter is active. + bool active; + + // Insert mode of new particles. + InsertMode insertMode; + + // The maximum number of particles. + uint32 maxParticles; + + // The number of active particles. + uint32 activeParticles; + + // The emission rate (particles/sec). + float emissionRate; + + // Used to determine when a particle should be emitted. + float emitCounter; + + // The relative position of the particle emitter. + love::Vector position; + love::Vector prevPosition; + + // Emission area spread. + AreaSpreadDistribution areaSpreadDistribution; + love::Vector areaSpread; + + // The lifetime of the particle emitter (-1 means infinite) and the life it has left. + float lifetime; + float life; + + // The particle life. + float particleLifeMin; + float particleLifeMax; + + // The direction (and spread) the particles will be emitted in. Measured in radians. + float direction; + float spread; + + // The speed. + float speedMin; + float speedMax; + + // Acceleration along the x and y axes. + love::Vector linearAccelerationMin; + love::Vector linearAccelerationMax; + + // Acceleration towards the emitter's center + float radialAccelerationMin; + float radialAccelerationMax; + + // Acceleration perpendicular to the particle's direction. + float tangentialAccelerationMin; + float tangentialAccelerationMax; + + float linearDampingMin; + float linearDampingMax; + + // Size. + std::vector sizes; + float sizeVariation; + + // Rotation + float rotationMin; + float rotationMax; + + // Spin. + float spinStart; + float spinEnd; + float spinVariation; + + // Offsets + love::Vector offset; + + // Is the ParticleSystem using a default offset? + bool defaultOffset; + + // Color. + std::vector colors; + + // Quads. + std::vector> quads; + + bool relativeRotation; + +private: + + void resetOffset(); + + void createBuffers(size_t size); + void deleteBuffers(); + + void addParticle(float t); + Particle *removeParticle(Particle *p); + + // Called by addParticle. + void initParticle(Particle *p, float t); + void insertTop(Particle *p); + void insertBottom(Particle *p); + void insertRandom(Particle *p); + + static StringMap::Entry distributionsEntries[]; + static StringMap distributions; + + static StringMap::Entry insertModesEntries[]; + static StringMap insertModes; +}; + +} // graphics +} // love + +#endif // LOVE_GRAPHICS_PARTICLE_SYSTEM_H diff --git a/jni/love/src/modules/graphics/Quad.cpp b/jni/love/src/modules/graphics/Quad.cpp index d4aa80b2..61ff71e6 100644 --- a/jni/love/src/modules/graphics/Quad.cpp +++ b/jni/love/src/modules/graphics/Quad.cpp @@ -29,7 +29,7 @@ namespace love namespace graphics { -Quad::Quad(const Quad::Viewport &v, float sw, float sh) +Quad::Quad(const Quad::Viewport &v, double sw, double sh) : sw(sw) , sh(sh) { @@ -41,7 +41,7 @@ Quad::~Quad() { } -void Quad::refresh(const Quad::Viewport &v, float sw, float sh) +void Quad::refresh(const Quad::Viewport &v, double sw, double sh) { viewport = v; @@ -53,20 +53,20 @@ void Quad::refresh(const Quad::Viewport &v, float sw, float sh) vertices[0].x = 0.0f; vertices[0].y = 0.0f; vertices[1].x = 0.0f; - vertices[1].y = v.h; - vertices[2].x = v.w; + vertices[1].y = (float) v.h; + vertices[2].x = (float) v.w; vertices[2].y = 0.0f; - vertices[3].x = v.w; - vertices[3].y = v.h; + vertices[3].x = (float) v.w; + vertices[3].y = (float) v.h; - vertices[0].s = v.x/sw; - vertices[0].t = v.y/sh; - vertices[1].s = v.x/sw; - vertices[1].t = (v.y+v.h)/sh; - vertices[2].s = (v.x+v.w)/sw; - vertices[2].t = v.y/sh; - vertices[3].s = (v.x+v.w)/sw; - vertices[3].t = (v.y+v.h)/sh; + vertices[0].s = (float) (v.x/sw); + vertices[0].t = (float) (v.y/sh); + vertices[1].s = (float) (v.x/sw); + vertices[1].t = (float) ((v.y+v.h)/sh); + vertices[2].s = (float) ((v.x+v.w)/sw); + vertices[2].t = (float) (v.y/sh); + vertices[3].s = (float) ((v.x+v.w)/sw); + vertices[3].t = (float) ((v.y+v.h)/sh); } void Quad::setViewport(const Quad::Viewport &v) diff --git a/jni/love/src/modules/graphics/Quad.h b/jni/love/src/modules/graphics/Quad.h index c146c97e..32647aa8 100644 --- a/jni/love/src/modules/graphics/Quad.h +++ b/jni/love/src/modules/graphics/Quad.h @@ -36,14 +36,14 @@ public: struct Viewport { - float x, y; - float w, h; + double x, y; + double w, h; }; - Quad(const Viewport &v, float sw, float sh); + Quad(const Viewport &v, double sw, double sh); virtual ~Quad(); - void refresh(const Viewport &v, float sw, float sh); + void refresh(const Viewport &v, double sw, double sh); void setViewport(const Viewport &v); Viewport getViewport() const; @@ -54,8 +54,8 @@ private: Vertex vertices[4]; Viewport viewport; - float sw; - float sh; + double sw; + double sh; }; // Quad diff --git a/jni/love/src/modules/graphics/Texture.cpp b/jni/love/src/modules/graphics/Texture.cpp index d1e268dd..fd21c947 100644 --- a/jni/love/src/modules/graphics/Texture.cpp +++ b/jni/love/src/modules/graphics/Texture.cpp @@ -114,18 +114,19 @@ bool Texture::getConstant(WrapMode in, const char *&out) StringMap::Entry Texture::filterModeEntries[] = { - { "linear", Texture::FILTER_LINEAR }, - { "nearest", Texture::FILTER_NEAREST }, - { "none", Texture::FILTER_NONE }, + { "linear", FILTER_LINEAR }, + { "nearest", FILTER_NEAREST }, + { "none", FILTER_NONE }, }; StringMap Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries)); StringMap::Entry Texture::wrapModeEntries[] = { - { "clamp", Texture::WRAP_CLAMP }, - { "repeat", Texture::WRAP_REPEAT }, - { "mirroredrepeat", Texture::WRAP_MIRRORED_REPEAT }, + { "clamp", WRAP_CLAMP }, + { "clampzero", WRAP_CLAMP_ZERO }, + { "repeat", WRAP_REPEAT }, + { "mirroredrepeat", WRAP_MIRRORED_REPEAT }, }; StringMap Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries)); diff --git a/jni/love/src/modules/graphics/Texture.h b/jni/love/src/modules/graphics/Texture.h index fb6082a7..3176c0b1 100644 --- a/jni/love/src/modules/graphics/Texture.h +++ b/jni/love/src/modules/graphics/Texture.h @@ -43,6 +43,7 @@ public: enum WrapMode { WRAP_CLAMP, + WRAP_CLAMP_ZERO, WRAP_REPEAT, WRAP_MIRRORED_REPEAT, WRAP_MAX_ENUM diff --git a/jni/love/src/modules/graphics/opengl/Canvas.cpp b/jni/love/src/modules/graphics/opengl/Canvas.cpp index 1770a81e..a2904c6c 100644 --- a/jni/love/src/modules/graphics/opengl/Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/Canvas.cpp @@ -361,6 +361,14 @@ bool Canvas::setWrap(const Texture::Wrap &w) wrap.s = wrap.t = WRAP_CLAMP; } + if (!gl.isClampZeroTextureWrapSupported()) + { + if (wrap.s == WRAP_CLAMP_ZERO) + wrap.s = WRAP_CLAMP; + if (wrap.t == WRAP_CLAMP_ZERO) + wrap.t = WRAP_CLAMP; + } + gl.bindTexture(texture); gl.setTextureWrap(wrap); @@ -407,9 +415,6 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases.size() > 0) { - if (!isMultiCanvasSupported()) - throw love::Exception("Multi-canvas rendering is not supported on this system."); - if ((int) canvases.size() + 1 > gl.getMaxRenderTargets()) throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1); @@ -804,15 +809,9 @@ bool Canvas::isSupported() return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object; } -bool Canvas::isMultiCanvasSupported() -{ - // system must support at least 4 simultaneous active canvases. - return gl.getMaxRenderTargets() >= 4; -} - bool Canvas::isMultiFormatMultiCanvasSupported() { - return isMultiCanvasSupported() && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object); + return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object); } bool Canvas::supportedFormats[] = {false}; diff --git a/jni/love/src/modules/graphics/opengl/Canvas.h b/jni/love/src/modules/graphics/opengl/Canvas.h index 23e89c21..2db2d4c7 100644 --- a/jni/love/src/modules/graphics/opengl/Canvas.h +++ b/jni/love/src/modules/graphics/opengl/Canvas.h @@ -55,13 +55,13 @@ public: FORMAT_RG8, // Two-channel (red and green) with 8 bits per channel. FORMAT_RGBA8, // RGBA with 8 bits per channel. FORMAT_RGB10A2, // RGB with 10 bits each, and A with 2 bits. - FORMAT_RG11B10F, // Floating point [0, +inf]. RG with 11 FP bits each, and B with 10 FP bits. - FORMAT_R16F, // Floating point [-inf, +inf]. R with 16 FP bits. - FORMAT_RG16F, // Floating point [-inf, +inf]. RG with 16 FP bits per channel. - FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per channel. - FORMAT_R32F, // Floating point [-inf, +inf]. R with 32 FP bits. - FORMAT_RG32F, // Floating point [-inf, +inf]. RG with 32 FP bits per channel. - FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per channel. + FORMAT_RG11B10F, // Floating point [0, +65024]. RG with 11 FP bits each, and B with 10 FP bits. + FORMAT_R16F, // Floating point [-65504, +65504]. R with 16 FP bits. + FORMAT_RG16F, // Floating point [-65504, +65504]. RG with 16 FP bits per channel. + FORMAT_RGBA16F, // Floating point [-65504, +65504]. RGBA with 16 FP bits per channel. + FORMAT_R32F, // Floating point [-65504, +65504]. R with 32 FP bits. + FORMAT_RG32F, // Floating point [-65504, +65504]. RG with 32 FP bits per channel. + FORMAT_RGBA32F, // Floating point [-65504, +65504]. RGBA with 32 FP bits per channel. FORMAT_SRGB, // sRGB with 8 bits per channel, plus 8 bit linear A. FORMAT_MAX_ENUM }; @@ -118,7 +118,6 @@ public: } static bool isSupported(); - static bool isMultiCanvasSupported(); static bool isMultiFormatMultiCanvasSupported(); static bool isFormatSupported(Format format); diff --git a/jni/love/src/modules/graphics/opengl/Font.cpp b/jni/love/src/modules/graphics/opengl/Font.cpp index e303036c..25391875 100644 --- a/jni/love/src/modules/graphics/opengl/Font.cpp +++ b/jni/love/src/modules/graphics/opengl/Font.cpp @@ -38,6 +38,11 @@ namespace graphics namespace opengl { +static inline uint16 normToUint16(double n) +{ + return (uint16) (n * LOVE_UINT16_MAX); +} + int Font::fontCount = 0; Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter) @@ -272,18 +277,20 @@ const Font::Glyph &Font::addGlyph(uint32 glyph) g.texture = t; - float tX = (float) textureX, tY = (float) textureY; - float tWidth = (float) textureWidth, tHeight = (float) textureHeight; + double tX = (double) textureX, tY = (double) textureY; + double tWidth = (double) textureWidth, tHeight = (double) textureHeight; + + Color c(255, 255, 255, 255); // 0----2 // | / | // | / | // 1----3 const GlyphVertex verts[4] = { - { 0.0f, 0.0f, tX/tWidth, tY/tHeight}, - { 0.0f, float(h), tX/tWidth, (tY+h)/tHeight}, - {float(w), 0.0f, (tX+w)/tWidth, tY/tHeight}, - {float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight} + {float(0), float(0), normToUint16((tX+0)/tWidth), normToUint16((tY+0)/tHeight), c}, + {float(0), float(h), normToUint16((tX+0)/tWidth), normToUint16((tY+h)/tHeight), c}, + {float(w), float(0), normToUint16((tX+w)/tWidth), normToUint16((tY+0)/tHeight), c}, + {float(w), float(h), normToUint16((tX+w)/tWidth), normToUint16((tY+h)/tHeight), c} }; // Copy vertex data to the glyph and set proper bearing. @@ -341,28 +348,9 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph) return k; } -float Font::getHeight() const +void Font::getCodepointsFromString(const std::string &text, Codepoints &codepoints) { - return (float) height; -} - -std::vector Font::generateVertices(const std::string &text, std::vector &vertices, float extra_spacing, Vector offset, TextInfo *info) -{ - // Spacing counter and newline handling. - float dx = offset.x; - float dy = offset.y; - - float lineheight = getBaseline(); - int maxwidth = 0; - - // Keeps track of when we need to switch textures in our vertex array. - std::vector drawcommands; - - // Pre-allocate space for the maximum possible number of vertices. - size_t vertstartsize = vertices.size(); - vertices.reserve(vertstartsize + text.length() * 4); - - uint32 prevglyph = 0; + codepoints.reserve(text.size()); try { @@ -372,80 +360,152 @@ std::vector Font::generateVertices(const std::string &text, s while (i != end) { uint32 g = *i++; - - if (g == '\n') - { - if (dx > maxwidth) - maxwidth = (int) dx; - - // Wrap newline, but do not print it. - dy += floorf(getHeight() * getLineHeight() + 0.5f); - dx = offset.x; - continue; - } - - uint32 cacheid = textureCacheID; - - const Glyph &glyph = findGlyph(g); - - // If findGlyph invalidates the texture cache, re-start the loop. - if (cacheid != textureCacheID) - { - i = utf8::iterator(text.begin(), text.begin(), text.end()); - maxwidth = 0; - dx = offset.x; - dy = offset.y; - drawcommands.clear(); - vertices.resize(vertstartsize); - prevglyph = 0; - continue; - } - - // Add kerning to the current horizontal offset. - dx += getKerning(prevglyph, g); - - if (glyph.texture != 0) - { - // Copy the vertices and set their proper relative positions. - for (int j = 0; j < 4; j++) - { - vertices.push_back(glyph.vertices[j]); - vertices.back().x += dx; - vertices.back().y += dy + lineheight; - } - - // Check if glyph texture has changed since the last iteration. - if (drawcommands.empty() || drawcommands.back().texture != glyph.texture) - { - // Add a new draw command if the texture has changed. - DrawCommand cmd; - cmd.startvertex = (int) vertices.size() - 4; - cmd.vertexcount = 0; - cmd.texture = glyph.texture; - drawcommands.push_back(cmd); - } - - drawcommands.back().vertexcount += 4; - } - - // Advance the x position for the next glyph. - dx += glyph.spacing; - - // Account for extra spacing given to space characters. - if (g == ' ' && extra_spacing != 0.0f) - dx = floorf(dx + extra_spacing); - - prevglyph = g; + codepoints.push_back(g); } } catch (utf8::exception &e) { throw love::Exception("UTF-8 decoding error: %s", e.what()); } +} - // Sort draw commands by texture first, and quad position in memory second - // (using the struct's < operator). - std::sort(drawcommands.begin(), drawcommands.end()); +void Font::getCodepointsFromString(const std::vector &strs, ColoredCodepoints &codepoints) +{ + if (strs.empty()) + return; + + codepoints.cps.reserve(strs[0].str.size()); + + for (const ColoredString &cstr : strs) + { + IndexedColor c = {cstr.color, (int) codepoints.cps.size()}; + codepoints.colors.push_back(c); + + getCodepointsFromString(cstr.str, codepoints.cps); + } + + if (codepoints.colors.size() == 1) + { + IndexedColor c = codepoints.colors[0]; + + if (c.index == 0 && c.color == Color(255, 255, 255, 255)) + codepoints.colors.pop_back(); + } +} + +float Font::getHeight() const +{ + return (float) height; +} + +std::vector Font::generateVertices(const ColoredCodepoints &codepoints, std::vector &vertices, float extra_spacing, Vector offset, TextInfo *info) +{ + // Spacing counter and newline handling. + float dx = offset.x; + float dy = offset.y; + + float lineheight = getBaseline(); + int maxwidth = 0; + + // Keeps track of when we need to switch textures in our vertex array. + std::vector commands; + + // Pre-allocate space for the maximum possible number of vertices. + size_t vertstartsize = vertices.size(); + vertices.reserve(vertstartsize + codepoints.cps.size() * 4); + + uint32 prevglyph = 0; + + Color curcolor(255, 255, 255, 255); + int curcolori = -1; + int ncolors = (int) codepoints.colors.size(); + + for (int i = 0; i < (int) codepoints.cps.size(); i++) + { + uint32 g = codepoints.cps[i]; + + if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i) + curcolor = codepoints.colors[++curcolori].color; + + if (g == '\n') + { + if (dx > maxwidth) + maxwidth = (int) dx; + + // Wrap newline, but do not print it. + dy += floorf(getHeight() * getLineHeight() + 0.5f); + dx = offset.x; + continue; + } + + uint32 cacheid = textureCacheID; + + const Glyph &glyph = findGlyph(g); + + // If findGlyph invalidates the texture cache, re-start the loop. + if (cacheid != textureCacheID) + { + i = 0; + maxwidth = 0; + dx = offset.x; + dy = offset.y; + commands.clear(); + vertices.resize(vertstartsize); + prevglyph = 0; + curcolori = -1; + curcolor = Color(255, 255, 255, 255); + continue; + } + + // Add kerning to the current horizontal offset. + dx += getKerning(prevglyph, g); + + if (glyph.texture != 0) + { + // Copy the vertices and set their colors and relative positions. + for (int j = 0; j < 4; j++) + { + vertices.push_back(glyph.vertices[j]); + vertices.back().x += dx; + vertices.back().y += dy + lineheight; + vertices.back().color = curcolor; + } + + // Check if glyph texture has changed since the last iteration. + if (commands.empty() || commands.back().texture != glyph.texture) + { + // Add a new draw command if the texture has changed. + DrawCommand cmd; + cmd.startvertex = (int) vertices.size() - 4; + cmd.vertexcount = 0; + cmd.texture = glyph.texture; + commands.push_back(cmd); + } + + commands.back().vertexcount += 4; + } + + // Advance the x position for the next glyph. + dx += glyph.spacing; + + // Account for extra spacing given to space characters. + if (g == ' ' && extra_spacing != 0.0f) + dx = floorf(dx + extra_spacing); + + prevglyph = g; + + } + + const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool + { + // Texture binds are expensive, so we should sort by that first. + if (a.texture != b.texture) + return a.texture < b.texture; + else + return a.startvertex < b.startvertex; + }; + + std::sort(commands.begin(), commands.end(), drawsort); if (dx > maxwidth) maxwidth = (int) dx; @@ -455,43 +515,44 @@ std::vector Font::generateVertices(const std::string &text, s info->width = maxwidth - offset.x;; info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y; } - - return drawcommands; + + return commands; } -std::vector Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector &vertices, TextInfo *info) +std::vector Font::generateVertices(const std::string &text, std::vector &vertices, float extra_spacing, Vector offset, TextInfo *info) { - if (wrap < 0.0f) - wrap = std::numeric_limits::max(); + ColoredCodepoints codepoints; + getCodepointsFromString(text, codepoints.cps); + return generateVertices(codepoints, vertices, extra_spacing, offset, info); +} + +std::vector Font::generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector &vertices, TextInfo *info) +{ + + wrap = std::max(wrap, 0.0f); uint32 cacheid = textureCacheID; std::vector drawcommands; - vertices.reserve(text.length() * 4); + vertices.reserve(text.cps.size() * 4); - // wrappedlines indicates which lines were automatically wrapped. It - // has the same number of elements as lines_to_draw. - std::vector wrappedlines; std::vector widths; - std::vector lines; + std::vector lines; - // We only need the list of wrapped lines in 'justify' mode. - getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr); + getWrap(text, wrap, lines, &widths); - float extraspacing = 0.0f; - int numspaces = 0; - int i = 0; float y = 0.0f; - float maxwidth = 0; + float maxwidth = 0.0f; - for (const std::string &line : lines) + for (int i = 0; i < (int) lines.size(); i++) { - extraspacing = 0.0f; + const auto &line = lines[i]; + float width = (float) widths[i]; love::Vector offset(0.0f, floorf(y)); + float extraspacing = 0.0f; - if (width > maxwidth) - maxwidth = width; + maxwidth = std::max(width, maxwidth); switch (align) { @@ -502,22 +563,24 @@ std::vector Font::generateVerticesFormatted(const std::string offset.x = floorf((wrap - width) / 2.0f); break; case ALIGN_JUSTIFY: - numspaces = (int) std::count(line.begin(), line.end(), ' '); - if (wrappedlines[i] && numspaces >= 1) - extraspacing = (wrap - width) / float(numspaces); + { + float numspaces = (float) std::count(line.cps.begin(), line.cps.end(), ' '); + if (width < wrap && numspaces >= 1) + extraspacing = (wrap - width) / numspaces; else extraspacing = 0.0f; break; + } case ALIGN_LEFT: default: break; } - std::vector commands = generateVertices(line, vertices, extraspacing, offset); + std::vector newcommands = generateVertices(line, vertices, extraspacing, offset); - if (!commands.empty()) + if (!newcommands.empty()) { - auto firstcmd = commands.begin(); + auto firstcmd = newcommands.begin(); // If the first draw command in the new list has the same texture // as the last one in the existing list we're building and its @@ -533,11 +596,10 @@ std::vector Font::generateVerticesFormatted(const std::string } // Append the new draw commands to the list we're building. - drawcommands.insert(drawcommands.end(), firstcmd, commands.end()); + drawcommands.insert(drawcommands.end(), firstcmd, newcommands.end()); } y += getHeight() * getLineHeight(); - i++; } if (info != nullptr) @@ -597,30 +659,37 @@ void Font::printv(const Matrix4 &t, const std::vector &drawcommands OpenGL::TempTransform transform(gl); transform.get() *= t; - gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD); - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GlyphVertex), &vertices[0].s); + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlyphVertex), &vertices[0].color.r); + + gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR); drawVertices(drawcommands); } -void Font::print(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) +void Font::print(const std::vector &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - std::vector vertices; - std::vector drawcommands = generateVertices(text, vertices); + ColoredCodepoints codepoints; + getCodepointsFromString(text, codepoints); - Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); + std::vector vertices; + std::vector drawcommands = generateVertices(codepoints, vertices); + + Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky); printv(t, drawcommands, vertices); } -void Font::printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky) +void Font::printf(const std::vector &text, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - std::vector vertices; - std::vector drawcommands = generateVerticesFormatted(text, wrap, align, vertices); + ColoredCodepoints codepoints; + getCodepointsFromString(text, codepoints); - Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); + std::vector vertices; + std::vector drawcommands = generateVerticesFormatted(codepoints, wrap, align, vertices); + + Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky); printv(t, drawcommands, vertices); } @@ -636,15 +705,16 @@ int Font::getWidth(const std::string &str) while (getline(iss, line, '\n')) { int width = 0; + uint32 prevglyph = 0; try { - uint32 prevglyph = 0; - utf8::iterator i(line.begin(), line.begin(), line.end()); utf8::iterator end(line.end(), line.begin(), line.end()); + while (i != end) { uint32 c = *i++; + const Glyph &g = findGlyph(c); width += g.spacing + getKerning(prevglyph, c); @@ -668,69 +738,180 @@ int Font::getWidth(char character) return g.spacing; } -void Font::getWrap(const std::string &text, float wrap, std::vector &lines, std::vector *linewidths, std::vector *wrappedlines) +void Font::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector &lines, std::vector *linewidths) { - const float width_space = (float) getWidth(' '); + // Per-line info. + float width = 0.0f; + float widthbeforelastspace = 0.0f; + float widthoftrailingspace = 0.0f; + uint32 prevglyph = 0; - std::istringstream iss(text); - std::string line; - std::ostringstream string_builder; + int lastspaceindex = -1; - // Split text at newlines. - while (std::getline(iss, line, '\n')) + // Keeping the indexed colors "in sync" is a bit tricky, since we split + // things up and we might skip some glyphs but we don't want to skip any + // color which starts at those indices. + Color curcolor(255, 255, 255, 255); + bool addcurcolor = false; + int curcolori = -1; + int endcolori = (int) codepoints.colors.size() - 1; + + // A wrapped line of text. + ColoredCodepoints wline; + + int i = 0; + while (i < (int) codepoints.cps.size()) { - std::vector words; - std::istringstream word_iss(line); + uint32 c = codepoints.cps[i]; - // split line into words - std::copy(std::istream_iterator(word_iss), std::istream_iterator(), std::back_inserter(words)); - - float width = 0.0f; - float oldwidth = 0.0f; - string_builder.str(""); - - // Put words back together until a wrap occurs. - for (const std::string &word : words) + // Determine the current color before doing anything else, to make sure + // it's still applied to future glyphs even if this one is skipped. + if (curcolori < endcolori && codepoints.colors[curcolori + 1].index == i) { - float wordwidth = (float) getWidth(word); - width += wordwidth; - - // On wordwrap, push line to line buffer and clear string builder. - if (width > wrap && oldwidth > 0) - { - int realw = (int) width; - - // Remove trailing space. - std::string tmp = string_builder.str(); - lines.push_back(tmp.substr(0,tmp.size()-1)); - string_builder.str(""); - width = wordwidth; - realw -= (int) width; - - if (linewidths) - linewidths->push_back(realw); - - // Indicate that this line was automatically wrapped. - if (wrappedlines) - wrappedlines->push_back(true); - } - - string_builder << word << " "; - - width += width_space; - oldwidth = width; + curcolor = codepoints.colors[curcolori + 1].color; + curcolori++; + addcurcolor = true; } - // Push last line. + // Split text at newlines. + if (c == '\n') + { + lines.push_back(wline); + + // Ignore the width of any trailing spaces, for individual lines. + if (linewidths) + linewidths->push_back(width - widthoftrailingspace); + + // Make sure the new line keeps any color that was set previously. + addcurcolor = true; + + width = widthbeforelastspace = widthoftrailingspace = 0.0f; + prevglyph = 0; // Reset kerning information. + lastspaceindex = -1; + wline.cps.clear(); + wline.colors.clear(); + i++; + + continue; + } + + const Glyph &g = findGlyph(c); + float charwidth = g.spacing + getKerning(prevglyph, c); + float newwidth = width + charwidth; + + // Wrap the line if it exceeds the wrap limit. Don't wrap yet if we're + // processing a newline character, though. + if (c != ' ' && newwidth > wraplimit) + { + // If this is the first character in the line and it exceeds the + // limit, skip it completely. + if (wline.cps.empty()) + i++; + else if (lastspaceindex != -1) + { + // 'Rewind' to the last seen space, if the line has one. + // FIXME: This could be more efficient... + while (!wline.cps.empty() && wline.cps.back() != ' ') + wline.cps.pop_back(); + + while (!wline.colors.empty() && wline.colors.back().index >= (int) wline.cps.size()) + wline.colors.pop_back(); + + // Also 'rewind' to the color that the last character is using. + for (int colori = curcolori; colori >= 0; colori--) + { + if (codepoints.colors[colori].index <= lastspaceindex) + { + curcolor = codepoints.colors[colori].color; + curcolori = colori; + break; + } + } + + // Ignore the width of trailing spaces in wrapped lines. + width = widthbeforelastspace; + + i = lastspaceindex; + i++; // Start the next line after the space. + } + + lines.push_back(wline); + + if (linewidths) + linewidths->push_back(width); + + addcurcolor = true; + + prevglyph = 0; + width = widthbeforelastspace = widthoftrailingspace = 0.0f; + wline.cps.clear(); + wline.colors.clear(); + lastspaceindex = -1; + + continue; + } + + if (prevglyph != ' ' && c == ' ') + widthbeforelastspace = width; + + width = newwidth; + prevglyph = c; + + if (addcurcolor) + { + wline.colors.push_back({curcolor, (int) wline.cps.size()}); + addcurcolor = false; + } + + wline.cps.push_back(c); + + // Keep track of the last seen space, so we can "rewind" to it when + // wrapping. + if (c == ' ') + { + lastspaceindex = i; + widthoftrailingspace += charwidth; + } + else if (c != '\n') + widthoftrailingspace = 0.0f; + + i++; + } + + // Push the last line. + if (!wline.cps.empty()) + { + lines.push_back(wline); + + // Ignore the width of any trailing spaces, for individual lines. if (linewidths) - linewidths->push_back(width); + linewidths->push_back(width - widthoftrailingspace); + } +} - std::string tmp = string_builder.str(); - lines.push_back(tmp.substr(0,tmp.size()-1)); +void Font::getWrap(const std::string &text, float wraplimit, std::vector &lines, std::vector *linewidths) +{ + ColoredCodepoints codepoints; + getCodepointsFromString(text, codepoints.cps); - // Indicate that this line was not automatically wrapped. - if (wrappedlines) - wrappedlines->push_back(false); + std::vector codepointlines; + getWrap(codepoints, wraplimit, codepointlines, linewidths); + + std::string line; + + for (const ColoredCodepoints &codepoints : codepointlines) + { + line.clear(); + line.reserve(codepoints.cps.size()); + + for (uint32 codepoint : codepoints.cps) + { + char character[5] = {'\0'}; + char *end = utf8::unchecked::append(codepoint, character); + line.append(character, end - character); + } + + lines.push_back(line); } } @@ -870,10 +1051,10 @@ bool Font::getConstant(AlignMode in, const char *&out) StringMap::Entry Font::alignModeEntries[] = { - { "left", Font::ALIGN_LEFT }, - { "right", Font::ALIGN_RIGHT }, - { "center", Font::ALIGN_CENTER }, - { "justify", Font::ALIGN_JUSTIFY }, + { "left", ALIGN_LEFT }, + { "right", ALIGN_RIGHT }, + { "center", ALIGN_CENTER }, + { "justify", ALIGN_JUSTIFY }, }; StringMap Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries)); diff --git a/jni/love/src/modules/graphics/opengl/Font.h b/jni/love/src/modules/graphics/opengl/Font.h index cf1dbca2..6ed474b0 100644 --- a/jni/love/src/modules/graphics/opengl/Font.h +++ b/jni/love/src/modules/graphics/opengl/Font.h @@ -50,6 +50,8 @@ class Font : public Object, public Volatile { public: + typedef std::vector Codepoints; + enum AlignMode { ALIGN_LEFT, @@ -59,10 +61,29 @@ public: ALIGN_MAX_ENUM }; + struct ColoredString + { + std::string str; + Color color; + }; + + struct IndexedColor + { + Color color; + int index; + }; + + struct ColoredCodepoints + { + std::vector cps; + std::vector colors; + }; + struct GlyphVertex { - float x, y; - float s, t; + float x, y; + uint16 s, t; + Color color; }; struct TextInfo @@ -77,27 +98,22 @@ public: GLuint texture; int startvertex; int vertexcount; - - // used when sorting with std::sort. - bool operator < (const DrawCommand &other) const - { - // Texture binds are expensive, so we should sort by that first. - if (texture != other.texture) - return texture < other.texture; - else - return startvertex < other.startvertex; - } }; Font(love::font::Rasterizer *r, const Texture::Filter &filter = Texture::getDefaultFilter()); virtual ~Font(); + std::vector generateVertices(const ColoredCodepoints &codepoints, std::vector &vertices, float extra_spacing = 0.0f, Vector offset = {}, TextInfo *info = nullptr); std::vector generateVertices(const std::string &text, std::vector &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr); - std::vector generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector &vertices, TextInfo *info = nullptr); + + std::vector generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector &vertices, TextInfo *info = nullptr); void drawVertices(const std::vector &drawcommands); + static void getCodepointsFromString(const std::string &str, Codepoints &codepoints); + static void getCodepointsFromString(const std::vector &strs, ColoredCodepoints &codepoints); + /** * Prints the text at the designated position with rotation and scaling. * @@ -112,9 +128,9 @@ public: * @param kx Shear along the x axis. * @param ky Shear along the y axis. **/ - void print(const std::string &text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f); + void print(const std::vector &text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f); - void printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f); + void printf(const std::vector &text, float x, float y, float wrap, AlignMode align, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f); /** * Returns the height of the font. @@ -140,13 +156,12 @@ public: * and optionally the number of lines * * @param text The input text - * @param wrap The number of pixels to wrap at + * @param wraplimit The number of pixels to wrap at * @param max_width Optional output of the maximum width - * @param wrapped_lines Optional output indicating which lines were - * auto-wrapped. Indices correspond to indices of the returned value. * Returns a vector with the lines. **/ - void getWrap(const std::string &text, float wrap, std::vector &lines, std::vector *line_widths = 0, std::vector *wrapped_lines = 0); + void getWrap(const std::string &text, float wraplimit, std::vector &lines, std::vector *line_widths = nullptr); + void getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector &lines, std::vector *line_widths = nullptr); /** * Sets the line height (which should be a number to multiply the font size by, diff --git a/jni/love/src/modules/graphics/opengl/GLBuffer.cpp b/jni/love/src/modules/graphics/opengl/GLBuffer.cpp index 416edee1..2a778924 100644 --- a/jni/love/src/modules/graphics/opengl/GLBuffer.cpp +++ b/jni/love/src/modules/graphics/opengl/GLBuffer.cpp @@ -90,6 +90,9 @@ void *GLBuffer::map() void GLBuffer::unmapStatic(size_t offset, size_t size) { + if (size == 0) + return; + // Upload the mapped data to the buffer. glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset); } @@ -163,12 +166,10 @@ void GLBuffer::setMappedRangeModified(size_t offset, size_t modifiedsize) // from the start of section a to the end of section b as modified if both // a and b are marked as modified. - size_t old_range_end = modified_offset + modified_offset; - + size_t old_range_end = modified_offset + modified_size; modified_offset = std::min(modified_offset, offset); size_t new_range_end = std::max(offset + modifiedsize, old_range_end); - modified_size = new_range_end - modified_offset; } diff --git a/jni/love/src/modules/graphics/opengl/Graphics.cpp b/jni/love/src/modules/graphics/opengl/Graphics.cpp index 0b830c78..6215082a 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/Graphics.cpp @@ -86,6 +86,11 @@ Graphics::~Graphics() Shader::defaultShader->release(); Shader::defaultShader = nullptr; } + if (Shader::defaultVideoShader) + { + Shader::defaultVideoShader->release(); + Shader::defaultVideoShader = nullptr; + } if (quadIndices) delete quadIndices; @@ -110,7 +115,7 @@ void Graphics::restoreState(const DisplayState &s) setPointSize(s.pointSize); if (s.scissor) - setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h); + setScissor(s.scissorRect.x, s.scissorRect.y, s.scissorRect.w, s.scissorRect.h); else setScissor(); @@ -131,11 +136,10 @@ void Graphics::restoreStateChecked(const DisplayState &s) { const DisplayState &cur = states.back(); - if (*(uint32 *) &s.color.r != *(uint32 *) &cur.color.r) + if (s.color != cur.color) setColor(s.color); - if (*(uint32 *) &s.backgroundColor.r != *(uint32 *) &cur.backgroundColor.r) - setBackgroundColor(s.backgroundColor); + setBackgroundColor(s.backgroundColor); if (s.blendMode != cur.blendMode || s.blendMultiplyAlpha != cur.blendMultiplyAlpha) setBlendMode(s.blendMode, s.blendMultiplyAlpha); @@ -148,10 +152,10 @@ void Graphics::restoreStateChecked(const DisplayState &s) if (s.pointSize != cur.pointSize) setPointSize(s.pointSize); - if (s.scissor != cur.scissor || (s.scissor && !(s.scissorBox == cur.scissorBox))) + if (s.scissor != cur.scissor || (s.scissor && !(s.scissorRect == cur.scissorRect))) { if (s.scissor) - setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h); + setScissor(s.scissorRect.x, s.scissorRect.y, s.scissorRect.w, s.scissorRect.h); else setScissor(); } @@ -295,6 +299,10 @@ bool Graphics::setMode(int width, int height) setDebug(enabledebug); + // Reload all volatile objects. + if (!Volatile::loadAll()) + ::printf("Could not reload all volatile objects.\n"); + // Create a quad indices object owned by love.graphics, so at least one // QuadIndices object is alive at all times while love.graphics is alive. // This makes sure there aren't too many expensive destruction/creations of @@ -303,10 +311,6 @@ bool Graphics::setMode(int width, int height) if (quadIndices == nullptr) quadIndices = new QuadIndices(20); - // Reload all volatile objects. - if (!Volatile::loadAll()) - ::printf("Could not reload all volatile objects.\n"); - // Restore the graphics state. restoreState(states.back()); @@ -321,6 +325,13 @@ bool Graphics::setMode(int width, int height) Shader::defaultShader = newShader(Shader::defaultCode[renderer]); } + // and a default video shader. + if (!Shader::defaultVideoShader) + { + Renderer renderer = GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL; + Shader::defaultVideoShader = newShader(Shader::defaultVideoCode[renderer]); + } + // A shader should always be active, but the default shader shouldn't be // returned by getShader(), so we don't do setShader(defaultShader). if (!Shader::current) @@ -581,14 +592,35 @@ bool Graphics::isCreated() const void Graphics::setScissor(int x, int y, int width, int height) { - OpenGL::Viewport box = {x, y, width, height}; + ScissorRect rect = {x, y, width, height}; glEnable(GL_SCISSOR_TEST); // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor. - gl.setScissor(box); + gl.setScissor({rect.x, rect.y, rect.w, rect.h}); states.back().scissor = true; - states.back().scissorBox = box; + states.back().scissorRect = rect; +} + +void Graphics::intersectScissor(int x, int y, int width, int height) +{ + ScissorRect rect = states.back().scissorRect; + + if (!states.back().scissor) + { + rect.x = 0; + rect.y = 0; + rect.w = std::numeric_limits::max(); + rect.h = std::numeric_limits::max(); + } + + int x1 = std::max(rect.x, x); + int y1 = std::max(rect.y, y); + + int x2 = std::min(rect.x + rect.w, x + width); + int y2 = std::min(rect.y + rect.h, y + height); + + setScissor(x1, y1, std::max(0, x2 - x1), std::max(0, y2 - y1)); } void Graphics::setScissor() @@ -601,10 +633,10 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const { const DisplayState &state = states.back(); - x = state.scissorBox.x; - y = state.scissorBox.y; - width = state.scissorBox.w; - height = state.scissorBox.h; + x = state.scissorRect.x; + y = state.scissorRect.y; + width = state.scissorRect.w; + height = state.scissorRect.h; return state.scissor; } @@ -688,7 +720,7 @@ Image *Graphics::newImage(const std::vector return new Image(cdata, flags); } -Quad *Graphics::newQuad(Quad::Viewport v, float sw, float sh) +Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh) { return new Quad(v, sw, sh); } @@ -794,11 +826,16 @@ Mesh *Graphics::newMesh(const std::vector &vertexformat, con return new Mesh(vertexformat, data, datasize, drawmode, usage); } -Text *Graphics::newText(Font *font, const std::string &text) +Text *Graphics::newText(Font *font, const std::vector &text) { return new Text(font, text); } +Video *Graphics::newVideo(love::video::VideoStream *stream) +{ + return new Video(stream); +} + bool Graphics::isGammaCorrect() const { return love::graphics::isGammaCorrect(); @@ -1085,7 +1122,7 @@ bool Graphics::isWireframe() const return states.back().wireframe; } -void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky) +void Graphics::print(const std::vector &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky) { checkSetDefaultFont(); @@ -1095,7 +1132,7 @@ void Graphics::print(const std::string &str, float x, float y , float angle, flo state.font->print(str, x, y, angle, sx, sy, ox, oy, kx, ky); } -void Graphics::printf(const std::string &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky) +void Graphics::printf(const std::vector &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { checkSetDefaultFont(); @@ -1278,21 +1315,8 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, coords[2 * (i+1) + 1] = y + radius * sinf(phi); } - // GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here - if (mode == DRAW_LINE) - { - polyline(coords, num_coords); // Artifacts at sharp angles if set to looping. - } - else - { - OpenGL::TempDebugGroup debuggroup("Filled arc draw"); - - gl.prepareDraw(); - gl.bindTexture(gl.getDefaultTexture()); - gl.useVertexAttribArrays(ATTRIBFLAG_POS); - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords); - gl.drawArrays(GL_TRIANGLE_FAN, 0, points + 2); - } + // NOTE: We rely on polygon() using GL_TRIANGLE_FAN, when fill mode is used. + polygon(mode, coords, num_coords); delete[] coords; } @@ -1482,10 +1506,10 @@ bool Graphics::isSupported(Support feature) const { switch (feature) { - case SUPPORT_MULTI_CANVAS: - return Canvas::isMultiCanvasSupported(); case SUPPORT_MULTI_CANVAS_FORMATS: return Canvas::isMultiFormatMultiCanvasSupported(); + case SUPPORT_CLAMP_ZERO: + return gl.isClampZeroTextureWrapSupported(); default: return false; } diff --git a/jni/love/src/modules/graphics/opengl/Graphics.h b/jni/love/src/modules/graphics/opengl/Graphics.h index cda95242..8bf2d982 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.h +++ b/jni/love/src/modules/graphics/opengl/Graphics.h @@ -37,6 +37,8 @@ #include "window/Window.h" +#include "video/VideoStream.h" + #include "Font.h" #include "Image.h" #include "graphics/Quad.h" @@ -47,6 +49,7 @@ #include "Shader.h" #include "Mesh.h" #include "Text.h" +#include "Video.h" namespace love { @@ -126,6 +129,8 @@ public: **/ void setScissor(int x, int y, int width, int height); + void intersectScissor(int x, int y, int width, int height); + /** * Clears any scissor that has been created. **/ @@ -160,7 +165,7 @@ public: Image *newImage(const std::vector &data, const Image::Flags &flags); Image *newImage(const std::vector &cdata, const Image::Flags &flags); - Quad *newQuad(Quad::Viewport v, float sw, float sh); + Quad *newQuad(Quad::Viewport v, double sw, double sh); /** * Creates a Font object. @@ -181,7 +186,9 @@ public: Mesh *newMesh(const std::vector &vertexformat, int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage); Mesh *newMesh(const std::vector &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, Mesh::Usage usage); - Text *newText(Font *font, const std::string &text = ""); + Text *newText(Font *font, const std::vector &text = {}); + + Video *newVideo(love::video::VideoStream *stream); bool isGammaCorrect() const; @@ -326,7 +333,7 @@ public: * @param kx Shear along the x-axis. * @param ky Shear along the y-axis. **/ - void print(const std::string &str, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); + void print(const std::vector &str, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * Draw formatted text on screen at the specified coordinates. @@ -344,7 +351,7 @@ public: * @param kx Shear along the x-axis. * @param ky Shear along the y-axis. **/ - void printf(const std::string &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky); + void printf(const std::vector &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * Draws a point at (x,y). @@ -478,7 +485,7 @@ private: float pointSize = 1.0f; bool scissor = false; - OpenGL::Viewport scissorBox = OpenGL::Viewport(); + ScissorRect scissorRect = ScissorRect(); // Stencil. bool stencilTest = false; diff --git a/jni/love/src/modules/graphics/opengl/Image.cpp b/jni/love/src/modules/graphics/opengl/Image.cpp index 9503d12e..b21fa355 100644 --- a/jni/love/src/modules/graphics/opengl/Image.cpp +++ b/jni/love/src/modules/graphics/opengl/Image.cpp @@ -356,7 +356,7 @@ bool Image::loadVolatile() GLenum glerr = glGetError(); if (glerr != GL_NO_ERROR) - throw love::Exception("Cannot create image (error code 0x%x)", glerr); + throw love::Exception("Cannot create image (OpenGL error: %s)", OpenGL::errorString(glerr)); } catch (love::Exception &) { @@ -531,8 +531,16 @@ bool Image::setWrap(const Texture::Wrap &w) wrap.s = wrap.t = WRAP_CLAMP; } + if (!gl.isClampZeroTextureWrapSupported()) + { + if (wrap.s == WRAP_CLAMP_ZERO) + wrap.s = WRAP_CLAMP; + if (wrap.t == WRAP_CLAMP_ZERO) + wrap.t = WRAP_CLAMP; + } + gl.bindTexture(texture); - gl.setTextureWrap(w); + gl.setTextureWrap(wrap); return success; } @@ -589,112 +597,101 @@ bool Image::isCompressed() const GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const { + using image::CompressedImageData; + switch (cformat) { - case image::CompressedImageData::FORMAT_DXT1: - if (isSRGB) - return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; - else - return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - case image::CompressedImageData::FORMAT_DXT3: - if (isSRGB) - return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; - else - return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case image::CompressedImageData::FORMAT_DXT5: - if (isSRGB) - return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; - else - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case image::CompressedImageData::FORMAT_BC4: + case CompressedImageData::FORMAT_DXT1: + return isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case CompressedImageData::FORMAT_DXT3: + return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case CompressedImageData::FORMAT_DXT5: + return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case CompressedImageData::FORMAT_BC4: isSRGB = false; return GL_COMPRESSED_RED_RGTC1; - case image::CompressedImageData::FORMAT_BC4s: + case CompressedImageData::FORMAT_BC4s: isSRGB = false; return GL_COMPRESSED_SIGNED_RED_RGTC1; - case image::CompressedImageData::FORMAT_BC5: + case CompressedImageData::FORMAT_BC5: isSRGB = false; return GL_COMPRESSED_RG_RGTC2; - case image::CompressedImageData::FORMAT_BC5s: + case CompressedImageData::FORMAT_BC5s: isSRGB = false; return GL_COMPRESSED_SIGNED_RG_RGTC2; - case image::CompressedImageData::FORMAT_BC6H: + case CompressedImageData::FORMAT_BC6H: isSRGB = false; return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; - case image::CompressedImageData::FORMAT_BC6Hs: + case CompressedImageData::FORMAT_BC6Hs: isSRGB = false; return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; - case image::CompressedImageData::FORMAT_BC7: - if (isSRGB) - return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; - else - return GL_COMPRESSED_RGBA_BPTC_UNORM; - case image::CompressedImageData::FORMAT_ETC1: + case CompressedImageData::FORMAT_BC7: + return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM : GL_COMPRESSED_RGBA_BPTC_UNORM; + case CompressedImageData::FORMAT_PVR1_RGB2: + return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case CompressedImageData::FORMAT_PVR1_RGB4: + return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case CompressedImageData::FORMAT_PVR1_RGBA2: + return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case CompressedImageData::FORMAT_PVR1_RGBA4: + return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case CompressedImageData::FORMAT_ETC1: // The ETC2 format can load ETC1 textures. if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility) - { - if (isSRGB) - return GL_COMPRESSED_SRGB8_ETC2; - else - return GL_COMPRESSED_RGB8_ETC2; - } + return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2; else { isSRGB = false; return GL_ETC1_RGB8_OES; } - case image::CompressedImageData::FORMAT_ETC2_RGB: - if (isSRGB) - return GL_COMPRESSED_SRGB8_ETC2; - else - return GL_COMPRESSED_RGB8_ETC2; - case image::CompressedImageData::FORMAT_ETC2_RGBA: - if (isSRGB) - return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; - else - return GL_COMPRESSED_RGBA8_ETC2_EAC; - case image::CompressedImageData::FORMAT_ETC2_RGBA1: - if (isSRGB) - return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; - else - return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; - case image::CompressedImageData::FORMAT_EAC_R: + case CompressedImageData::FORMAT_ETC2_RGB: + return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2; + case CompressedImageData::FORMAT_ETC2_RGBA: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC; + case CompressedImageData::FORMAT_ETC2_RGBA1: + return isSRGB ? GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case CompressedImageData::FORMAT_EAC_R: isSRGB = false; return GL_COMPRESSED_R11_EAC; - case image::CompressedImageData::FORMAT_EAC_Rs: + case CompressedImageData::FORMAT_EAC_Rs: isSRGB = false; return GL_COMPRESSED_SIGNED_R11_EAC; - case image::CompressedImageData::FORMAT_EAC_RG: + case CompressedImageData::FORMAT_EAC_RG: isSRGB = false; return GL_COMPRESSED_RG11_EAC; - case image::CompressedImageData::FORMAT_EAC_RGs: + case CompressedImageData::FORMAT_EAC_RGs: isSRGB = false; return GL_COMPRESSED_SIGNED_RG11_EAC; - case image::CompressedImageData::FORMAT_PVR1_RGB2: - if (isSRGB) - return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT; - else - return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - case image::CompressedImageData::FORMAT_PVR1_RGB4: - if (isSRGB) - return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT; - else - return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - case image::CompressedImageData::FORMAT_PVR1_RGBA2: - if (isSRGB) - return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT; - else - return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - case image::CompressedImageData::FORMAT_PVR1_RGBA4: - if (isSRGB) - return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT; - else - return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case CompressedImageData::FORMAT_ASTC_4x4: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case CompressedImageData::FORMAT_ASTC_5x4: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : GL_COMPRESSED_RGBA_ASTC_5x4_KHR; + case CompressedImageData::FORMAT_ASTC_5x5: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : GL_COMPRESSED_RGBA_ASTC_5x5_KHR; + case CompressedImageData::FORMAT_ASTC_6x5: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : GL_COMPRESSED_RGBA_ASTC_6x5_KHR; + case CompressedImageData::FORMAT_ASTC_6x6: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : GL_COMPRESSED_RGBA_ASTC_6x6_KHR; + case CompressedImageData::FORMAT_ASTC_8x5: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : GL_COMPRESSED_RGBA_ASTC_8x5_KHR; + case CompressedImageData::FORMAT_ASTC_8x6: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : GL_COMPRESSED_RGBA_ASTC_8x6_KHR; + case CompressedImageData::FORMAT_ASTC_8x8: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : GL_COMPRESSED_RGBA_ASTC_8x8_KHR; + case CompressedImageData::FORMAT_ASTC_10x5: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : GL_COMPRESSED_RGBA_ASTC_10x5_KHR; + case CompressedImageData::FORMAT_ASTC_10x6: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : GL_COMPRESSED_RGBA_ASTC_10x6_KHR; + case CompressedImageData::FORMAT_ASTC_10x8: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : GL_COMPRESSED_RGBA_ASTC_10x8_KHR; + case CompressedImageData::FORMAT_ASTC_10x10: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : GL_COMPRESSED_RGBA_ASTC_10x10_KHR; + case CompressedImageData::FORMAT_ASTC_12x10: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : GL_COMPRESSED_RGBA_ASTC_12x10_KHR; + case CompressedImageData::FORMAT_ASTC_12x12: + return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : GL_COMPRESSED_RGBA_ASTC_12x12_KHR; default: - if (isSRGB) - return GL_SRGB8_ALPHA8; - else - return GL_RGBA8; + return isSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8; } } @@ -705,42 +702,56 @@ bool Image::hasAnisotropicFilteringSupport() bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB) { + using image::CompressedImageData; + switch (format) { - case image::CompressedImageData::FORMAT_DXT1: + case CompressedImageData::FORMAT_DXT1: return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1; - case image::CompressedImageData::FORMAT_DXT3: + case CompressedImageData::FORMAT_DXT3: return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt3; - case image::CompressedImageData::FORMAT_DXT5: + case CompressedImageData::FORMAT_DXT5: return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5; - case image::CompressedImageData::FORMAT_BC4: - case image::CompressedImageData::FORMAT_BC4s: - case image::CompressedImageData::FORMAT_BC5: - case image::CompressedImageData::FORMAT_BC5s: + case CompressedImageData::FORMAT_BC4: + case CompressedImageData::FORMAT_BC4s: + case CompressedImageData::FORMAT_BC5: + case CompressedImageData::FORMAT_BC5s: return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc); - case image::CompressedImageData::FORMAT_BC6H: - case image::CompressedImageData::FORMAT_BC6Hs: - case image::CompressedImageData::FORMAT_BC7: + case CompressedImageData::FORMAT_BC6H: + case CompressedImageData::FORMAT_BC6Hs: + case CompressedImageData::FORMAT_BC7: return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc; - case image::CompressedImageData::FORMAT_ETC1: + case CompressedImageData::FORMAT_PVR1_RGB2: + case CompressedImageData::FORMAT_PVR1_RGB4: + case CompressedImageData::FORMAT_PVR1_RGBA2: + case CompressedImageData::FORMAT_PVR1_RGBA4: + return sRGB ? GLAD_EXT_pvrtc_sRGB : GLAD_IMG_texture_compression_pvrtc; + case CompressedImageData::FORMAT_ETC1: // ETC2 support guarantees ETC1 support as well. return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture; - case image::CompressedImageData::FORMAT_ETC2_RGB: - case image::CompressedImageData::FORMAT_ETC2_RGBA: - case image::CompressedImageData::FORMAT_ETC2_RGBA1: - case image::CompressedImageData::FORMAT_EAC_R: - case image::CompressedImageData::FORMAT_EAC_Rs: - case image::CompressedImageData::FORMAT_EAC_RG: - case image::CompressedImageData::FORMAT_EAC_RGs: + case CompressedImageData::FORMAT_ETC2_RGB: + case CompressedImageData::FORMAT_ETC2_RGBA: + case CompressedImageData::FORMAT_ETC2_RGBA1: + case CompressedImageData::FORMAT_EAC_R: + case CompressedImageData::FORMAT_EAC_Rs: + case CompressedImageData::FORMAT_EAC_RG: + case CompressedImageData::FORMAT_EAC_RGs: return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility; - case image::CompressedImageData::FORMAT_PVR1_RGB2: - case image::CompressedImageData::FORMAT_PVR1_RGB4: - case image::CompressedImageData::FORMAT_PVR1_RGBA2: - case image::CompressedImageData::FORMAT_PVR1_RGBA4: - if (sRGB) - return GLAD_EXT_pvrtc_sRGB; - else - return GLAD_IMG_texture_compression_pvrtc; + case CompressedImageData::FORMAT_ASTC_4x4: + case CompressedImageData::FORMAT_ASTC_5x4: + case CompressedImageData::FORMAT_ASTC_5x5: + case CompressedImageData::FORMAT_ASTC_6x5: + case CompressedImageData::FORMAT_ASTC_6x6: + case CompressedImageData::FORMAT_ASTC_8x5: + case CompressedImageData::FORMAT_ASTC_8x6: + case CompressedImageData::FORMAT_ASTC_8x8: + case CompressedImageData::FORMAT_ASTC_10x5: + case CompressedImageData::FORMAT_ASTC_10x6: + case CompressedImageData::FORMAT_ASTC_10x8: + case CompressedImageData::FORMAT_ASTC_10x10: + case CompressedImageData::FORMAT_ASTC_12x10: + case CompressedImageData::FORMAT_ASTC_12x12: + return /*GLAD_ES_VERSION_3_2 ||*/ GLAD_KHR_texture_compression_astc_ldr; default: return false; } @@ -763,8 +774,8 @@ bool Image::getConstant(FlagType in, const char *&out) StringMap::Entry Image::flagNameEntries[] = { - {"mipmaps", Image::FLAG_TYPE_MIPMAPS}, - {"linear", Image::FLAG_TYPE_LINEAR}, + {"mipmaps", FLAG_TYPE_MIPMAPS}, + {"linear", FLAG_TYPE_LINEAR}, }; StringMap Image::flagNames(Image::flagNameEntries, sizeof(Image::flagNameEntries)); diff --git a/jni/love/src/modules/graphics/opengl/Mesh.cpp b/jni/love/src/modules/graphics/opengl/Mesh.cpp index cc051134..8e37e63e 100644 --- a/jni/love/src/modules/graphics/opengl/Mesh.cpp +++ b/jni/love/src/modules/graphics/opengl/Mesh.cpp @@ -64,6 +64,7 @@ Mesh::Mesh(const std::vector &vertexformat, const void *data, size , vertexCount(0) , vertexStride(0) , ibo(nullptr) + , useIndexBuffer(false) , elementCount(0) , elementDataType(0) , drawMode(drawmode) @@ -90,6 +91,7 @@ Mesh::Mesh(const std::vector &vertexformat, int vertexcount, DrawM , vertexCount((size_t) vertexcount) , vertexStride(0) , ibo(nullptr) + , useIndexBuffer(false) , elementCount(0) , elementDataType(getGLDataTypeFromMax(vertexcount)) , drawMode(drawmode) @@ -147,7 +149,7 @@ void Mesh::setupAttachedAttributes() if (attachedAttributes.find(name) != attachedAttributes.end()) throw love::Exception("Duplicate vertex attribute name: %s", name.c_str()); - attachedAttributes[name] = {this, i, true}; + attachedAttributes[name] = {this, (int) i, true}; } } @@ -285,6 +287,17 @@ Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const return type; } +int Mesh::getAttributeIndex(const std::string &name) const +{ + for (int i = 0; i < (int) vertexFormat.size(); i++) + { + if (vertexFormat[i].name == name) + return i; + } + + return -1; +} + void Mesh::setAttributeEnabled(const std::string &name, bool enable) { auto it = attachedAttributes.find(name); @@ -326,20 +339,10 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh) oldattrib = it->second; newattrib.mesh = mesh; - newattrib.index = std::numeric_limits::max(); newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true; + newattrib.index = mesh->getAttributeIndex(name); - // Find the index of the attribute in the mesh. - for (size_t i = 0; i < mesh->vertexFormat.size(); i++) - { - if (mesh->vertexFormat[i].name == name) - { - newattrib.index = i; - break; - } - } - - if (newattrib.index == std::numeric_limits::max()) + if (newattrib.index < 0) throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str()); if (newattrib.mesh != this) @@ -357,11 +360,10 @@ void *Mesh::mapVertexData() return vbo->map(); } -void Mesh::unmapVertexData() +void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize) { - // Assume the whole buffer was modified. GLBuffer::Bind bind(*vbo); - vbo->setMappedRangeModified(0, vbo->getSize()); + vbo->setMappedRangeModified(modifiedoffset, modifiedsize); vbo->unmap(); } @@ -414,6 +416,7 @@ void Mesh::setVertexMap(const std::vector &map) if (!ibo && size > 0) ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage()); + useIndexBuffer = true; elementCount = map.size(); if (!ibo || elementCount == 0) @@ -437,6 +440,11 @@ void Mesh::setVertexMap(const std::vector &map) elementDataType = datatype; } +void Mesh::setVertexMap() +{ + useIndexBuffer = false; +} + /** * Copies index data from a mapped buffer to a vector. **/ @@ -448,14 +456,17 @@ static void copyFromIndexBuffer(void *buffer, size_t count, std::vector indices.push_back((uint32) elems[i]); } -void Mesh::getVertexMap(std::vector &map) const +bool Mesh::getVertexMap(std::vector &map) const { - if (!ibo || elementCount == 0) - return; + if (!useIndexBuffer) + return false; map.clear(); map.reserve(elementCount); + if (!ibo || elementCount == 0) + return true; + GLBuffer::Bind ibobind(*ibo); // We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush. @@ -472,6 +483,8 @@ void Mesh::getVertexMap(std::vector &map) const copyFromIndexBuffer(buffer, elementCount, map); break; } + + return true; } size_t Mesh::getVertexMapCount() const @@ -524,6 +537,39 @@ void Mesh::getDrawRange(int &min, int &max) const max = rangeMax; } +int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname) +{ + const AttribFormat &format = vertexFormat[attributeindex]; + + GLint attriblocation = -1; + + // If the attribute is one of the LOVE-defined ones, use the constant + // attribute index for it, otherwise query the index from the shader. + VertexAttribID builtinattrib; + if (Shader::getConstant(inputname.c_str(), builtinattrib)) + attriblocation = (GLint) builtinattrib; + else if (Shader::current) + attriblocation = Shader::current->getAttribLocation(inputname); + + // The active shader might not use this vertex attribute name. + if (attriblocation < 0) + return attriblocation; + + // Needed for unmap and glVertexAttribPointer. + GLBuffer::Bind vbobind(*vbo); + + // Make sure the buffer isn't mapped (sends data to GPU if needed.) + vbo->unmap(); + + const void *gloffset = vbo->getPointer(getAttributeOffset(attributeindex)); + GLenum datatype = getGLDataType(format.type); + GLboolean normalized = (datatype == GL_UNSIGNED_BYTE); + + glVertexAttribPointer(attriblocation, format.components, datatype, normalized, vertexStride, gloffset); + + return attriblocation; +} + void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { OpenGL::TempDebugGroup debuggroup("Mesh draw"); @@ -536,43 +582,15 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo continue; Mesh *mesh = attrib.second.mesh; - const AttribFormat &format = mesh->vertexFormat[attrib.second.index]; + int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first); - GLint attriblocation = -1; - - // If the attribute is one of the LOVE-defined ones, use the constant - // attribute index for it, otherwise query the index from the shader. - VertexAttribID builtinattrib; - if (Shader::getConstant(format.name.c_str(), builtinattrib)) - attriblocation = (GLint) builtinattrib; - else if (Shader::current) - attriblocation = Shader::current->getAttribLocation(format.name); - - // The active shader might not use this vertex attribute name. - if (attriblocation < 0) - continue; - - // Needed for unmap and glVertexAttribPointer. - GLBuffer::Bind vbobind(*mesh->vbo); - - // Make sure the buffer isn't mapped (sends data to GPU if needed.) - mesh->vbo->unmap(); - - size_t offset = mesh->getAttributeOffset(attrib.second.index); - const void *gloffset = mesh->vbo->getPointer(offset); - GLenum datatype = getGLDataType(format.type); - GLboolean normalized = (datatype == GL_UNSIGNED_BYTE); - - glVertexAttribPointer(attriblocation, format.components, datatype, normalized, mesh->vertexStride, gloffset); - - enabledattribs |= 1 << uint32(attriblocation); + if (location >= 0) + enabledattribs |= 1u << (uint32) location; } + // Not supported on all platforms or GL versions, I believe. if (!(enabledattribs & ATTRIBFLAG_POS)) - { - // Not supported on all platforms or GL versions at least, I believe. throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn."); - } gl.useVertexAttribArrays(enabledattribs); @@ -588,7 +606,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo gl.prepareDraw(); - if (ibo && elementCount > 0) + if (useIndexBuffer && ibo && elementCount > 0) { // Use the custom vertex map (index buffer) to draw the vertices. GLBuffer::Bind ibo_bind(*ibo); diff --git a/jni/love/src/modules/graphics/opengl/Mesh.h b/jni/love/src/modules/graphics/opengl/Mesh.h index 5482dec2..4c7897fe 100644 --- a/jni/love/src/modules/graphics/opengl/Mesh.h +++ b/jni/love/src/modules/graphics/opengl/Mesh.h @@ -126,6 +126,7 @@ public: **/ const std::vector &getVertexFormat() const; DataType getAttributeInfo(int attribindex, int &components) const; + int getAttributeIndex(const std::string &name) const; /** * Sets whether a specific vertex attribute is used when drawing the Mesh. @@ -140,7 +141,7 @@ public: void attachAttribute(const std::string &name, Mesh *mesh); void *mapVertexData(); - void unmapVertexData(); + void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1); /** * Flushes all modified data to the GPU. @@ -154,12 +155,13 @@ public: * {0, 1, 2, 3, 4, ...} **/ void setVertexMap(const std::vector &map); + void setVertexMap(); /** * Fills the uint32 vector passed into the method with the previously set * vertex map (index buffer) values. **/ - void getVertexMap(std::vector &map) const; + bool getVertexMap(std::vector &map) const; /** * Gets the total number of elements in the vertex map array. @@ -192,6 +194,8 @@ public: void setDrawRange(); void getDrawRange(int &min, int &max) const; + int bindAttributeToShaderInput(int attributeindex, const std::string &inputname); + // Implements Drawable. void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override; @@ -211,7 +215,7 @@ private: struct AttachedAttribute { Mesh *mesh; - size_t index; + int index; bool enabled; }; @@ -242,6 +246,7 @@ private: // Element (vertex index) buffer, for the vertex map. GLBuffer *ibo; + bool useIndexBuffer; size_t elementCount; GLenum elementDataType; diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.cpp b/jni/love/src/modules/graphics/opengl/OpenGL.cpp index df1092a0..19d9b286 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.cpp +++ b/jni/love/src/modules/graphics/opengl/OpenGL.cpp @@ -32,6 +32,7 @@ // C #include +#include // For SDL_GL_GetProcAddress. #include @@ -601,24 +602,32 @@ void OpenGL::setTextureFilter(graphics::Texture::Filter &f) f.anisotropy = 1.0f; } +GLint OpenGL::getGLWrapMode(Texture::WrapMode wmode) +{ + switch (wmode) + { + case Texture::WRAP_CLAMP: + default: + return GL_CLAMP_TO_EDGE; + case Texture::WRAP_CLAMP_ZERO: + return GL_CLAMP_TO_BORDER; + case Texture::WRAP_REPEAT: + return GL_REPEAT; + case Texture::WRAP_MIRRORED_REPEAT: + return GL_MIRRORED_REPEAT; + } + +} + void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w) { - auto glWrapMode = [](Texture::WrapMode wmode) -> GLint - { - switch (wmode) - { - case Texture::WRAP_CLAMP: - default: - return GL_CLAMP_TO_EDGE; - case Texture::WRAP_REPEAT: - return GL_REPEAT; - case Texture::WRAP_MIRRORED_REPEAT: - return GL_MIRRORED_REPEAT; - } - }; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGLWrapMode(w.s)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGLWrapMode(w.t)); +} - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapMode(w.s)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapMode(w.t)); +bool OpenGL::isClampZeroTextureWrapSupported() const +{ + return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp; } int OpenGL::getMaxTextureSize() const @@ -643,7 +652,7 @@ int OpenGL::getMaxTextureUnits() const void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize) { - int64 memsize = (int64) stats.textureMemory + ((int64 )newsize - (int64) oldsize); + int64 memsize = (int64) stats.textureMemory + ((int64) newsize - (int64) oldsize); stats.textureMemory = (size_t) std::max(memsize, (int64) 0); } @@ -652,6 +661,36 @@ OpenGL::Vendor OpenGL::getVendor() const return vendor; } +const char *OpenGL::errorString(GLenum errorcode) +{ + switch (errorcode) + { + case GL_NO_ERROR: + return "no error"; + case GL_INVALID_ENUM: + return "invalid enum"; + case GL_INVALID_VALUE: + return "invalid value"; + case GL_INVALID_OPERATION: + return "invalid operation"; + case GL_OUT_OF_MEMORY: + return "out of memory"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "invalid framebuffer operation"; + case GL_CONTEXT_LOST: + return "OpenGL context has been lost"; + default: + break; + } + + static char text[64] = {}; + + memset(text, 0, sizeof(text)); + sprintf(text, "0x%x", errorcode); + + return text; +} + const char *OpenGL::debugSeverityString(GLenum severity) { switch (severity) diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.h b/jni/love/src/modules/graphics/opengl/OpenGL.h index 2d87f452..33b6c0b1 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.h +++ b/jni/love/src/modules/graphics/opengl/OpenGL.h @@ -143,7 +143,7 @@ public: { public: -#if defined(DEBUG) && DEBUG == 1 +#if defined(LOVE_IOS) TempDebugGroup(const char *name) { if (GLAD_EXT_debug_marker) @@ -153,7 +153,7 @@ public: TempDebugGroup(const char *) {} #endif -#if defined(DEBUG) && DEBUG == 1 +#if defined(LOVE_IOS) ~TempDebugGroup() { if (GLAD_EXT_debug_marker) @@ -312,6 +312,8 @@ public: **/ void setTextureWrap(const graphics::Texture::Wrap &w); + bool isClampZeroTextureWrapSupported() const; + /** * Returns the maximum supported width or height of a texture. **/ @@ -339,6 +341,8 @@ public: **/ Vendor getVendor() const; + static const char *errorString(GLenum errorcode); + // Get human-readable strings for debug info. static const char *debugSeverityString(GLenum severity); static const char *debugSourceString(GLenum source); @@ -352,6 +356,8 @@ private: void initMatrices(); void createDefaultTexture(); + GLint getGLWrapMode(Texture::WrapMode wmode); + bool contextInitialized; float maxAnisotropy; diff --git a/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp b/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp index b08ee83b..83cb4969 100644 --- a/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp @@ -22,8 +22,6 @@ #include "common/config.h" #include "ParticleSystem.h" -#include "common/math.h" -#include "modules/math/RandomGenerator.h" #include "OpenGL.h" // STD @@ -38,123 +36,39 @@ namespace graphics namespace opengl { -namespace -{ - -love::math::RandomGenerator rng; - -float calculate_variation(float inner, float outer, float var) -{ - float low = inner - (outer/2.0f)*var; - float high = inner + (outer/2.0f)*var; - float r = (float) rng.random(); - return low*(1-r)+high*r; -} - -} // anonymous namespace - ParticleSystem::ParticleSystem(Texture *texture, uint32 size) - : pMem(nullptr) - , pFree(nullptr) - , pHead(nullptr) - , pTail(nullptr) + : love::graphics::ParticleSystem(texture, size) , particleVerts(nullptr) - , quadIndices(1) - , texture(texture) - , active(true) - , insertMode(INSERT_MODE_TOP) - , maxParticles(0) - , activeParticles(0) - , emissionRate(0) - , emitCounter(0) - , areaSpreadDistribution(DISTRIBUTION_NONE) - , lifetime(-1) - , life(0) - , particleLifeMin(0) - , particleLifeMax(0) - , direction(0) - , spread(0) - , speedMin(0) - , speedMax(0) - , linearAccelerationMin(0, 0) - , linearAccelerationMax(0, 0) - , radialAccelerationMin(0) - , radialAccelerationMax(0) - , tangentialAccelerationMin(0) - , tangentialAccelerationMax(0) - , linearDampingMin(0.0f) - , linearDampingMax(0.0f) - , sizeVariation(0) - , rotationMin(0) - , rotationMax(0) - , spinStart(0) - , spinEnd(0) - , spinVariation(0) - , offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f) - , defaultOffset(true) - , relativeRotation(false) + , quadIndices(size) { - if (size == 0 || size > MAX_PARTICLES) - throw love::Exception("Invalid ParticleSystem size."); - - sizes.push_back(1.0f); - colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); - setBufferSize(size); + createVertices(size); } ParticleSystem::ParticleSystem(const ParticleSystem &p) - : pMem(nullptr) - , pFree(nullptr) - , pHead(nullptr) - , pTail(nullptr) + : love::graphics::ParticleSystem(p) , particleVerts(nullptr) , quadIndices(p.quadIndices) - , texture(p.texture) - , active(p.active) - , insertMode(p.insertMode) - , maxParticles(p.maxParticles) - , activeParticles(0) - , emissionRate(p.emissionRate) - , emitCounter(0.0f) - , position(p.position) - , prevPosition(p.prevPosition) - , areaSpreadDistribution(p.areaSpreadDistribution) - , areaSpread(p.areaSpread) - , lifetime(p.lifetime) - , life(p.lifetime) // Initialize with the maximum life time. - , particleLifeMin(p.particleLifeMin) - , particleLifeMax(p.particleLifeMax) - , direction(p.direction) - , spread(p.spread) - , speedMin(p.speedMin) - , speedMax(p.speedMax) - , linearAccelerationMin(p.linearAccelerationMin) - , linearAccelerationMax(p.linearAccelerationMax) - , radialAccelerationMin(p.radialAccelerationMin) - , radialAccelerationMax(p.radialAccelerationMax) - , tangentialAccelerationMin(p.tangentialAccelerationMin) - , tangentialAccelerationMax(p.tangentialAccelerationMax) - , linearDampingMin(p.linearDampingMin) - , linearDampingMax(p.linearDampingMax) - , sizes(p.sizes) - , sizeVariation(p.sizeVariation) - , rotationMin(p.rotationMin) - , rotationMax(p.rotationMax) - , spinStart(p.spinStart) - , spinEnd(p.spinEnd) - , spinVariation(p.spinVariation) - , offset(p.offset) - , defaultOffset(p.defaultOffset) - , colors(p.colors) - , quads(p.quads) - , relativeRotation(p.relativeRotation) { - setBufferSize(maxParticles); + createVertices(maxParticles); } ParticleSystem::~ParticleSystem() { - deleteBuffers(); + delete[] particleVerts; +} + +void ParticleSystem::createVertices(size_t numparticles) +{ + try + { + love::Vertex *pverts = new love::Vertex[numparticles * 4]; + delete[] particleVerts; + particleVerts = pverts; + } + catch (std::exception &) + { + throw love::Exception("Out of memory."); + } } ParticleSystem *ParticleSystem::clone() @@ -162,688 +76,18 @@ ParticleSystem *ParticleSystem::clone() return new ParticleSystem(*this); } -void ParticleSystem::resetOffset() -{ - if (quads.empty()) - offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f); - else - { - Quad::Viewport v = quads[0]->getViewport(); - offset = love::Vector(v.x*0.5f, v.y*0.5f); - } -} - -void ParticleSystem::createBuffers(size_t size) -{ - try - { - pFree = pMem = new Particle[size]; - particleVerts = new love::Vertex[size * 4]; - maxParticles = (uint32) size; - } - catch (std::bad_alloc &) - { - deleteBuffers(); - throw love::Exception("Out of memory"); - } -} - -void ParticleSystem::deleteBuffers() -{ - // Clean up for great gracefulness! - delete[] pMem; - delete[] particleVerts; - - pMem = nullptr; - particleVerts = nullptr; - maxParticles = 0; - activeParticles = 0; -} - void ParticleSystem::setBufferSize(uint32 size) { - if (size == 0 || size > MAX_PARTICLES) - throw love::Exception("Invalid buffer size"); + love::graphics::ParticleSystem::setBufferSize(size); + quadIndices = QuadIndices(size); - deleteBuffers(); - createBuffers(size); - reset(); -} - -uint32 ParticleSystem::getBufferSize() const -{ - return maxParticles; -} - -void ParticleSystem::addParticle(float t) -{ - if (isFull()) - return; - - // Gets a free particle and updates the allocation pointer. - Particle *p = pFree++; - initParticle(p, t); - - switch (insertMode) - { - default: - case INSERT_MODE_TOP: - insertTop(p); - break; - case INSERT_MODE_BOTTOM: - insertBottom(p); - break; - case INSERT_MODE_RANDOM: - insertRandom(p); - break; - } - - activeParticles++; -} - -void ParticleSystem::initParticle(Particle *p, float t) -{ - float min,max; - - // Linearly interpolate between the previous and current emitter position. - love::Vector pos = prevPosition + (position - prevPosition) * t; - - min = particleLifeMin; - max = particleLifeMax; - if (min == max) - p->life = min; - else - p->life = (float) rng.random(min, max); - p->lifetime = p->life; - - p->position = pos; - - switch (areaSpreadDistribution) - { - case DISTRIBUTION_UNIFORM: - p->position.x += (float) rng.random(-areaSpread.getX(), areaSpread.getX()); - p->position.y += (float) rng.random(-areaSpread.getY(), areaSpread.getY()); - break; - case DISTRIBUTION_NORMAL: - p->position.x += (float) rng.randomNormal(areaSpread.getX()); - p->position.y += (float) rng.randomNormal(areaSpread.getY()); - break; - case DISTRIBUTION_NONE: - default: - break; - } - - p->origin = pos; - - min = speedMin; - max = speedMax; - float speed = (float) rng.random(min, max); - - min = direction - spread/2.0f; - max = direction + spread/2.0f; - float dir = (float) rng.random(min, max); - - p->velocity = love::Vector(cosf(dir), sinf(dir)) * speed; - - p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x); - p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y); - - min = radialAccelerationMin; - max = radialAccelerationMax; - p->radialAcceleration = (float) rng.random(min, max); - - min = tangentialAccelerationMin; - max = tangentialAccelerationMax; - p->tangentialAcceleration = (float) rng.random(min, max); - - min = linearDampingMin; - max = linearDampingMax; - p->linearDamping = (float) rng.random(min, max); - - p->sizeOffset = (float) rng.random(sizeVariation); // time offset for size change - p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset; - p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)]; - - min = rotationMin; - max = rotationMax; - p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation); - p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation); - p->rotation = (float) rng.random(min, max); - - p->angle = p->rotation; - if (relativeRotation) - p->angle += atan2f(p->velocity.y, p->velocity.x); - - p->color = colors[0]; - - p->quadIndex = 0; -} - -void ParticleSystem::insertTop(Particle *p) -{ - if (pHead == nullptr) - { - pHead = p; - p->prev = nullptr; - } - else - { - pTail->next = p; - p->prev = pTail; - } - p->next = nullptr; - pTail = p; -} - -void ParticleSystem::insertBottom(Particle *p) -{ - if (pTail == nullptr) - { - pTail = p; - p->next = nullptr; - } - else - { - pHead->prev = p; - p->next = pHead; - } - p->prev = nullptr; - pHead = p; -} - -void ParticleSystem::insertRandom(Particle *p) -{ - // Nonuniform, but 64-bit is so large nobody will notice. Hopefully. - uint64 pos = rng.rand() % ((int64) activeParticles + 1); - - // Special case where the particle gets inserted before the head. - if (pos == activeParticles) - { - Particle *pA = pHead; - if (pA) - pA->prev = p; - p->prev = nullptr; - p->next = pA; - pHead = p; - return; - } - - // Inserts the particle after the randomly selected particle. - Particle *pA = pMem + pos; - Particle *pB = pA->next; - pA->next = p; - if (pB) - pB->prev = p; - else - pTail = p; - p->prev = pA; - p->next = pB; -} - -ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p) -{ - // The linked list is updated in this function and old pointers may be - // invalidated. The returned pointer will inform the caller of the new - // pointer to the next particle. - Particle *pNext = nullptr; - - // Removes the particle from the linked list. - if (p->prev) - p->prev->next = p->next; - else - pHead = p->next; - - if (p->next) - { - p->next->prev = p->prev; - pNext = p->next; - } - else - pTail = p->prev; - - // The (in memory) last particle can now be moved into the free slot. - // It will skip the moving if it happens to be the removed particle. - pFree--; - if (p != pFree) - { - *p = *pFree; - if (pNext == pFree) - pNext = p; - - if (p->prev) - p->prev->next = p; - else - pHead = p; - - if (p->next) - p->next->prev = p; - else - pTail = p; - } - - activeParticles--; - return pNext; -} - -void ParticleSystem::setTexture(Texture *tex) -{ - texture.set(tex); - - if (defaultOffset) - resetOffset(); -} - -Texture *ParticleSystem::getTexture() const -{ - return texture.get(); -} - -void ParticleSystem::setInsertMode(InsertMode mode) -{ - insertMode = mode; -} - -ParticleSystem::InsertMode ParticleSystem::getInsertMode() const -{ - return insertMode; -} - -void ParticleSystem::setEmissionRate(float rate) -{ - if (rate < 0.0f) - throw love::Exception("Invalid emission rate"); - emissionRate = rate; -} - -float ParticleSystem::getEmissionRate() const -{ - return emissionRate; -} - -void ParticleSystem::setEmitterLifetime(float life) -{ - this->life = lifetime = life; -} - -float ParticleSystem::getEmitterLifetime() const -{ - return lifetime; -} - -void ParticleSystem::setParticleLifetime(float min, float max) -{ - particleLifeMin = min; - if (max == 0) - particleLifeMax = min; - else - particleLifeMax = max; -} - -void ParticleSystem::getParticleLifetime(float &min, float &max) const -{ - min = particleLifeMin; - max = particleLifeMax; -} - -void ParticleSystem::setPosition(float x, float y) -{ - position = love::Vector(x, y); - prevPosition = position; -} - -const love::Vector &ParticleSystem::getPosition() const -{ - return position; -} - -void ParticleSystem::moveTo(float x, float y) -{ - position = love::Vector(x, y); -} - -void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y) -{ - areaSpread = love::Vector(x, y); - areaSpreadDistribution = distribution; -} - -ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const -{ - return areaSpreadDistribution; -} - -const love::Vector &ParticleSystem::getAreaSpreadParameters() const -{ - return areaSpread; -} - -void ParticleSystem::setDirection(float direction) -{ - this->direction = direction; -} - -float ParticleSystem::getDirection() const -{ - return direction; -} - -void ParticleSystem::setSpread(float spread) -{ - this->spread = spread; -} - -float ParticleSystem::getSpread() const -{ - return spread; -} - -void ParticleSystem::setSpeed(float speed) -{ - speedMin = speedMax = speed; -} - -void ParticleSystem::setSpeed(float min, float max) -{ - speedMin = min; - speedMax = max; -} - -void ParticleSystem::getSpeed(float &min, float &max) const -{ - min = speedMin; - max = speedMax; -} - -void ParticleSystem::setLinearAcceleration(float x, float y) -{ - linearAccelerationMin.x = linearAccelerationMax.x = x; - linearAccelerationMin.y = linearAccelerationMax.y = y; -} - -void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax) -{ - linearAccelerationMin = love::Vector(xmin, ymin); - linearAccelerationMax = love::Vector(xmax, ymax); -} - -void ParticleSystem::getLinearAcceleration(love::Vector &min, love::Vector &max) const -{ - min = linearAccelerationMin; - max = linearAccelerationMax; -} - -void ParticleSystem::setRadialAcceleration(float acceleration) -{ - radialAccelerationMin = radialAccelerationMax = acceleration; -} - -void ParticleSystem::setRadialAcceleration(float min, float max) -{ - radialAccelerationMin = min; - radialAccelerationMax = max; -} - -void ParticleSystem::getRadialAcceleration(float &min, float &max) const -{ - min = radialAccelerationMin; - max = radialAccelerationMax; -} - -void ParticleSystem::setTangentialAcceleration(float acceleration) -{ - tangentialAccelerationMin = tangentialAccelerationMax = acceleration; -} - -void ParticleSystem::setTangentialAcceleration(float min, float max) -{ - tangentialAccelerationMin = min; - tangentialAccelerationMax = max; -} - -void ParticleSystem::getTangentialAcceleration(float &min, float &max) const -{ - min = tangentialAccelerationMin; - max = tangentialAccelerationMax; -} - -void ParticleSystem::setLinearDamping(float min, float max) -{ - linearDampingMin = min; - linearDampingMax = max; -} - -void ParticleSystem::getLinearDamping(float &min, float &max) const -{ - min = linearDampingMin; - max = linearDampingMax; -} - -void ParticleSystem::setSize(float size) -{ - sizes.resize(1); - sizes[0] = size; -} - -void ParticleSystem::setSizes(const std::vector &newSizes) -{ - sizes = newSizes; -} - -const std::vector &ParticleSystem::getSizes() const -{ - return sizes; -} - -void ParticleSystem::setSizeVariation(float variation) -{ - sizeVariation = variation; -} - -float ParticleSystem::getSizeVariation() const -{ - return sizeVariation; -} - -void ParticleSystem::setRotation(float rotation) -{ - rotationMin = rotationMax = rotation; -} - -void ParticleSystem::setRotation(float min, float max) -{ - rotationMin = min; - rotationMax = max; -} - -void ParticleSystem::getRotation(float &min, float &max) const -{ - min = rotationMin; - max = rotationMax; -} - -void ParticleSystem::setSpin(float spin) -{ - spinStart = spin; - spinEnd = spin; -} - -void ParticleSystem::setSpin(float start, float end) -{ - spinStart = start; - spinEnd = end; -} - -void ParticleSystem::getSpin(float &start, float &end) const -{ - start = spinStart; - end = spinEnd; -} - -void ParticleSystem::setSpinVariation(float variation) -{ - spinVariation = variation; -} - -float ParticleSystem::getSpinVariation() const -{ - return spinVariation; -} - -void ParticleSystem::setOffset(float x, float y) -{ - offset = love::Vector(x, y); - defaultOffset = false; -} - -love::Vector ParticleSystem::getOffset() const -{ - return offset; -} - -void ParticleSystem::setColor(const std::vector &newColors) -{ - colors = newColors; - - for (Colorf &c : colors) - { - // We want to store the colors as [0, 1], rather than [0, 255]. - c.r /= 255.0f; - c.g /= 255.0f; - c.b /= 255.0f; - c.a /= 255.0f; - } -} - -std::vector ParticleSystem::getColor() const -{ - // The particle system stores colors in the range of [0, 1]... - std::vector ncolors(colors); - - for (Colorf &c : ncolors) - { - c.r *= 255.0f; - c.g *= 255.0f; - c.b *= 255.0f; - c.a *= 255.0f; - } - - return ncolors; -} - -void ParticleSystem::setQuads(const std::vector &newQuads) -{ - std::vector> quadlist; - quadlist.reserve(newQuads.size()); - - for (Quad *q : newQuads) - quadlist.push_back(q); - - quads = quadlist; - - if (defaultOffset) - resetOffset(); -} - -void ParticleSystem::setQuads() -{ - quads.clear(); -} - -std::vector ParticleSystem::getQuads() const -{ - std::vector quadlist; - quadlist.reserve(quads.size()); - - for (const StrongRef &q : quads) - quadlist.push_back(q.get()); - - return quadlist; -} - -void ParticleSystem::setRelativeRotation(bool enable) -{ - relativeRotation = enable; -} - -bool ParticleSystem::hasRelativeRotation() const -{ - return relativeRotation; -} - -uint32 ParticleSystem::getCount() const -{ - return activeParticles; -} - -void ParticleSystem::start() -{ - active = true; -} - -void ParticleSystem::stop() -{ - active = false; - life = lifetime; - emitCounter = 0; -} - -void ParticleSystem::pause() -{ - active = false; -} - -void ParticleSystem::reset() -{ - if (pMem == nullptr) - return; - - pFree = pMem; - pHead = nullptr; - pTail = nullptr; - activeParticles = 0; - life = lifetime; - emitCounter = 0; -} - -void ParticleSystem::emit(uint32 num) -{ - if (!active) - return; - - num = std::min(num, maxParticles - activeParticles); - - while(num--) - addParticle(1.0f); -} - -bool ParticleSystem::isActive() const -{ - return active; -} - -bool ParticleSystem::isPaused() const -{ - return !active && life < lifetime; -} - -bool ParticleSystem::isStopped() const -{ - return !active && life >= lifetime; -} - -bool ParticleSystem::isEmpty() const -{ - return activeParticles == 0; -} - -bool ParticleSystem::isFull() const -{ - return activeParticles == maxParticles; + createVertices(size); } void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { uint32 pCount = getCount(); + if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr) return; @@ -904,163 +148,6 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo } } -void ParticleSystem::update(float dt) -{ - if (pMem == nullptr || dt == 0.0f) - return; - - // Traverse all particles and update. - Particle *p = pHead; - - while (p) - { - // Decrease lifespan. - p->life -= dt; - - if (p->life <= 0) - p = removeParticle(p); - else - { - // Temp variables. - love::Vector radial, tangential; - love::Vector ppos = p->position; - - // Get vector from particle center to particle. - radial = ppos - p->origin; - radial.normalize(); - tangential = radial; - - // Resize radial acceleration. - radial *= p->radialAcceleration; - - // Calculate tangential acceleration. - { - float a = tangential.getX(); - tangential.setX(-tangential.getY()); - tangential.setY(a); - } - - // Resize tangential. - tangential *= p->tangentialAcceleration; - - // Update velocity. - p->velocity += (radial + tangential + p->linearAcceleration) * dt; - - // Apply damping. - p->velocity *= 1.0f / (1.0f + p->linearDamping * dt); - - // Modify position. - ppos += p->velocity * dt; - - p->position = ppos; - - const float t = 1.0f - p->life / p->lifetime; - - // Rotate. - p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt; - - p->angle = p->rotation; - - if (relativeRotation) - p->angle += atan2f(p->velocity.y, p->velocity.x); - - // Change size according to given intervals: - // i = 0 1 2 3 n-1 - // |-------|-------|------|--- ... ---| - // t = 0 1/(n-1) 3/(n-1) 1 - // - // `s' is the interpolation variable scaled to the current - // interval width, e.g. if n = 5 and t = 0.3, then the current - // indices are 1,2 and s = 0.3 - 0.25 = 0.05 - float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation - s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size() - size_t i = (size_t)s; - size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f) - s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1 - p->size = sizes[i] * (1.0f - s) + sizes[k] * s; - - // Update color according to given intervals (as above) - s = t * (float)(colors.size() - 1); - i = (size_t)s; - k = (i == colors.size() - 1) ? i : i + 1; - s -= (float)i; // 0 <= s <= 1 - p->color = colors[i] * (1.0f - s) + colors[k] * s; - - // Update the quad index. - k = quads.size(); - if (k > 0) - { - s = t * (float) k; // [0:numquads-1] (clamped below) - i = (s > 0.0f) ? (size_t) s : 0; - p->quadIndex = (int) ((i < k) ? i : k - 1); - } - - // Next particle. - p = p->next; - } - } - - // Make some more particles. - if (active) - { - float rate = 1.0f / emissionRate; // the amount of time between each particle emit - emitCounter += dt; - float total = emitCounter - rate; - while (emitCounter > rate) - { - addParticle(1.0f - (emitCounter - rate) / total); - emitCounter -= rate; - } - /*int particles = (int)(emissionRate * dt); - for (int i = 0; i != particles; i++) - add();*/ - - life -= dt; - if (lifetime != -1 && life < 0) - stop(); - } - - prevPosition = position; -} - -bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out) -{ - return distributions.find(in, out); -} - -bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out) -{ - return distributions.find(in, out); -} - -bool ParticleSystem::getConstant(const char *in, InsertMode &out) -{ - return insertModes.find(in, out); -} - -bool ParticleSystem::getConstant(InsertMode in, const char *&out) -{ - return insertModes.find(in, out); -} - -StringMap::Entry ParticleSystem::distributionsEntries[] = -{ - { "none", ParticleSystem::DISTRIBUTION_NONE }, - { "uniform", ParticleSystem::DISTRIBUTION_UNIFORM }, - { "normal", ParticleSystem::DISTRIBUTION_NORMAL }, -}; - -StringMap ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries)); - -StringMap::Entry ParticleSystem::insertModesEntries[] = -{ - { "top", ParticleSystem::INSERT_MODE_TOP }, - { "bottom", ParticleSystem::INSERT_MODE_BOTTOM }, - { "random", ParticleSystem::INSERT_MODE_RANDOM }, -}; - -StringMap ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries)); - } // opengl } // graphics } // love diff --git a/jni/love/src/modules/graphics/opengl/ParticleSystem.h b/jni/love/src/modules/graphics/opengl/ParticleSystem.h index 8502f826..6641bdb1 100644 --- a/jni/love/src/modules/graphics/opengl/ParticleSystem.h +++ b/jni/love/src/modules/graphics/opengl/ParticleSystem.h @@ -22,18 +22,9 @@ #define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H // LOVE -#include "common/int.h" -#include "common/math.h" -#include "common/Vector.h" -#include "graphics/Drawable.h" -#include "graphics/Color.h" -#include "graphics/Quad.h" -#include "graphics/Texture.h" +#include "graphics/ParticleSystem.h" #include "GLBuffer.h" -// STL -#include - namespace love { namespace graphics @@ -41,643 +32,28 @@ namespace graphics namespace opengl { -/** - * A class for creating, moving and drawing particles. - * A big thanks to bobthebloke.org - **/ -class ParticleSystem : public Drawable +class ParticleSystem : public love::graphics::ParticleSystem { public: - /** - * Type of distribution new particles are drawn from: None, uniform, normal. - */ - enum AreaSpreadDistribution - { - DISTRIBUTION_NONE, - DISTRIBUTION_UNIFORM, - DISTRIBUTION_NORMAL, - DISTRIBUTION_MAX_ENUM - }; - /** - * Insertion modes of new particles in the list: top, bottom, random. - */ - enum InsertMode - { - INSERT_MODE_TOP, - INSERT_MODE_BOTTOM, - INSERT_MODE_RANDOM, - INSERT_MODE_MAX_ENUM - }; - - /** - * Maximum numbers of particles in a ParticleSystem. - * This limit comes from the fact that a quad requires four vertices and the - * OpenGL API where GLsizei is a signed int. - **/ - static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4; - - /** - * Creates a particle system with the specified buffer size and texture. - **/ ParticleSystem(Texture *texture, uint32 buffer); ParticleSystem(const ParticleSystem &p); - /** - * Deletes any allocated memory. - **/ virtual ~ParticleSystem(); - /** - * Creates an identical copy of this ParticleSystem. The clone does not - * duplicate any existing particles from this ParticleSystem, just the - * settable parameters. - **/ - ParticleSystem *clone(); + ParticleSystem *clone() override; + void setBufferSize(uint32 size) override; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override; - /** - * Sets the texture used in the particle system. - * @param texture The new texture. - **/ - void setTexture(Texture *texture); +private: - /** - * Returns the texture used when drawing the particle system. - **/ - Texture *getTexture() const; - - /** - * Clears the current buffer and allocates the appropriate amount of space for the buffer. - * @param size The new buffer size. - **/ - void setBufferSize(uint32 size); - - /** - * Returns the total amount of particles this ParticleSystem can have active - * at any given point in time. - **/ - uint32 getBufferSize() const; - - /** - * Sets the insert mode for new particles. - * @param mode The new insert mode. - */ - void setInsertMode(InsertMode mode); - - /** - * Returns the current insert mode. - */ - InsertMode getInsertMode() const; - - /** - * Sets the emission rate. - * @param rate The amount of particles per second. - **/ - void setEmissionRate(float rate); - - /** - * Returns the number of particles created per second. - **/ - float getEmissionRate() const; - - /** - * Sets the lifetime of the particle emitter (-1 means eternal) - * @param life The lifetime (in seconds). - **/ - void setEmitterLifetime(float life); - - /** - * Returns the lifetime of the particle emitter. - **/ - float getEmitterLifetime() const; - - /** - * Sets the life range of the particles. - * @param min The minimum life. - * @param max The maximum life (if 0, then becomes the same as minimum life). - **/ - void setParticleLifetime(float min, float max = 0); - - /** - * Gets the lifetime of a particle. - * @param[out] min The minimum life. - * @param[out] max The maximum life. - **/ - void getParticleLifetime(float &min, float &max) const; - - /** - * Sets the position of the center of the emitter. - * Used to move the emitter without changing the position of already existing particles. - * @param x The x-coordinate. - * @param y The y-coordinate. - **/ - void setPosition(float x, float y); - - /** - * Returns the position of the emitter. - **/ - const love::Vector &getPosition() const; - - /** - * Moves the position of the center of the emitter. - * When update is called, newly spawned particles will appear in a line - * between the old emitter position and where the emitter was moved to, - * resulting in a smoother-feeling particle system if moveTo is called - * repeatedly. - **/ - void moveTo(float x, float y); - - /** - * Sets the emission area spread parameters and distribution type. The interpretation of - * the parameters depends on the distribution type: - * - * * None: Parameters are ignored. No area spread. - * * Uniform: Parameters denote maximal (symmetric) displacement from emitter position. - * * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated. - * @param x First parameter. Interpretation depends on distribution type. - * @param y Second parameter. Interpretation depends on distribution type. - * @param distribution Distribution type - **/ - void setAreaSpread(AreaSpreadDistribution distribution, float x, float y); - - /** - * Returns area spread distribution type. - **/ - AreaSpreadDistribution getAreaSpreadDistribution() const; - - /** - * Returns area spread parameters. - **/ - const love::Vector &getAreaSpreadParameters() const; - - /** - * Sets the direction of the particle emitter. - * @param direction The direction (in degrees). - **/ - void setDirection(float direction); - - /** - * Returns the direction of the particle emitter (in radians). - **/ - float getDirection() const; - - /** - * Sets the spread of the particle emitter. - * @param spread The spread (in radians). - **/ - void setSpread(float spread); - - /** - * Returns the directional spread of the emitter (in radians). - **/ - float getSpread() const; - - /** - * Sets the speed of the particles. - * @param speed The speed. - **/ - void setSpeed(float speed); - - /** - * Sets the speed of the particles. - * @param min The minimum speed. - * @param max The maximum speed. - **/ - void setSpeed(float min, float max); - - /** - * Gets the speed of the particles. - * @param[out] min The minimum speed. - * @param[out] max The maximum speed. - **/ - void getSpeed(float &min, float &max) const; - - /** - * Sets the linear acceleration (the acceleration along the x and y axes). - * @param x The acceleration along the x-axis. - * @param y The acceleration along the y-axis. - **/ - void setLinearAcceleration(float x, float y); - - /** - * Sets the linear acceleration (the acceleration along the x and y axes). - * @param xmin The minimum amount of acceleration along the x-axis. - * @param ymin The minimum amount of acceleration along the y-axis. - * @param xmax The maximum amount of acceleration along the x-axis. - * @param ymax The maximum amount of acceleration along the y-axis. - **/ - void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax); - - /** - * Gets the linear acceleration of the particles. - * @param[out] min The minimum acceleration. - * @param[out] max The maximum acceleration. - **/ - void getLinearAcceleration(love::Vector &min, love::Vector &max) const; - - /** - * Sets the radial acceleration (the acceleration towards the particle emitter). - * @param acceleration The amount of acceleration. - **/ - void setRadialAcceleration(float acceleration); - - /** - * Sets the radial acceleration (the acceleration towards the particle emitter). - * @param min The minimum acceleration. - * @param max The maximum acceleration. - **/ - void setRadialAcceleration(float min, float max); - - /** - * Gets the radial acceleration. - * @param[out] min The minimum amount of radial acceleration. - * @param[out] max The maximum amount of radial acceleration. - **/ - void getRadialAcceleration(float &min, float &max) const; - - /** - * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction). - * @param acceleration The amount of acceleration. - **/ - void setTangentialAcceleration(float acceleration); - - /** - * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction). - * @param min The minimum acceleration. - * @param max The maximum acceleration. - **/ - void setTangentialAcceleration(float min, float max); - - /** - * Gets the tangential acceleration. - * @param[out] min The minimum tangential acceleration. - * @param[out] max The maximum tangential acceleration. - **/ - void getTangentialAcceleration(float &min, float &max) const; - - /** - * Sets the amount of linear damping. Damping reduces the velocity of - * particles over time. A value of 0 corresponds to no damping. - **/ - void setLinearDamping(float min, float max); - - /** - * Gets the current amount of linear damping. - **/ - void getLinearDamping(float &min, float &max) const; - - /** - * Sets the size of the sprite (1.0 being the default size). - * @param size The size of the sprite. - **/ - void setSize(float size); - - /** - * Sets the sizes of the sprite upon creation and upon death (1.0 being the default size). - * @param newSizes Array of sizes - **/ - void setSizes(const std::vector &newSizes); - - /** - * Returns the sizes of the particle sprites. - **/ - const std::vector &getSizes() const; - - /** - * Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end). - * @param variation The amount of variation. - **/ - void setSizeVariation(float variation); - - /** - * Returns the amount of initial size variation between particles. - **/ - float getSizeVariation() const; - - /** - * Sets the amount of rotation a sprite starts out with. - * @param rotation The amount of rotation. - **/ - void setRotation(float rotation); - - /** - * Sets the amount of rotation a sprite starts out with (a random value between min and max). - * @param min The minimum amount of rotation. - * @param max The maximum amount of rotation. - **/ - void setRotation(float min, float max); - - /** - * Gets the initial amount of rotation of a particle, in radians. - * @param[out] min The minimum initial rotation. - * @param[out] max The maximum initial rotation. - **/ - void getRotation(float &min, float &max) const; - - /** - * Sets the spin of the sprite. - * @param spin The spin of the sprite (in degrees). - **/ - void setSpin(float spin); - - /** - * Sets the spin of the sprite upon particle creation and death. - * @param start The spin of the sprite upon creation (in radians / second). - * @param end The spin of the sprite upon death (in radians / second). - **/ - void setSpin(float start, float end); - - /** - * Gets the amount of spin of a particle during its lifetime. - * @param[out] start The initial spin, in radians / s. - * @param[out] end The final spin, in radians / s. - **/ - void getSpin(float &start, float &end) const; - - /** - * Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end). - * @param variation The variation. - **/ - void setSpinVariation(float variation); - - /** - * Returns the amount of variation of the start spin of a particle. - **/ - float getSpinVariation() const; - - /** - * Sets the particles' offsets for rotation. - * @param x The x offset. - * @param y The y offset. - **/ - void setOffset(float x, float y); - - /** - * Returns of the particle offset. - **/ - love::Vector getOffset() const; - - /** - * Sets the color of the particles. - * @param newColors Array of colors - **/ - void setColor(const std::vector &newColors); - - /** - * Returns the color of the particles. - **/ - std::vector getColor() const; - - /** - * Sets a list of Quads to use for particles over their lifetime. - **/ - void setQuads(const std::vector &newQuads); - void setQuads(); - - /** - * Gets the Quads used when drawing the particles. - **/ - std::vector getQuads() const; - - /** - * sets whether particle angles & rotations are relative to their velocities. - **/ - void setRelativeRotation(bool enable); - bool hasRelativeRotation() const; - - /** - * Returns the amount of particles that are currently active in the system. - **/ - uint32 getCount() const; - - /** - * Starts/resumes the particle emitter. - **/ - void start(); - - /** - * Stops the particle emitter and resets. - **/ - void stop(); - - /** - * Pauses the particle emitter. - **/ - void pause(); - - /** - * Resets the particle emitter. - **/ - void reset(); - - /** - * Instantly emits a number of particles. - * @param num The number of particles to emit. - **/ - void emit(uint32 num); - - /** - * Returns whether the particle emitter is active. - **/ - bool isActive() const; - - /** - * Returns whether the particle emitter is paused. - **/ - bool isPaused() const; - - bool isStopped() const; - - /** - * Returns whether the particle system is empty of particles or not. - **/ - bool isEmpty() const; - - /** - * Returns whether the amount of particles has reached the buffer limit or not. - **/ - bool isFull() const; - - /** - * Draws the particle emitter at the specified position. - * @param x The x-coordinate. - * @param y The y-coordinate. - **/ - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); - - /** - * Updates the particle system. - * @param dt Time since last update. - **/ - void update(float dt); - - static bool getConstant(const char *in, AreaSpreadDistribution &out); - static bool getConstant(AreaSpreadDistribution in, const char *&out); - - static bool getConstant(const char *in, InsertMode &out); - static bool getConstant(InsertMode in, const char *&out); - -protected: - - // Represents a single particle. - struct Particle - { - Particle *prev; - Particle *next; - - float lifetime; - float life; - - love::Vector position; - - // Particles gravitate towards this point. - love::Vector origin; - - love::Vector velocity; - love::Vector linearAcceleration; - float radialAcceleration; - float tangentialAcceleration; - - float linearDamping; - - float size; - float sizeOffset; - float sizeIntervalSize; - - float rotation; // Amount of rotation applied to the final angle. - float angle; - float spinStart; - float spinEnd; - - Colorf color; - - int quadIndex; - }; - - // Pointer to the beginning of the allocated memory. - Particle *pMem; - - // Pointer to a free particle. - Particle *pFree; - - // Pointer to the start of the linked list. - Particle *pHead; - - // Pointer to the end of the linked list. - Particle *pTail; + void createVertices(size_t numparticles); // array of transformed vertex data for all particles, for drawing Vertex *particleVerts; // Vertex index buffer. QuadIndices quadIndices; - - // The texture to be drawn. - StrongRef texture; - - // Whether the particle emitter is active. - bool active; - - // Insert mode of new particles. - InsertMode insertMode; - - // The maximum number of particles. - uint32 maxParticles; - - // The number of active particles. - uint32 activeParticles; - - // The emission rate (particles/sec). - float emissionRate; - - // Used to determine when a particle should be emitted. - float emitCounter; - - // The relative position of the particle emitter. - love::Vector position; - love::Vector prevPosition; - - // Emission area spread. - AreaSpreadDistribution areaSpreadDistribution; - love::Vector areaSpread; - - // The lifetime of the particle emitter (-1 means infinite) and the life it has left. - float lifetime; - float life; - - // The particle life. - float particleLifeMin; - float particleLifeMax; - - // The direction (and spread) the particles will be emitted in. Measured in radians. - float direction; - float spread; - - // The speed. - float speedMin; - float speedMax; - - // Acceleration along the x and y axes. - love::Vector linearAccelerationMin; - love::Vector linearAccelerationMax; - - // Acceleration towards the emitter's center - float radialAccelerationMin; - float radialAccelerationMax; - - // Acceleration perpendicular to the particle's direction. - float tangentialAccelerationMin; - float tangentialAccelerationMax; - - float linearDampingMin; - float linearDampingMax; - - // Size. - std::vector sizes; - float sizeVariation; - - // Rotation - float rotationMin; - float rotationMax; - - // Spin. - float spinStart; - float spinEnd; - float spinVariation; - - // Offsets - love::Vector offset; - - // Is the ParticleSystem using a default offset? - bool defaultOffset; - - // Color. - std::vector colors; - - // Quads. - std::vector> quads; - - bool relativeRotation; - - void resetOffset(); - - void createBuffers(size_t size); - void deleteBuffers(); - - void addParticle(float t); - Particle *removeParticle(Particle *p); - - // Called by addParticle. - void initParticle(Particle *p, float t); - void insertTop(Particle *p); - void insertBottom(Particle *p); - void insertRandom(Particle *p); - - static StringMap::Entry distributionsEntries[]; - static StringMap distributions; - - static StringMap::Entry insertModesEntries[]; - static StringMap insertModes; }; } // opengl diff --git a/jni/love/src/modules/graphics/opengl/Shader.cpp b/jni/love/src/modules/graphics/opengl/Shader.cpp index 226eb37c..dee541dd 100644 --- a/jni/love/src/modules/graphics/opengl/Shader.cpp +++ b/jni/love/src/modules/graphics/opengl/Shader.cpp @@ -64,8 +64,10 @@ namespace Shader *Shader::current = nullptr; Shader *Shader::defaultShader = nullptr; +Shader *Shader::defaultVideoShader = nullptr; Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM]; +Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM]; std::vector Shader::textureCounters; @@ -77,6 +79,7 @@ Shader::Shader(const ShaderSource &source) , lastCanvas((Canvas *) -1) , lastViewport() , lastPointSize(0.0f) + , videoTextureUnits() { if (source.vertex.empty() && source.pixel.empty()) throw love::Exception("Cannot create shader: no source code!"); @@ -225,6 +228,9 @@ bool Shader::loadVolatile() lastProjectionMatrix.setTranslation(nan, nan); lastTransformMatrix.setTranslation(nan, nan); + for (int i = 0; i < 3; i++) + videoTextureUnits[i] = 0; + // zero out active texture list activeTexUnits.clear(); activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0); @@ -635,6 +641,57 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const return builtinAttributes[int(attrib)] != -1; } +void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture) +{ + TemporaryAttacher attacher(this); + + // Set up the texture units that will be used by the shader to sample from + // the textures, if they haven't been set up yet. + if (videoTextureUnits[0] == 0) + { + const GLint locs[3] = { + builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL], + builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL], + builtinUniforms[BUILTIN_VIDEO_CR_CHANNEL] + }; + + const char *names[3] = {nullptr, nullptr, nullptr}; + builtinNames.find(BUILTIN_VIDEO_Y_CHANNEL, names[0]); + builtinNames.find(BUILTIN_VIDEO_CB_CHANNEL, names[1]); + builtinNames.find(BUILTIN_VIDEO_CR_CHANNEL, names[2]); + + for (int i = 0; i < 3; i++) + { + if (locs[i] >= 0 && names[i] != nullptr) + { + videoTextureUnits[i] = getTextureUnit(names[i]); + + // Increment global shader texture id counter for this texture + // unit, if we haven't already. + if (activeTexUnits[videoTextureUnits[i] - 1] == 0) + ++textureCounters[videoTextureUnits[i] - 1]; + + glUniform1i(locs[i], videoTextureUnits[i]); + } + } + } + + const GLuint textures[3] = {ytexture, cbtexture, crtexture}; + + // Bind the textures to their respective texture units. + for (int i = 0; i < 3; i++) + { + if (videoTextureUnits[i] != 0) + { + // Store texture id so it can be re-bound later. + activeTexUnits[videoTextureUnits[i] - 1] = textures[i]; + gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false); + } + } + + gl.setTextureUnit(0); +} + void Shader::checkSetScreenParams() { OpenGL::Viewport view = gl.getViewport(); @@ -898,6 +955,9 @@ StringMap::Entry Shader::built {"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX}, {"love_PointSize", Shader::BUILTIN_POINT_SIZE}, {"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE}, + {"love_VideoYChannel", Shader::BUILTIN_VIDEO_Y_CHANNEL}, + {"love_VideoCbChannel", Shader::BUILTIN_VIDEO_CB_CHANNEL}, + {"love_VideoCrChannel", Shader::BUILTIN_VIDEO_CR_CHANNEL}, }; StringMap Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries)); diff --git a/jni/love/src/modules/graphics/opengl/Shader.h b/jni/love/src/modules/graphics/opengl/Shader.h index 47e7d294..b69df7b6 100644 --- a/jni/love/src/modules/graphics/opengl/Shader.h +++ b/jni/love/src/modules/graphics/opengl/Shader.h @@ -64,6 +64,9 @@ public: BUILTIN_NORMAL_MATRIX, BUILTIN_POINT_SIZE, BUILTIN_SCREEN_SIZE, + BUILTIN_VIDEO_Y_CHANNEL, + BUILTIN_VIDEO_CB_CHANNEL, + BUILTIN_VIDEO_CR_CHANNEL, BUILTIN_MAX_ENUM }; @@ -89,9 +92,11 @@ public: // Pointer to the default Shader. static Shader *defaultShader; + static Shader *defaultVideoShader; // Default shader code (a shader is always required internally.) static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM]; + static ShaderSource defaultVideoCode[Graphics::RENDERER_MAX_ENUM]; /** * Creates a new Shader using a list of source codes. @@ -182,6 +187,7 @@ public: **/ bool hasVertexAttrib(VertexAttribID attrib) const; + void setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture); void checkSetScreenParams(); void checkSetPointSize(float size); void checkSetBuiltinUniforms(); @@ -263,6 +269,8 @@ private: Matrix4 lastTransformMatrix; Matrix4 lastProjectionMatrix; + GLuint videoTextureUnits[3]; + // Counts total number of textures bound to each texture unit in all shaders static std::vector textureCounters; diff --git a/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp b/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp index 6d2d12c3..2936274a 100644 --- a/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp @@ -53,23 +53,9 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, Mesh::Usage usage) throw love::Exception("Invalid SpriteBatch size."); GLenum gl_usage = Mesh::getGLBufferUsage(usage); + size_t vertex_size = sizeof(Vertex) * 4 * size; - const size_t vertex_size = sizeof(Vertex) * 4 * size; - - try - { - array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY); - } - catch (love::Exception &) - { - delete array_buf; - throw; - } - catch (std::bad_alloc &) - { - delete array_buf; - throw love::Exception("Out of memory."); - } + array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY); } SpriteBatch::~SpriteBatch() @@ -207,6 +193,28 @@ int SpriteBatch::getBufferSize() const return size; } +void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh) +{ + AttachedAttribute oldattrib = {}; + AttachedAttribute newattrib = {}; + + if (mesh->getVertexCount() < (size_t) getBufferSize() * 4) + throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", getBufferSize()*4); + + auto it = attached_attributes.find(name); + if (it != attached_attributes.end()) + oldattrib = it->second; + + newattrib.index = mesh->getAttributeIndex(name); + + if (newattrib.index < 0) + throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str()); + + newattrib.mesh = mesh; + + attached_attributes[name] = newattrib; +} + void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { const size_t pos_offset = offsetof(Vertex, x); @@ -223,27 +231,47 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float gl.bindTexture(*(GLuint *) texture->getHandle()); - GLBuffer::Bind array_bind(*array_buf); - GLBuffer::Bind element_bind(*quad_indices.getBuffer()); - - // Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.) - array_buf->unmap(); - uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD; - // Apply per-sprite color, if a color is set. - if (color) { - enabledattribs |= ATTRIBFLAG_COLOR; - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset)); + // Scope this bind so it doesn't interfere with the + // Mesh::bindAttributeToShaderInput calls below. + GLBuffer::Bind array_bind(*array_buf); + + // Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.) + array_buf->unmap(); + + // Apply per-sprite color, if a color is set. + if (color) + { + enabledattribs |= ATTRIBFLAG_COLOR; + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset)); + } + + glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset)); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset)); } - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset)); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset)); + for (const auto &it : attached_attributes) + { + Mesh *mesh = it.second.mesh.get(); + + // We have to do this check here as well because setBufferSize can be + // called after attachAttribute. + if (mesh->getVertexCount() < (size_t) getBufferSize() * 4) + throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str()); + + int location = mesh->bindAttributeToShaderInput(it.second.index, it.first); + + if (location >= 0) + enabledattribs |= 1u << (uint32) location; + } gl.useVertexAttribArrays(enabledattribs); gl.prepareDraw(); + + GLBuffer::Bind element_bind(*quad_indices.getBuffer()); gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0)); } diff --git a/jni/love/src/modules/graphics/opengl/SpriteBatch.h b/jni/love/src/modules/graphics/opengl/SpriteBatch.h index 0db524c5..f64adcff 100644 --- a/jni/love/src/modules/graphics/opengl/SpriteBatch.h +++ b/jni/love/src/modules/graphics/opengl/SpriteBatch.h @@ -24,6 +24,9 @@ // C #include +// C++ +#include + // LOVE #include "common/math.h" #include "common/Matrix.h" @@ -98,11 +101,23 @@ public: **/ int getBufferSize() const; + /** + * Attaches a specific vertex attribute from a Mesh to this SpriteBatch. + * The vertex attribute will be used when drawing the SpriteBatch. + **/ + void attachAttribute(const std::string &name, Mesh *mesh); + // Implements Drawable. void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); private: + struct AttachedAttribute + { + StrongRef mesh; + int index; + }; + void addv(const Vertex *v, const Matrix3 &m, int index); /** @@ -129,6 +144,8 @@ private: GLBuffer *array_buf; QuadIndices quad_indices; + std::unordered_map attached_attributes; + }; // SpriteBatch } // opengl diff --git a/jni/love/src/modules/graphics/opengl/Text.cpp b/jni/love/src/modules/graphics/opengl/Text.cpp index b87f4a38..06a0ac47 100644 --- a/jni/love/src/modules/graphics/opengl/Text.cpp +++ b/jni/love/src/modules/graphics/opengl/Text.cpp @@ -30,10 +30,9 @@ namespace graphics namespace opengl { -Text::Text(Font *font, const std::string &text) +Text::Text(Font *font, const std::vector &text) : font(font) , vbo(nullptr) - , text_info() , vert_offset(0) , texture_cache_id((uint32) -1) { @@ -82,7 +81,7 @@ void Text::uploadVertices(const std::vector &vertices, size_t vbo = new_vbo; } - if (vbo != nullptr) + if (vbo != nullptr && datasize > 0) { GLBuffer::Bind bind(*vbo); vbodata = (uint8 *) vbo->map(); @@ -113,11 +112,13 @@ void Text::addTextData(const TextData &t) std::vector vertices; std::vector new_commands; + Font::TextInfo text_info; + // We only have formatted text if the align mode is valid. if (t.align == Font::ALIGN_MAX_ENUM) - new_commands = font->generateVertices(t.text, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info); + new_commands = font->generateVertices(t.codepoints, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info); else - new_commands = font->generateVerticesFormatted(t.text, t.wrap, t.align, vertices, &text_info); + new_commands = font->generateVerticesFormatted(t.codepoints, t.wrap, t.align, vertices, &text_info); if (t.use_matrix) t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size()); @@ -158,27 +159,29 @@ void Text::addTextData(const TextData &t) } vert_offset = voffset + vertices.size(); + text_data.push_back(t); + text_data.back().text_info = text_info; // Font::generateVertices can invalidate the font's texture cache. if (font->getTextureCacheID() != texture_cache_id) regenerateVertices(); } -void Text::set(const std::string &text) +void Text::set(const std::vector &text) { - if (text.empty()) - return set(); - - addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, false, false, Matrix3()}); + return set(text, -1.0f, Font::ALIGN_MAX_ENUM); } -void Text::set(const std::string &text, float wrap, Font::AlignMode align) +void Text::set(const std::vector &text, float wrap, Font::AlignMode align) { - if (text.empty()) + if (text.empty() || (text.size() == 1 && text[0].str.empty())) return set(); - addTextData({text, wrap, align, false, false, Matrix3()}); + Font::ColoredCodepoints codepoints; + Font::getCodepointsFromString(text, codepoints); + + addTextData({codepoints, wrap, align, {}, false, false, Matrix3()}); } void Text::set() @@ -186,24 +189,21 @@ void Text::set() clear(); } -void Text::add(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) +int Text::add(const std::vector &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - if (text.empty()) - return; - - Matrix3 m(x, y, angle, sx, sy, ox, oy, kx, ky); - - addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, true, true, m}); + return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, x, y, angle, sx, sy, ox, oy, kx, ky); } -void Text::addf(const std::string &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) +int Text::addf(const std::vector &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - if (text.empty()) - return; + Font::ColoredCodepoints codepoints; + Font::getCodepointsFromString(text, codepoints); Matrix3 m(x, y, angle, sx, sy, ox, oy, kx, ky); - addTextData({text, wrap, align, true, true, m}); + addTextData({codepoints, wrap, align, {}, true, true, m}); + + return (int) text_data.size() - 1; } void Text::clear() @@ -211,7 +211,6 @@ void Text::clear() text_data.clear(); draw_commands.clear(); texture_cache_id = font->getTextureCacheID(); - text_info = {}; vert_offset = 0; } @@ -226,12 +225,13 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo if (font->getTextureCacheID() != texture_cache_id) regenerateVertices(); - const size_t pos_offset = offsetof(Font::GlyphVertex, x); - const size_t tex_offset = offsetof(Font::GlyphVertex, s); + const size_t pos_offset = offsetof(Font::GlyphVertex, x); + const size_t tex_offset = offsetof(Font::GlyphVertex, s); + const size_t color_offset = offsetof(Font::GlyphVertex, color.r); const size_t stride = sizeof(Font::GlyphVertex); OpenGL::TempTransform transform(gl); - transform.get() *= Matrix4(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); + transform.get() *= Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky); { GLBuffer::Bind bind(*vbo); @@ -239,10 +239,11 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo // Font::drawVertices expects AttribPointer calls to be done already. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset)); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(tex_offset)); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset)); + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset)); } - gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD); + gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR); font->drawVertices(draw_commands); } @@ -262,14 +263,26 @@ Font *Text::getFont() const return font.get(); } -int Text::getWidth() const +int Text::getWidth(int index) const { - return text_info.width; + if (index < 0) + index = std::max((int) text_data.size() - 1, 0); + + if (index >= (int) text_data.size()) + return 0; + + return text_data[index].text_info.width; } -int Text::getHeight() const +int Text::getHeight(int index) const { - return text_info.height; + if (index < 0) + index = std::max((int) text_data.size() - 1, 0); + + if (index >= (int) text_data.size()) + return 0; + + return text_data[index].text_info.height; } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/Text.h b/jni/love/src/modules/graphics/opengl/Text.h index 15e02b74..345be9df 100644 --- a/jni/love/src/modules/graphics/opengl/Text.h +++ b/jni/love/src/modules/graphics/opengl/Text.h @@ -38,15 +38,15 @@ class Text : public Drawable { public: - Text(Font *font, const std::string &text = ""); + Text(Font *font, const std::vector &text = {}); virtual ~Text(); - void set(const std::string &text); - void set(const std::string &text, float wrap, Font::AlignMode align); + void set(const std::vector &text); + void set(const std::vector &text, float wrap, Font::AlignMode align); void set(); - void add(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); - void addf(const std::string &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); + int add(const std::vector &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); + int addf(const std::vector &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); void clear(); // Implements Drawable. @@ -58,20 +58,21 @@ public: /** * Gets the width of the currently set text. **/ - int getWidth() const; + int getWidth(int index = 0) const; /** * Gets the height of the currently set text. **/ - int getHeight() const; + int getHeight(int index = 0) const; private: struct TextData { - std::string text; + Font::ColoredCodepoints codepoints; float wrap; Font::AlignMode align; + Font::TextInfo text_info; bool use_matrix; bool append_vertices; Matrix3 matrix; @@ -87,7 +88,6 @@ private: std::vector draw_commands; std::vector text_data; - Font::TextInfo text_info; size_t vert_offset; diff --git a/jni/love/src/modules/graphics/opengl/Video.cpp b/jni/love/src/modules/graphics/opengl/Video.cpp new file mode 100644 index 00000000..4f33f4fb --- /dev/null +++ b/jni/love/src/modules/graphics/opengl/Video.cpp @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "Video.h" + +// LOVE +#include "Shader.h" + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + +Video::Video(love::video::VideoStream *stream) + : stream(stream) + , filter(Texture::getDefaultFilter()) +{ + filter.mipmap = Texture::FILTER_NONE; + + stream->fillBackBuffer(); + + for (int i = 0; i < 4; i++) + vertices[i].r = vertices[i].g = vertices[i].b = vertices[i].a = 255; + + // Vertices are ordered for use with triangle strips: + // 0----2 + // | / | + // | / | + // 1----3 + vertices[0].x = 0.0f; + vertices[0].y = 0.0f; + vertices[1].x = 0.0f; + vertices[1].y = (float) stream->getHeight(); + vertices[2].x = (float) stream->getWidth(); + vertices[2].y = 0.0f; + vertices[3].x = (float) stream->getWidth(); + vertices[3].y = (float) stream->getHeight(); + + vertices[0].s = 0.0f; + vertices[0].t = 0.0f; + vertices[1].s = 0.0f; + vertices[1].t = 1.0f; + vertices[2].s = 1.0f; + vertices[2].t = 0.0f; + vertices[3].s = 1.0f; + vertices[3].t = 1.0f; + + loadVolatile(); +} + +Video::~Video() +{ + unloadVolatile(); +} + +bool Video::loadVolatile() +{ + glGenTextures(3, &textures[0]); + + // Create the textures using the initial frame data. + auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer(); + + gl.bindTexture(textures[0]); + gl.setTextureFilter(filter); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->yw, frame->yh, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane); + + gl.bindTexture(textures[1]); + gl.setTextureFilter(filter); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane); + + gl.bindTexture(textures[2]); + gl.setTextureFilter(filter); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane); + + return true; +} + +void Video::unloadVolatile() +{ + for (int i = 0; i < 3; i++) + { + gl.deleteTexture(textures[i]); + textures[i] = 0; + } +} + +love::video::VideoStream *Video::getStream() +{ + return stream; +} + +void Video::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) +{ + update(); + + Shader *shader = Shader::current; + bool defaultShader = (shader == Shader::defaultShader); + if (defaultShader) + { + // If we're still using the default shader, substitute the video version + Shader::defaultVideoShader->attach(); + shader = Shader::defaultVideoShader; + } + + shader->setVideoTextures(textures[0], textures[1], textures[2]); + + OpenGL::TempTransform transform(gl); + transform.get() *= Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky); + + gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD); + + glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].x); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].s); + + gl.prepareDraw(); + gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // If we were using the default shader, reattach it + if (defaultShader) + Shader::defaultShader->attach(); +} + +void Video::update() +{ + bool bufferschanged = stream->swapBuffers(); + stream->fillBackBuffer(); + + if (bufferschanged) + { + auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer(); + + gl.bindTexture(textures[0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->yw, frame->yh, + GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane); + + gl.bindTexture(textures[1]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch, + GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane); + + gl.bindTexture(textures[2]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch, + GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane); + } +} + +love::audio::Source *Video::getSource() +{ + return source; +} + +void Video::setSource(love::audio::Source *source) +{ + this->source = source; +} + +int Video::getWidth() const +{ + return stream->getWidth(); +} + +int Video::getHeight() const +{ + return stream->getHeight(); +} + +void Video::setFilter(const Texture::Filter &f) +{ + if (!Texture::validateFilter(f, false)) + throw love::Exception("Invalid texture filter."); + + filter = f; + + for (int i = 0; i < 3; i++) + { + gl.bindTexture(textures[i]); + gl.setTextureFilter(filter); + } +} + +const Texture::Filter &Video::getFilter() const +{ + return filter; +} + +} // opengl +} // graphics +} // love diff --git a/jni/love/src/modules/graphics/opengl/Video.h b/jni/love/src/modules/graphics/opengl/Video.h new file mode 100644 index 00000000..f1704a48 --- /dev/null +++ b/jni/love/src/modules/graphics/opengl/Video.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#pragma once + +// LOVE +#include "common/math.h" +#include "graphics/Drawable.h" +#include "graphics/Volatile.h" +#include "video/VideoStream.h" +#include "audio/Source.h" + +#include "OpenGL.h" + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + +class Video : public Drawable, public Volatile +{ +public: + + Video(love::video::VideoStream *stream); + ~Video(); + + // Volatile + bool loadVolatile(); + void unloadVolatile(); + + love::video::VideoStream *getStream(); + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); + + love::audio::Source *getSource(); + void setSource(love::audio::Source *source); + + int getWidth() const; + int getHeight() const; + + void setFilter(const Texture::Filter &f); + const Texture::Filter &getFilter() const; + +private: + + void update(); + + StrongRef stream; + StrongRef source; + + GLuint textures[3]; + + Vertex vertices[4]; + + Texture::Filter filter; + +}; // Video + +} // opengl +} // graphics +} // love diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp index 004ee2f9..7294d265 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -101,17 +101,8 @@ int w_Canvas_getMSAA(lua_State *L) return 1; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Canvas_functions[] = { - // From wrap_Texture. - { "getWidth", w_Texture_getWidth }, - { "getHeight", w_Texture_getHeight }, - { "getDimensions", w_Texture_getDimensions }, - { "setFilter", w_Texture_setFilter }, - { "getFilter", w_Texture_getFilter }, - { "setWrap", w_Texture_setWrap }, - { "getWrap", w_Texture_getWrap }, - { "renderTo", w_Canvas_renderTo }, { "newImageData", w_Canvas_newImageData }, { "getFormat", w_Canvas_getFormat }, @@ -121,7 +112,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_canvas(lua_State *L) { - return luax_register_type(L, GRAPHICS_CANVAS_ID, functions); + return luax_register_type(L, GRAPHICS_CANVAS_ID, "Canvas", w_Texture_functions, w_Canvas_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.h b/jni/love/src/modules/graphics/opengl/wrap_Canvas.h index 9eb0fbe1..9cb28ff5 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.h @@ -35,10 +35,6 @@ namespace opengl //see Canvas.h Canvas *luax_checkcanvas(lua_State *L, int idx); -int w_Canvas_renderTo(lua_State *L); -int w_Canvas_newImageData(lua_State *L); -int w_Canvas_getFormat(lua_State *L); -int w_Canvas_getMSAA(lua_State *L); extern "C" int luaopen_canvas(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Font.cpp b/jni/love/src/modules/graphics/opengl/wrap_Font.cpp index 22836c84..d449925b 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Font.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Font.cpp @@ -184,7 +184,7 @@ int w_Font_setFallbacks(lua_State *L) return 0; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Font_functions[] = { { "getHeight", w_Font_getHeight }, { "getWidth", w_Font_getWidth }, @@ -203,7 +203,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_font(lua_State *L) { - return luax_register_type(L, GRAPHICS_FONT_ID, functions); + return luax_register_type(L, GRAPHICS_FONT_ID, "Font", w_Font_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Font.h b/jni/love/src/modules/graphics/opengl/wrap_Font.h index 172cc90c..048960fe 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Font.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Font.h @@ -33,18 +33,6 @@ namespace opengl { Font *luax_checkfont(lua_State *L, int idx); -int w_Font_getHeight(lua_State *L); -int w_Font_getWidth(lua_State *L); -int w_Font_getWrap(lua_State *L); -int w_Font_setLineHeight(lua_State *L); -int w_Font_getLineHeight(lua_State *L); -int w_Font_setFilter(lua_State *L); -int w_Font_getFilter(lua_State *L); -int w_Font_getAscent(lua_State *L); -int w_Font_getDescent(lua_State *L); -int w_Font_getBaseline(lua_State *L); -int w_Font_hasGlyphs(lua_State *L); -int w_Font_setFallbacks(lua_State *L); extern "C" int luaopen_font(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp index 4a445b7e..9d98032b 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -25,6 +25,7 @@ #include "image/Image.h" #include "font/Rasterizer.h" #include "filesystem/wrap_Filesystem.h" +#include "video/VideoStream.h" #include "image/wrap_Image.h" #include @@ -184,6 +185,20 @@ int w_setScissor(lua_State *L) return 0; } +int w_intersectScissor(lua_State *L) +{ + int x = (int) luaL_checknumber(L, 1); + int y = (int) luaL_checknumber(L, 2); + int w = (int) luaL_checknumber(L, 3); + int h = (int) luaL_checknumber(L, 4); + + if (w < 0 || h < 0) + return luaL_error(L, "Can't set scissor with negative width and/or height."); + + instance()->intersectScissor(x, y, w, h); + return 0; +} + int w_getScissor(lua_State *L) { int x, y, w, h; @@ -354,13 +369,13 @@ int w_newImage(lua_State *L) int w_newQuad(lua_State *L) { Quad::Viewport v; - v.x = (float) luaL_checknumber(L, 1); - v.y = (float) luaL_checknumber(L, 2); - v.w = (float) luaL_checknumber(L, 3); - v.h = (float) luaL_checknumber(L, 4); + v.x = luaL_checknumber(L, 1); + v.y = luaL_checknumber(L, 2); + v.w = luaL_checknumber(L, 3); + v.h = luaL_checknumber(L, 4); - float sw = (float) luaL_checknumber(L, 5); - float sh = (float) luaL_checknumber(L, 6); + double sw = luaL_checknumber(L, 5); + double sh = luaL_checknumber(L, 6); Quad *quad = instance()->newQuad(v, sw, sh); luax_pushtype(L, GRAPHICS_QUAD_ID, quad); @@ -837,7 +852,9 @@ int w_newText(lua_State *L) luax_catchexcept(L, [&](){ t = instance()->newText(font); }); else { - std::string text = luax_checkstring(L, 2); + std::vector text; + luax_checkcoloredstring(L, 2, text); + luax_catchexcept(L, [&](){ t = instance()->newText(font, text); }); } @@ -846,6 +863,20 @@ int w_newText(lua_State *L) return 1; } +int w_newVideo(lua_State *L) +{ + if (!luax_istype(L, 1, VIDEO_VIDEO_STREAM_ID)) + luax_convobj(L, 1, "video", "newVideoStream"); + + auto stream = luax_checktype(L, 1, VIDEO_VIDEO_STREAM_ID); + Video *video = nullptr; + + luax_catchexcept(L, [&]() { video = instance()->newVideo(stream); }); + luax_pushtype(L, GRAPHICS_VIDEO_ID, video); + video->release(); + return 1; +} + int w_setColor(lua_State *L) { Colorf c; @@ -1258,25 +1289,37 @@ int w_setDefaultShaderCode(lua_State *L) lua_getfield(L, 1, "opengl"); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); + lua_rawgeti(L, -3, 3); Shader::ShaderSource openglcode; - openglcode.vertex = luax_checkstring(L, -2); - openglcode.pixel = luax_checkstring(L, -1); + openglcode.vertex = luax_checkstring(L, -3); + openglcode.pixel = luax_checkstring(L, -2); - lua_pop(L, 3); + Shader::ShaderSource openglVideocode; + openglVideocode.vertex = luax_checkstring(L, -3); + openglVideocode.pixel = luax_checkstring(L, -1); + + lua_pop(L, 4); lua_getfield(L, 1, "opengles"); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); + lua_rawgeti(L, -3, 3); Shader::ShaderSource openglescode; - openglescode.vertex = luax_checkstring(L, -2); - openglescode.pixel = luax_checkstring(L, -1); + openglescode.vertex = luax_checkstring(L, -3); + openglescode.pixel = luax_checkstring(L, -2); - lua_pop(L, 3); + Shader::ShaderSource openglesVideocode; + openglesVideocode.vertex = luax_checkstring(L, -3); + openglesVideocode.pixel = luax_checkstring(L, -1); + + lua_pop(L, 4); Shader::defaultCode[Graphics::RENDERER_OPENGL] = openglcode; Shader::defaultCode[Graphics::RENDERER_OPENGLES] = openglescode; + Shader::defaultVideoCode[Graphics::RENDERER_OPENGL] = openglVideocode; + Shader::defaultVideoCode[Graphics::RENDERER_OPENGLES] = openglesVideocode; return 0; } @@ -1452,7 +1495,9 @@ int w_draw(lua_State *L) int w_print(lua_State *L) { - std::string str = luax_checkstring(L, 1); + std::vector str; + luax_checkcoloredstring(L, 1, str); + float x = (float)luaL_optnumber(L, 2, 0.0); float y = (float)luaL_optnumber(L, 3, 0.0); float angle = (float)luaL_optnumber(L, 4, 0.0f); @@ -1471,7 +1516,9 @@ int w_print(lua_State *L) int w_printf(lua_State *L) { - std::string str = luax_checkstring(L, 1); + std::vector str; + luax_checkcoloredstring(L, 1, str); + float x = (float)luaL_checknumber(L, 2); float y = (float)luaL_checknumber(L, 3); float wrap = (float)luaL_checknumber(L, 4); @@ -1843,6 +1890,7 @@ static const luaL_Reg functions[] = { "newShader", w_newShader }, { "newMesh", w_newMesh }, { "newText", w_newText }, + { "_newVideo", w_newVideo }, { "setColor", w_setColor }, { "getColor", w_getColor }, @@ -1899,6 +1947,7 @@ static const luaL_Reg functions[] = { "getDimensions", w_getDimensions }, { "setScissor", w_setScissor }, + { "intersectScissor", w_intersectScissor }, { "getScissor", w_getScissor }, { "stencil", w_stencil }, @@ -1925,9 +1974,16 @@ static const luaL_Reg functions[] = { 0, 0 } }; +static int luaopen_drawable(lua_State *L) +{ + return luax_register_type(L, GRAPHICS_DRAWABLE_ID, "Drawable", nullptr); +} + // Types for this module. static const lua_CFunction types[] = { + luaopen_drawable, + luaopen_texture, luaopen_font, luaopen_image, luaopen_quad, @@ -1937,6 +1993,7 @@ static const lua_CFunction types[] = luaopen_shader, luaopen_mesh, luaopen_text, + luaopen_video, 0 }; diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.h b/jni/love/src/modules/graphics/opengl/wrap_Graphics.h index 47837599..5a7b7b78 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.h @@ -32,6 +32,7 @@ #include "wrap_Shader.h" #include "wrap_Mesh.h" #include "wrap_Text.h" +#include "wrap_Video.h" #include "Graphics.h" namespace love @@ -41,85 +42,6 @@ namespace graphics namespace opengl { -int w_reset(lua_State *L); -int w_clear(lua_State *L); -int w_discard(lua_State *L); -int w_present(lua_State *L); -int w_isCreated(lua_State *L); -int w_isActive(lua_State *L); -int w_isGammaCorrect(lua_State *L); -int w_getWidth(lua_State *L); -int w_getHeight(lua_State *L); -int w_getDimensions(lua_State *L); -int w_setScissor(lua_State *L); -int w_getScissor(lua_State *L); -int w_stencil(lua_State *L); -int w_setStencilTest(lua_State *L); -int w_getStencilTest(lua_State *L); -int w_newImage(lua_State *L); -int w_newQuad(lua_State *L); -int w_newFont(lua_State *L); -int w_newImageFont(lua_State *L); -int w_newSpriteBatch(lua_State *L); -int w_newParticleSystem(lua_State *L); -int w_newCanvas(lua_State *L); -int w_newShader(lua_State *L); -int w_newMesh(lua_State *L); -int w_newText(lua_State *L); -int w_setColor(lua_State *L); -int w_getColor(lua_State *L); -int w_setBackgroundColor(lua_State *L); -int w_getBackgroundColor(lua_State *L); -int w_setNewFont(lua_State *L); -int w_setFont(lua_State *L); -int w_getFont(lua_State *L); -int w_setColorMask(lua_State *L); -int w_getColorMask(lua_State *L); -int w_setBlendMode(lua_State *L); -int w_getBlendMode(lua_State *L); -int w_setDefaultFilter(lua_State *L); -int w_getDefaultFilter(lua_State *L); -int w_setDefaultMipmapFilter(lua_State *L); -int w_getDefaultMipmapFilter(lua_State *L); -int w_setLineWidth(lua_State *L); -int w_setLineStyle(lua_State *L); -int w_setLineJoin(lua_State *L); -int w_getLineWidth(lua_State *L); -int w_getLineStyle(lua_State *L); -int w_getLineJoin(lua_State *L); -int w_setPointSize(lua_State *L); -int w_getPointSize(lua_State *L); -int w_setWireframe(lua_State *L); -int w_isWireframe(lua_State *L); -int w_newScreenshot(lua_State *L); -int w_setCanvas(lua_State *L); -int w_getCanvas(lua_State *L); -int w_setShader(lua_State *L); -int w_getShader(lua_State *L); -int w_setDefaultShaderCode(lua_State *L); -int w_getSupported(lua_State *L); -int w_getCanvasFormats(lua_State *L); -int w_getCompressedImageFormats(lua_State *L); -int w_getRendererInfo(lua_State *L); -int w_getSystemLimits(lua_State *L); -int w_getStats(lua_State *L); -int w_draw(lua_State *L); -int w_print(lua_State *L); -int w_printf(lua_State *L); -int w_points(lua_State *L); -int w_line(lua_State *L); -int w_rectangle(lua_State *L); -int w_circle(lua_State *L); -int w_ellipse(lua_State *L); -int w_arc(lua_State *L); -int w_polygon(lua_State *L); -int w_push(lua_State *L); -int w_pop(lua_State *L); -int w_rotate(lua_State *L); -int w_scale(lua_State *L); -int w_translate(lua_State *L); -int w_shear(lua_State *L); -int w_origin(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_graphics(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.lua b/jni/love/src/modules/graphics/opengl/wrap_Graphics.lua index 5a52d4b3..823779cc 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.lua +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.lua @@ -159,12 +159,34 @@ precision mediump float; #endif varying mediump vec4 VaryingTexCoord; -varying lowp vec4 VaryingColor; +varying mediump vec4 VaryingColor; #define love_Canvases gl_FragData uniform sampler2D _tex0_;]], + FUNCTIONS = [[ +uniform sampler2D love_VideoYChannel; +uniform sampler2D love_VideoCbChannel; +uniform sampler2D love_VideoCrChannel; + +vec4 VideoTexel(vec2 texcoords) +{ + vec3 yuv; + yuv[0] = Texel(love_VideoYChannel, texcoords).r; + yuv[1] = Texel(love_VideoCbChannel, texcoords).r; + yuv[2] = Texel(love_VideoCrChannel, texcoords).r; + yuv += vec3(-0.0627451017, -0.501960814, -0.501960814); + + vec4 color; + color.r = dot(yuv, vec3(1.164, 0.000, 1.596)); + color.g = dot(yuv, vec3(1.164, -0.391, -0.813)); + color.b = dot(yuv, vec3(1.164, 2.018, 0.000)); + color.a = 1.0; + + return gammaCorrectColor(color); +}]], + FOOTER = [[ void main() { // fix crashing issue in OSX when _tex0_ is unused within effect() @@ -209,6 +231,7 @@ local function createPixelCode(pixelcode, is_multicanvas, lang) love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "", GLSL.PIXEL.HEADER, GLSL.UNIFORMS, GLSL.FUNCTIONS, + GLSL.PIXEL.FUNCTIONS, lang == "glsles" and "#line 1" or "#line 0", pixelcode, is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER, @@ -307,23 +330,47 @@ vec4 position(mat4 transform_proj, vec4 vertpos) { return transform_proj * vertpos; }]], pixel = [[ -vec4 effect(lowp vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) { +vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) { return Texel(tex, texcoord) * vcolor; -}]] +}]], + videopixel = [[ +vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) { + return VideoTexel(texcoord) * vcolor; +}]], } local defaults = { opengl = { createVertexCode(defaultcode.vertex, "glsl"), createPixelCode(defaultcode.pixel, false, "glsl"), + createPixelCode(defaultcode.videopixel, false, "glsl"), }, opengles = { createVertexCode(defaultcode.vertex, "glsles"), createPixelCode(defaultcode.pixel, false, "glsles"), + createPixelCode(defaultcode.videopixel, false, "glsles"), }, } love.graphics._setDefaultShaderCode(defaults) +function love.graphics.newVideo(file, loadaudio) + local video = love.graphics._newVideo(file) + local source, success + + if loadaudio ~= false then + success, source = pcall(love.audio.newSource, video:getStream():getFilename()) + end + if success then + video:setSource(source) + elseif loadaudio == true then + error("Video had no audio track", 2) + else + video:getStream():setSync(love.video.newRemote()) + end + + return video +end + -- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. --)luastring"--" diff --git a/jni/love/src/modules/graphics/opengl/wrap_Image.cpp b/jni/love/src/modules/graphics/opengl/wrap_Image.cpp index 8a3a147f..c14892b5 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Image.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Image.cpp @@ -137,17 +137,8 @@ int w_Image_getFlags(lua_State *L) return 1; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Image_functions[] = { - // From wrap_Texture. - { "getWidth", w_Texture_getWidth }, - { "getHeight", w_Texture_getHeight }, - { "getDimensions", w_Texture_getDimensions }, - { "setFilter", w_Texture_setFilter }, - { "getFilter", w_Texture_getFilter }, - { "setWrap", w_Texture_setWrap }, - { "getWrap", w_Texture_getWrap }, - { "setMipmapFilter", w_Image_setMipmapFilter }, { "getMipmapFilter", w_Image_getMipmapFilter }, { "isCompressed", w_Image_isCompressed }, @@ -159,7 +150,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_image(lua_State *L) { - return luax_register_type(L, GRAPHICS_IMAGE_ID, functions); + return luax_register_type(L, GRAPHICS_IMAGE_ID, "Image", w_Texture_functions, w_Image_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Image.h b/jni/love/src/modules/graphics/opengl/wrap_Image.h index edf8f789..d5848798 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Image.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Image.h @@ -34,12 +34,6 @@ namespace opengl { Image *luax_checkimage(lua_State *L, int idx); -int w_Image_setMipmapFilter(lua_State *L); -int w_Image_getMipmapFilter(lua_State *L); -int w_Image_isCompressed(lua_State *L); -int w_Image_refresh(lua_State *L); -int w_Image_getData(lua_State *L); -int w_Image_getFlags(lua_State *L); extern "C" int luaopen_image(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp b/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp index 6d6c25b9..d430c48b 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp @@ -109,10 +109,15 @@ int w_Mesh_setVertices(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); luaL_checktype(L, 2, LUA_TTABLE); + size_t vertoffset = (size_t) luaL_optnumber(L, 3, 1) - 1; + + if (vertoffset >= t->getVertexCount()) + return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", (int) t->getVertexCount()); size_t nvertices = luax_objlen(L, 2); - if (nvertices != t->getVertexCount()) - return luaL_error(L, "Invalid number of vertices (expected %d, got %d)", (int) t->getVertexCount(), (int) nvertices); + + if (vertoffset + nvertices > t->getVertexCount()) + return luaL_error(L, "Too many vertices (expected at most %d, got %d)", (int) t->getVertexCount() - (int) vertoffset, (int) nvertices); const std::vector &vertexformat = t->getVertexFormat(); @@ -120,7 +125,10 @@ int w_Mesh_setVertices(lua_State *L) for (const Mesh::AttribFormat &format : vertexformat) ncomponents += format.components; - char *data = (char *) t->mapVertexData(); + size_t stride = t->getVertexStride(); + size_t byteoffset = vertoffset * stride; + + char *data = (char *) t->mapVertexData() + byteoffset; for (size_t i = 0; i < nvertices; i++) { @@ -145,7 +153,7 @@ int w_Mesh_setVertices(lua_State *L) lua_pop(L, ncomponents + 1); } - t->unmapVertexData(); + t->unmapVertexData(byteoffset, nvertices * stride); return 0; } @@ -331,6 +339,13 @@ int w_Mesh_setVertexMap(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); + if (lua_isnoneornil(L, 2)) + { + // Disable the vertex map / index buffer. + luax_catchexcept(L, [&](){ t->setVertexMap(); }); + return 0; + } + bool is_table = lua_istable(L, 2); int nargs = is_table ? (int) luax_objlen(L, 2) : lua_gettop(L) - 1; @@ -361,7 +376,14 @@ int w_Mesh_getVertexMap(lua_State *L) Mesh *t = luax_checkmesh(L, 1); std::vector vertex_map; - luax_catchexcept(L, [&](){ t->getVertexMap(vertex_map); }); + bool has_vertex_map = false; + luax_catchexcept(L, [&](){ has_vertex_map = t->getVertexMap(vertex_map); }); + + if (!has_vertex_map) + { + lua_pushnil(L); + return 1; + } int element_count = (int) vertex_map.size(); @@ -468,7 +490,7 @@ int w_Mesh_getDrawRange(lua_State *L) return 2; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Mesh_functions[] = { { "setVertices", w_Mesh_setVertices }, { "setVertex", w_Mesh_setVertex }, @@ -494,7 +516,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_mesh(lua_State *L) { - return luax_register_type(L, GRAPHICS_MESH_ID, functions); + return luax_register_type(L, GRAPHICS_MESH_ID, "Mesh", w_Mesh_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Mesh.h b/jni/love/src/modules/graphics/opengl/wrap_Mesh.h index fe8334a8..9a95bbd6 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Mesh.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Mesh.h @@ -36,25 +36,6 @@ char *luax_writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, i const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data); Mesh *luax_checkmesh(lua_State *L, int idx); -int w_Mesh_setVertices(lua_State *L); -int w_Mesh_setVertex(lua_State *L); -int w_Mesh_getVertex(lua_State *L); -int w_Mesh_setVertexAttribute(lua_State *L); -int w_Mesh_getVertexAttribute(lua_State *L); -int w_Mesh_getVertexCount(lua_State *L); -int w_Mesh_getVertexFormat(lua_State *L); -int w_Mesh_setAttributeEnabled(lua_State *L); -int w_Mesh_isAttributeEnabled(lua_State *L); -int w_Mesh_attachAttribute(lua_State *L); -int w_Mesh_flush(lua_State *L); -int w_Mesh_setVertexMap(lua_State *L); -int w_Mesh_getVertexMap(lua_State *L); -int w_Mesh_setTexture(lua_State *L); -int w_Mesh_getTexture(lua_State *L); -int w_Mesh_setDrawMode(lua_State *L); -int w_Mesh_getDrawMode(lua_State *L); -int w_Mesh_setDrawRange(lua_State *L); -int w_Mesh_getDrawRange(lua_State *L); extern "C" int luaopen_mesh(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp index 8c547b77..b1f1caa0 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp @@ -705,7 +705,7 @@ int w_ParticleSystem_update(lua_State *L) return 0; } -static const luaL_Reg functions[] = +static const luaL_Reg w_ParticleSystem_functions[] = { { "clone", w_ParticleSystem_clone }, { "setTexture", w_ParticleSystem_setTexture }, @@ -772,7 +772,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_particlesystem(lua_State *L) { - return luax_register_type(L, GRAPHICS_PARTICLE_SYSTEM_ID, functions); + return luax_register_type(L, GRAPHICS_PARTICLE_SYSTEM_ID, "ParticleSystem", w_ParticleSystem_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h index ff7b9091..fc25ad3e 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h +++ b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h @@ -33,66 +33,6 @@ namespace opengl { ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx); -int w_ParticleSystem_clone(lua_State *L); -int w_ParticleSystem_setTexture(lua_State *L); -int w_ParticleSystem_getTexture(lua_State *L); -int w_ParticleSystem_setBufferSize(lua_State *L); -int w_ParticleSystem_getBufferSize(lua_State *L); -int w_ParticleSystem_setInsertMode(lua_State *L); -int w_ParticleSystem_getInsertMode(lua_State *L); -int w_ParticleSystem_setEmissionRate(lua_State *L); -int w_ParticleSystem_getEmissionRate(lua_State *L); -int w_ParticleSystem_setEmitterLifetime(lua_State *L); -int w_ParticleSystem_getEmitterLifetime(lua_State *L); -int w_ParticleSystem_setParticleLifetime(lua_State *L); -int w_ParticleSystem_getParticleLifetime(lua_State *L); -int w_ParticleSystem_setPosition(lua_State *L); -int w_ParticleSystem_getPosition(lua_State *L); -int w_ParticleSystem_moveTo(lua_State *L); -int w_ParticleSystem_setAreaSpread(lua_State *L); -int w_ParticleSystem_getAreaSpread(lua_State *L); -int w_ParticleSystem_setDirection(lua_State *L); -int w_ParticleSystem_getDirection(lua_State *L); -int w_ParticleSystem_setSpread(lua_State *L); -int w_ParticleSystem_getSpread(lua_State *L); -int w_ParticleSystem_setSpeed(lua_State *L); -int w_ParticleSystem_getSpeed(lua_State *L); -int w_ParticleSystem_setLinearAcceleration(lua_State *L); -int w_ParticleSystem_getLinearAcceleration(lua_State *L); -int w_ParticleSystem_setRadialAcceleration(lua_State *L); -int w_ParticleSystem_getRadialAcceleration(lua_State *L); -int w_ParticleSystem_setTangentialAcceleration(lua_State *L); -int w_ParticleSystem_getTangentialAcceleration(lua_State *L); -int w_ParticleSystem_setLinearDamping(lua_State *L); -int w_ParticleSystem_getLinearDamping(lua_State *L); -int w_ParticleSystem_setSizes(lua_State *L); -int w_ParticleSystem_getSizes(lua_State *L); -int w_ParticleSystem_setSizeVariation(lua_State *L); -int w_ParticleSystem_getSizeVariation(lua_State *L); -int w_ParticleSystem_setRotation(lua_State *L); -int w_ParticleSystem_getRotation(lua_State *L); -int w_ParticleSystem_setSpin(lua_State *L); -int w_ParticleSystem_getSpin(lua_State *L); -int w_ParticleSystem_setSpinVariation(lua_State *L); -int w_ParticleSystem_getSpinVariation(lua_State *L); -int w_ParticleSystem_setColors(lua_State *L); -int w_ParticleSystem_getColors(lua_State *L); -int w_ParticleSystem_setQuads(lua_State *L); -int w_ParticleSystem_getQuads(lua_State *L); -int w_ParticleSystem_setOffset(lua_State *L); -int w_ParticleSystem_getOffset(lua_State *L); -int w_ParticleSystem_setRelativeRotation(lua_State *L); -int w_ParticleSystem_hasRelativeRotation(lua_State *L); -int w_ParticleSystem_getCount(lua_State *L); -int w_ParticleSystem_start(lua_State *L); -int w_ParticleSystem_stop(lua_State *L); -int w_ParticleSystem_pause(lua_State *L); -int w_ParticleSystem_reset(lua_State *L); -int w_ParticleSystem_emit(lua_State *L); -int w_ParticleSystem_isActive(lua_State *L); -int w_ParticleSystem_isPaused(lua_State *L); -int w_ParticleSystem_isStopped(lua_State *L); -int w_ParticleSystem_update(lua_State *L); extern "C" int luaopen_particlesystem(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Shader.cpp b/jni/love/src/modules/graphics/opengl/wrap_Shader.cpp index cbd8cfdf..21a33bf2 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Shader.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace love { @@ -229,38 +230,86 @@ int w_Shader_sendMatrix(lua_State *L) if (!lua_istable(L, 3)) return luax_typerror(L, 3, "matrix table"); - lua_getfield(L, 3, "dimension"); - int dimension = (int) lua_tointeger(L, -1); + int dimension = 0; + + lua_rawgeti(L, 3, 1); + if (lua_istable(L, -1)) + dimension = (int) luax_objlen(L, 3); lua_pop(L, 1); + if (dimension == 0) + { + lua_getfield(L, 3, "dimension"); + + if (!lua_isnoneornil(L, -1)) + dimension = (int) lua_tointeger(L, -1); + else + dimension = (int) sqrtf((float) luax_objlen(L, 3)); + + lua_pop(L, 1); + } + if (dimension < 2 || dimension > 4) return luaL_error(L, "Invalid matrix size: %dx%d (only 2x2, 3x3 and 4x4 matrices are supported).", dimension, dimension); float *values = new float[dimension * dimension * count]; + for (int i = 0; i < count; ++i) { - lua_getfield(L, 3+i, "dimension"); - if (lua_tointeger(L, -1) != dimension) + int other_dimension = 0; + + lua_rawgeti(L, 3+i, 1); + bool table_of_tables = lua_istable(L, -1); + + if (table_of_tables) + other_dimension = luax_objlen(L, -1); + + lua_pop(L, 1); + + if (!table_of_tables) + other_dimension = (int) sqrtf((float) luax_objlen(L, 3+i)); + + if (other_dimension != dimension) { // You unlock this door with the key of imagination. Beyond it is // another dimension: a dimension of sound, a dimension of sight, // a dimension of mind. You're moving into a land of both shadow // and substance, of things and ideas. You've just crossed over // into... the Twilight Zone. - int other_dimension = (int) lua_tointeger(L, -1); delete[] values; return luaL_error(L, "Invalid matrix size at argument %d: Expected size %dx%d, got %dx%d.", 3+i, dimension, dimension, other_dimension, other_dimension); } - for (int k = 1; k <= dimension*dimension; ++k) + if (table_of_tables) { - lua_rawgeti(L, 3+i, k); - values[i * dimension * dimension + k - 1] = (float)lua_tonumber(L, -1); - } + int n = 0; - lua_pop(L, 1 + dimension); + for (int j = 1; j <= dimension; j++) + { + lua_rawgeti(L, 3+i, j); + + for (int k = 1; k <= dimension; k++) + { + lua_rawgeti(L, -k, k); + values[i * dimension * dimension + n] = (float) lua_tonumber(L, -1); + n++; + } + + lua_pop(L, dimension + 1); + } + } + else + { + for (int k = 1; k <= dimension*dimension; k++) + { + lua_rawgeti(L, 3+i, k); + values[i * dimension * dimension + k - 1] = (float) lua_tonumber(L, -1); + } + + lua_pop(L, dimension*dimension); + } } luax_catchexcept(L, @@ -281,48 +330,6 @@ int w_Shader_sendTexture(lua_State *L) return 0; } -// Convert matrices on the stack for use with sendMatrix. -static void w_convertMatrices(lua_State *L, int idx) -{ - int matrixcount = lua_gettop(L) - (idx - 1); - - for (int matrix = idx; matrix < idx + matrixcount; matrix++) - { - luaL_checktype(L, matrix, LUA_TTABLE); - int dimension = (int) luax_objlen(L, matrix); - - int newi = 1; - lua_createtable(L, dimension * dimension, 0); - - // Collapse {{a,b,c}, {d,e,f}, ...} to {a,b,c, d,e,f, ...} - for (int i = 1; i <= (int) luax_objlen(L, matrix); i++) - { - // Push args[matrix][i] onto the stack. - lua_rawgeti(L, matrix, i); - luaL_checktype(L, -1, LUA_TTABLE); - - for (int j = 1; j <= (int) luax_objlen(L, -1); j++) - { - // Push args[matrix[i][j] onto the stack. - lua_rawgeti(L, -1, j); - luaL_checktype(L, -1, LUA_TNUMBER); - - // newtable[newi] = args[matrix][i][j] - lua_rawseti(L, -3, newi++); - } - - lua_pop(L, 1); - } - - // newtable.dimension = #args[matrix] - lua_pushinteger(L, dimension); - lua_setfield(L, -2, "dimension"); - - // Replace args[i] with the new table - lua_replace(L, matrix); - } -} - int w_Shader_send(lua_State *L) { int ttype = lua_type(L, 3); @@ -352,10 +359,7 @@ int w_Shader_send(lua_State *L) if (ttype == LUA_TNUMBER || ttype == LUA_TBOOLEAN) return w_Shader_sendFloat(L); else if (ttype == LUA_TTABLE) - { - w_convertMatrices(L, 3); return w_Shader_sendMatrix(L); - } break; default: @@ -397,7 +401,7 @@ int w_Shader_getExternVariable(lua_State *L) return 3; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Shader_functions[] = { { "getWarnings", w_Shader_getWarnings }, { "sendInt", w_Shader_sendInt }, @@ -413,7 +417,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_shader(lua_State *L) { - return luax_register_type(L, GRAPHICS_SHADER_ID, functions); + return luax_register_type(L, GRAPHICS_SHADER_ID, "Shader", w_Shader_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Shader.h b/jni/love/src/modules/graphics/opengl/wrap_Shader.h index 8cc77383..ecd46ffd 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Shader.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Shader.h @@ -33,14 +33,6 @@ namespace opengl { Shader *luax_checkshader(lua_State *L, int idx); -int w_Shader_getWarnings(lua_State *L); -int w_Shader_sendInt(lua_State *L); -int w_Shader_sendFloat(lua_State *L); -int w_Shader_sendColor(lua_State *L); -int w_Shader_sendMatrix(lua_State *L); -int w_Shader_sendTexture(lua_State *L); -int w_Shader_send(lua_State *L); -int w_Shader_getExternVariable(lua_State *L); extern "C" int luaopen_shader(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp b/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp index 52f76d3c..3394e83b 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp @@ -203,7 +203,17 @@ int w_SpriteBatch_getBufferSize(lua_State *L) return 1; } -static const luaL_Reg functions[] = +int w_SpriteBatch_attachAttribute(lua_State *L) +{ + SpriteBatch *t = luax_checkspritebatch(L, 1); + const char *name = luaL_checkstring(L, 2); + Mesh *m = luax_checktype(L, 3, GRAPHICS_MESH_ID); + + luax_catchexcept(L, [&](){ t->attachAttribute(name, m); }); + return 0; +} + +static const luaL_Reg w_SpriteBatch_functions[] = { { "add", w_SpriteBatch_add }, { "set", w_SpriteBatch_set }, @@ -216,12 +226,13 @@ static const luaL_Reg functions[] = { "getCount", w_SpriteBatch_getCount }, { "setBufferSize", w_SpriteBatch_setBufferSize }, { "getBufferSize", w_SpriteBatch_getBufferSize }, + { "attachAttribute", w_SpriteBatch_attachAttribute }, { 0, 0 } }; extern "C" int luaopen_spritebatch(lua_State *L) { - return luax_register_type(L, GRAPHICS_SPRITE_BATCH_ID, functions); + return luax_register_type(L, GRAPHICS_SPRITE_BATCH_ID, "SpriteBatch", w_SpriteBatch_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.h b/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.h index 84c92699..dd36502a 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.h +++ b/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.h @@ -32,18 +32,6 @@ namespace opengl { SpriteBatch *luax_checkspritebatch(lua_State *L, int idx); -int w_SpriteBatch_add(lua_State *L); -int w_SpriteBatch_set(lua_State *L); -int w_SpriteBatch_clear(lua_State *L); -int w_SpriteBatch_flush(lua_State *L); -int w_SpriteBatch_setTexture(lua_State *L); -int w_SpriteBatch_getTexture(lua_State *L); -int w_SpriteBatch_setColor(lua_State *L); -int w_SpriteBatch_getColor(lua_State *L); -int w_SpriteBatch_getCount(lua_State *L); -int w_SpriteBatch_setBufferSize(lua_State *L); -int w_SpriteBatch_getBufferSize(lua_State *L); - extern "C" int luaopen_spritebatch(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Text.cpp b/jni/love/src/modules/graphics/opengl/wrap_Text.cpp index 1f53871d..ef7058f1 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Text.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Text.cpp @@ -32,6 +32,47 @@ Text *luax_checktext(lua_State *L, int idx) return luax_checktype(L, idx, GRAPHICS_TEXT_ID); } +void luax_checkcoloredstring(lua_State *L, int idx, std::vector &strings) +{ + Font::ColoredString coloredstr; + coloredstr.color = Color(255, 255, 255, 255); + + if (lua_istable(L, idx)) + { + int len = luax_objlen(L, idx); + + for (int i = 1; i <= len; i++) + { + lua_rawgeti(L, idx, i); + + if (lua_istable(L, -1)) + { + for (int j = 1; j <= 4; j++) + lua_rawgeti(L, -j, j); + + coloredstr.color.r = (unsigned char) luaL_checknumber(L, -4); + coloredstr.color.g = (unsigned char) luaL_checknumber(L, -3); + coloredstr.color.b = (unsigned char) luaL_checknumber(L, -2); + coloredstr.color.a = (unsigned char) luaL_optnumber(L, -1, 255); + + lua_pop(L, 4); + } + else + { + coloredstr.str = luaL_checkstring(L, -1); + strings.push_back(coloredstr); + } + + lua_pop(L, 1); + } + } + else + { + coloredstr.str = luaL_checkstring(L, idx); + strings.push_back(coloredstr); + } +} + int w_Text_set(lua_State *L) { Text *t = luax_checktext(L, 1); @@ -44,7 +85,8 @@ int w_Text_set(lua_State *L) else if (lua_isnoneornil(L, 3)) { // Single argument: unformatted text. - std::string newtext = luax_checkstring(L, 2); + std::vector newtext; + luax_checkcoloredstring(L, 2, newtext); luax_catchexcept(L, [&](){ t->set(newtext); }); } else @@ -57,7 +99,8 @@ int w_Text_set(lua_State *L) if (!Font::getConstant(alignstr, align)) return luaL_error(L, "Invalid align mode: %s", alignstr); - std::string newtext = luax_checkstring(L, 2); + std::vector newtext; + luax_checkcoloredstring(L, 2, newtext); luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); }); } @@ -76,7 +119,8 @@ int w_Text_setf(lua_State *L) if (!Font::getConstant(alignstr, align)) return luaL_error(L, "Invalid align mode: %s", alignstr); - std::string newtext = luax_checkstring(L, 2); + std::vector newtext; + luax_checkcoloredstring(L, 2, newtext); luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); }); @@ -86,7 +130,9 @@ int w_Text_setf(lua_State *L) int w_Text_add(lua_State *L) { Text *t = luax_checktext(L, 1); - std::string text = luax_checkstring(L, 2); + + std::vector text; + luax_checkcoloredstring(L, 2, text); float x = (float) luaL_optnumber(L, 3, 0.0); float y = (float) luaL_optnumber(L, 4, 0.0); @@ -98,14 +144,20 @@ int w_Text_add(lua_State *L) float kx = (float) luaL_optnumber(L, 10, 0.0); float ky = (float) luaL_optnumber(L, 11, 0.0); - luax_catchexcept(L, [&](){ t->add(text, x, y, a, sx, sy, ox, oy, kx, ky); }); - return 0; + int index = 0; + luax_catchexcept(L, [&](){ index = t->add(text, x, y, a, sx, sy, ox, oy, kx, ky); }); + lua_pushnumber(L, index + 1); + + return 1; } int w_Text_addf(lua_State *L) { Text *t = luax_checktext(L, 1); - std::string text = luax_checkstring(L, 2); + + std::vector text; + luax_checkcoloredstring(L, 2, text); + float wrap = (float) luaL_checknumber(L, 3); Font::AlignMode align = Font::ALIGN_MAX_ENUM; @@ -124,8 +176,11 @@ int w_Text_addf(lua_State *L) float kx = (float) luaL_optnumber(L, 12, 0.0); float ky = (float) luaL_optnumber(L, 13, 0.0); - luax_catchexcept(L, [&](){ t->addf(text, wrap, align, x, y, a, sx, sy, ox, oy, kx, ky); }); - return 0; + int index = 0; + luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, x, y, a, sx, sy, ox, oy, kx, ky); }); + lua_pushnumber(L, index + 1); + + return 1; } int w_Text_clear(lua_State *L) @@ -154,18 +209,20 @@ int w_Text_getFont(lua_State *L) int w_Text_getWidth(lua_State *L) { Text *t = luax_checktext(L, 1); - lua_pushnumber(L, t->getWidth()); + int index = (int) luaL_optnumber(L, 2, 0) - 1; + lua_pushnumber(L, t->getWidth(index)); return 1; } int w_Text_getHeight(lua_State *L) { Text *t = luax_checktext(L, 1); - lua_pushnumber(L, t->getHeight()); + int index = (int) luaL_optnumber(L, 2, 0) - 1; + lua_pushnumber(L, t->getHeight(index)); return 1; } -static const luaL_Reg functions[] = +static const luaL_Reg w_Text_functions[] = { { "set", w_Text_set }, { "setf", w_Text_setf }, @@ -181,7 +238,7 @@ static const luaL_Reg functions[] = extern "C" int luaopen_text(lua_State *L) { - return luax_register_type(L, GRAPHICS_TEXT_ID, functions); + return luax_register_type(L, GRAPHICS_TEXT_ID, "Text", w_Text_functions, nullptr); } } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Text.h b/jni/love/src/modules/graphics/opengl/wrap_Text.h index fb867b85..283fad76 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Text.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Text.h @@ -32,15 +32,7 @@ namespace opengl { Text *luax_checktext(lua_State *L, int idx); -int w_Text_set(lua_State *L); -int w_Text_setf(lua_State *L); -int w_Text_add(lua_State *L); -int w_Text_addf(lua_State *L); -int w_Text_clear(lua_State *L); -int w_Text_setFont(lua_State *L); -int w_Text_getFont(lua_State *L); -int w_Text_getWidth(lua_State *L); -int w_Text_getHeight(lua_State *L); +void luax_checkcoloredstring(lua_State *L, int idx, std::vector &strings); extern "C" int luaopen_text(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Video.cpp b/jni/love/src/modules/graphics/opengl/wrap_Video.cpp new file mode 100644 index 00000000..70c01751 --- /dev/null +++ b/jni/love/src/modules/graphics/opengl/wrap_Video.cpp @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "wrap_Video.h" + +// Shove the wrap_Video.lua code directly into a raw string literal. +static const char video_lua[] = +#include "wrap_Video.lua" +; + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + +Video *luax_checkvideo(lua_State *L, int idx) +{ + return luax_checktype