Merge minor into dynamiccore2

--HG--
branch : dynamiccore2
This commit is contained in:
Bart van Strien
2016-11-26 22:19:12 +01:00
61 changed files with 4909 additions and 1720 deletions
+1
View File
@@ -10,3 +10,4 @@ e0f98d53debb62347c6433ca0534a0f77f15f76f 0.8.0
a5e405cdf14d030b71d09a105086797942ae91a9 0.9.2
14717f0fb5d96d380c10fe6f41b74b7652c7d836 0.10.0
3cb3559eebb89803c0b7fa83d51d8e48a2b20b7a 0.10.1
afc69c4f714534c64e6f8c8660b724b5b736d7f3 0.10.2
+24
View File
@@ -309,10 +309,14 @@ set(LOVE_SRC_MODULE_AUDIO_ROOT
src/modules/audio/Audio.h
src/modules/audio/Source.cpp
src/modules/audio/Source.h
src/modules/audio/RecordingDevice.cpp
src/modules/audio/RecordingDevice.h
src/modules/audio/wrap_Audio.cpp
src/modules/audio/wrap_Audio.h
src/modules/audio/wrap_Source.cpp
src/modules/audio/wrap_Source.h
src/modules/audio/wrap_RecordingDevice.cpp
src/modules/audio/wrap_RecordingDevice.h
)
set(LOVE_SRC_MODULE_AUDIO_NULL
@@ -320,6 +324,8 @@ set(LOVE_SRC_MODULE_AUDIO_NULL
src/modules/audio/null/Audio.h
src/modules/audio/null/Source.cpp
src/modules/audio/null/Source.h
src/modules/audio/null/RecordingDevice.cpp
src/modules/audio/null/RecordingDevice.h
)
set(LOVE_SRC_MODULE_AUDIO_OPENAL
@@ -329,6 +335,8 @@ set(LOVE_SRC_MODULE_AUDIO_OPENAL
src/modules/audio/openal/Pool.h
src/modules/audio/openal/Source.cpp
src/modules/audio/openal/Source.h
src/modules/audio/openal/RecordingDevice.cpp
src/modules/audio/openal/RecordingDevice.h
)
set(LOVE_SRC_MODULE_AUDIO
@@ -647,6 +655,8 @@ set(LOVE_SRC_MODULE_MATH
src/modules/math/MathModule.h
src/modules/math/RandomGenerator.cpp
src/modules/math/RandomGenerator.h
src/modules/math/Transform.cpp
src/modules/math/Transform.h
src/modules/math/wrap_BezierCurve.cpp
src/modules/math/wrap_BezierCurve.h
src/modules/math/wrap_CompressedData.cpp
@@ -655,6 +665,8 @@ set(LOVE_SRC_MODULE_MATH
src/modules/math/wrap_Math.h
src/modules/math/wrap_RandomGenerator.cpp
src/modules/math/wrap_RandomGenerator.h
src/modules/math/wrap_Transform.cpp
src/modules/math/wrap_Transform.h
)
source_group("modules\\math" FILES ${LOVE_SRC_MODULE_MATH})
@@ -1418,6 +1430,17 @@ set(LOVE_SRC_3P_WUFF
add_library(love_3p_wuff ${LOVE_SRC_3P_WUFF})
#
# xxHash
#
set(LOVE_SRC_3P_XXHASH
src/libraries/xxHash/xxhash.c
src/libraries/xxHash/xxhash.h
)
add_library(love_3p_xxhash ${LOVE_SRC_3P_XXHASH})
set(LOVE_3P
love_3p_box2d
love_3p_ddsparse
@@ -1429,6 +1452,7 @@ set(LOVE_3P
love_3p_lz4
love_3p_noise1234
love_3p_wuff
love_3p_xxhash
)
love_disable_warnings(love_3p_box2d love_3p_enet love_3p_luasocket)
+1 -1
View File
@@ -43,7 +43,7 @@ Released: N/A
LOVE 0.10.2 [Super Toast]
-------------------------
Released: N/A
Released: 2016-10-31
* Added lovec.exe in Windows. It is the same as love.exe but built with the Console subsystem, so it always uses or provides a console.
* Added the ability to restart the game via love.event.quit("restart").
@@ -880,6 +880,45 @@
FA4943551D9DDFFE00E8D38A /* HashFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA1DC2611C5D9555008F99A0 /* HashFunction.cpp */; };
FA4B66C91ABBCF1900558F15 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4B66C81ABBCF1900558F15 /* Timer.cpp */; };
FA4B66CA1ABBCF1900558F15 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4B66C81ABBCF1900558F15 /* Timer.cpp */; };
FA4F2B791DE0125B00CA37D7 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2B771DE0125B00CA37D7 /* xxhash.c */; };
FA4F2B7A1DE0125B00CA37D7 /* xxhash.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2B781DE0125B00CA37D7 /* xxhash.h */; };
FA4F2B7B1DE0181B00CA37D7 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2B771DE0125B00CA37D7 /* xxhash.c */; };
FA4F2BA61DE1E36400CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */; };
FA4F2BA71DE1E36400CA37D7 /* RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BA31DE1E36400CA37D7 /* RecordingDevice.h */; };
FA4F2BA81DE1E36400CA37D7 /* wrap_RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */; };
FA4F2BA91DE1E36400CA37D7 /* wrap_RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BA51DE1E36400CA37D7 /* wrap_RecordingDevice.h */; };
FA4F2BAC1DE1E37000CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */; };
FA4F2BAD1DE1E37000CA37D7 /* RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */; };
FA4F2BB01DE1E37B00CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */; };
FA4F2BB11DE1E37B00CA37D7 /* RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */; };
FA4F2BB21DE1E4B400CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */; };
FA4F2BB31DE1E4B800CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */; };
FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */; };
FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */; };
FA4F2BE31DE6650600CA37D7 /* Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */; };
FA4F2BE41DE6650600CA37D7 /* Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BE01DE6650600CA37D7 /* Transform.h */; };
FA4F2BE51DE6650600CA37D7 /* wrap_Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */; };
FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */; };
FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */; };
FA4F2BE81DE6651000CA37D7 /* wrap_Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */; };
FA4F2C031DE936C200CA37D7 /* auxiliar.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFB9D1D9F6D490055D849 /* auxiliar.c */; };
FA4F2C041DE936C600CA37D7 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFB9F1D9F6D490055D849 /* buffer.c */; };
FA4F2C051DE936C900CA37D7 /* compat.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBA11D9F6D490055D849 /* compat.c */; };
FA4F2C061DE936CD00CA37D7 /* except.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBA31D9F6D490055D849 /* except.c */; };
FA4F2C071DE936DA00CA37D7 /* inet.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBAB1D9F6D490055D849 /* inet.c */; };
FA4F2C081DE936DD00CA37D7 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBAD1D9F6D490055D849 /* io.c */; };
FA4F2C091DE936E200CA37D7 /* luasocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBB11D9F6D490055D849 /* luasocket.c */; };
FA4F2C0A1DE936E600CA37D7 /* mime.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBB61D9F6D490055D849 /* mime.c */; };
FA4F2C0B1DE936EA00CA37D7 /* options.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBBA1D9F6D490055D849 /* options.c */; };
FA4F2C0C1DE936ED00CA37D7 /* select.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBBD1D9F6D490055D849 /* select.c */; };
FA4F2C0D1DE936F100CA37D7 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBBF1D9F6D490055D849 /* serial.c */; };
FA4F2C0E1DE936FE00CA37D7 /* tcp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBC51D9F6D490055D849 /* tcp.c */; };
FA4F2C0F1DE936FE00CA37D7 /* timeout.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBC71D9F6D490055D849 /* timeout.c */; };
FA4F2C101DE936FE00CA37D7 /* udp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBCB1D9F6D490055D849 /* udp.c */; };
FA4F2C111DE936FE00CA37D7 /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBCD1D9F6D490055D849 /* unix.c */; };
FA4F2C121DE936FE00CA37D7 /* unixtcp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBCF1D9F6D490055D849 /* unixtcp.c */; };
FA4F2C131DE936FE00CA37D7 /* unixudp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD11D9F6D490055D849 /* unixudp.c */; };
FA4F2C141DE936FE00CA37D7 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; };
FA56D9BC1C208A0200D8D3C7 /* libmodplug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */; };
FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7916C71A1700860150 /* Cocoa.framework */; };
FA577AC216C7512D00860150 /* FreeType.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A6716C719D900860150 /* FreeType.framework */; };
@@ -1575,6 +1614,20 @@
FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ASTCHandler.cpp; sourceTree = "<group>"; };
FA41A3C71C0A1F950084430C /* ASTCHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTCHandler.h; sourceTree = "<group>"; };
FA4B66C81ABBCF1900558F15 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = "<group>"; };
FA4F2B771DE0125B00CA37D7 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = "<group>"; };
FA4F2B781DE0125B00CA37D7 /* xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xxhash.h; sourceTree = "<group>"; };
FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = "<group>"; };
FA4F2BA31DE1E36400CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = "<group>"; };
FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_RecordingDevice.cpp; sourceTree = "<group>"; };
FA4F2BA51DE1E36400CA37D7 /* wrap_RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_RecordingDevice.h; sourceTree = "<group>"; };
FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = "<group>"; };
FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = "<group>"; };
FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = "<group>"; };
FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = "<group>"; };
FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Transform.cpp; sourceTree = "<group>"; };
FA4F2BE01DE6650600CA37D7 /* Transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transform.h; sourceTree = "<group>"; };
FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Transform.cpp; sourceTree = "<group>"; };
FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Transform.h; sourceTree = "<group>"; };
FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmodplug.a; sourceTree = "<group>"; };
FA577A6716C719D900860150 /* FreeType.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FreeType.framework; path = /Library/Frameworks/FreeType.framework; sourceTree = "<absolute>"; };
FA577A6D16C719EA00860150 /* Lua.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lua.framework; path = /Library/Frameworks/Lua.framework; sourceTree = "<absolute>"; };
@@ -1783,6 +1836,7 @@
FA1557BE1CE90A2C00AFF582 /* tinyexr */,
FA0B7A191A958EA3000E1D17 /* utf8 */,
FA0B7A1F1A958EA3000E1D17 /* Wuff */,
FA4F2B761DE0125B00CA37D7 /* xxHash */,
);
name = libraries;
path = ../../src/libraries;
@@ -2195,10 +2249,14 @@
FA0B7B3F1A95902C000E1D17 /* Audio.h */,
FA0B7B401A95902C000E1D17 /* null */,
FA0B7B451A95902C000E1D17 /* openal */,
FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */,
FA4F2BA31DE1E36400CA37D7 /* RecordingDevice.h */,
FA0B7B4C1A95902C000E1D17 /* Source.cpp */,
FA0B7B4D1A95902C000E1D17 /* Source.h */,
FA0B7B4E1A95902C000E1D17 /* wrap_Audio.cpp */,
FA0B7B4F1A95902C000E1D17 /* wrap_Audio.h */,
FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */,
FA4F2BA51DE1E36400CA37D7 /* wrap_RecordingDevice.h */,
FA0B7B501A95902C000E1D17 /* wrap_Source.cpp */,
FA0B7B511A95902C000E1D17 /* wrap_Source.h */,
);
@@ -2210,6 +2268,8 @@
children = (
FA0B7B411A95902C000E1D17 /* Audio.cpp */,
FA0B7B421A95902C000E1D17 /* Audio.h */,
FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */,
FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */,
FA0B7B431A95902C000E1D17 /* Source.cpp */,
FA0B7B441A95902C000E1D17 /* Source.h */,
);
@@ -2223,6 +2283,8 @@
FA0B7B471A95902C000E1D17 /* Audio.h */,
FA0B7B481A95902C000E1D17 /* Pool.cpp */,
FA0B7B491A95902C000E1D17 /* Pool.h */,
FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */,
FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */,
FA0B7B4A1A95902C000E1D17 /* Source.cpp */,
FA0B7B4B1A95902C000E1D17 /* Source.h */,
);
@@ -2525,6 +2587,8 @@
FA0B7C041A95902C000E1D17 /* MathModule.h */,
FA0B7C051A95902C000E1D17 /* RandomGenerator.cpp */,
FA0B7C061A95902C000E1D17 /* RandomGenerator.h */,
FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */,
FA4F2BE01DE6650600CA37D7 /* Transform.h */,
FA0B7C071A95902C000E1D17 /* wrap_BezierCurve.cpp */,
FA0B7C081A95902C000E1D17 /* wrap_BezierCurve.h */,
FAB17BEE1ABFB37500F9BA27 /* wrap_CompressedData.cpp */,
@@ -2535,6 +2599,8 @@
FA0B7C0B1A95902C000E1D17 /* wrap_RandomGenerator.cpp */,
FA0B7C0C1A95902C000E1D17 /* wrap_RandomGenerator.h */,
FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */,
FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */,
FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */,
);
path = math;
sourceTree = "<group>";
@@ -2890,6 +2956,15 @@
path = theora;
sourceTree = "<group>";
};
FA4F2B761DE0125B00CA37D7 /* xxHash */ = {
isa = PBXGroup;
children = (
FA4F2B771DE0125B00CA37D7 /* xxhash.c */,
FA4F2B781DE0125B00CA37D7 /* xxhash.h */,
);
path = xxHash;
sourceTree = "<group>";
};
FA56D9B91C2089CE00D8D3C7 /* modplug */ = {
isa = PBXGroup;
children = (
@@ -3023,6 +3098,7 @@
FA0B7D381A95902C000E1D17 /* Font.h in Headers */,
FA0B7EC31A95902C000E1D17 /* threads.h in Headers */,
FA0B7AC21A958EA3000E1D17 /* enet.h in Headers */,
FA4F2BA71DE1E36400CA37D7 /* RecordingDevice.h in Headers */,
FA0B7E201A95902C000E1D17 /* Physics.h in Headers */,
217DFBEA1D9F6D490055D849 /* io.h in Headers */,
FA0B79221A958E3B000E1D17 /* delay.h in Headers */,
@@ -3147,6 +3223,7 @@
FA0B7E6B1A95902C000E1D17 /* wrap_PulleyJoint.h in Headers */,
FA0B7E051A95902C000E1D17 /* Contact.h in Headers */,
FA0B7A691A958EA3000E1D17 /* b2Island.h in Headers */,
FA4F2BE41DE6650600CA37D7 /* Transform.h in Headers */,
FA0B7E0E1A95902C000E1D17 /* Fixture.h in Headers */,
FA0B7A401A958EA3000E1D17 /* b2ChainShape.h in Headers */,
217DFBE81D9F6D490055D849 /* inet.h in Headers */,
@@ -3174,6 +3251,7 @@
FA0B7A341A958EA3000E1D17 /* b2Collision.h in Headers */,
217DFBDA1D9F6D490055D849 /* auxiliar.h in Headers */,
FA0B7EC71A95902C000E1D17 /* ThreadModule.h in Headers */,
FA4F2BAD1DE1E37000CA37D7 /* RecordingDevice.h in Headers */,
FA0B792B1A958E3B000E1D17 /* Matrix.h in Headers */,
FA0B7DA41A95902C000E1D17 /* PKMHandler.h in Headers */,
FA0B7AC81A958EA3000E1D17 /* utility.h in Headers */,
@@ -3221,6 +3299,7 @@
217DFBF71D9F6D490055D849 /* options.h in Headers */,
FA0B7E3B1A95902C000E1D17 /* World.h in Headers */,
FA0B7DC31A95902C000E1D17 /* wrap_Joystick.h in Headers */,
FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */,
FA0B7EE71A95902D000E1D17 /* Window.h in Headers */,
FA0B7E651A95902C000E1D17 /* wrap_PolygonShape.h in Headers */,
FA0B7AC91A958EA3000E1D17 /* win32.h in Headers */,
@@ -3242,6 +3321,7 @@
FA0B7D0B1A95902C000E1D17 /* wrap_FileData.h in Headers */,
FA0B7DF91A95902C000E1D17 /* Body.h in Headers */,
FA0B7DB91A95902C000E1D17 /* Joystick.h in Headers */,
FA4F2BB11DE1E37B00CA37D7 /* RecordingDevice.h in Headers */,
FA0B7E2C1A95902C000E1D17 /* RevoluteJoint.h in Headers */,
FA8951A41AA2EDF300EC385A /* wrap_Event.h in Headers */,
FA0B7B2A1A958EA3000E1D17 /* simplexnoise1234.h in Headers */,
@@ -3284,8 +3364,10 @@
FA0B79451A958E3B000E1D17 /* Variant.h in Headers */,
FA0B7D691A95902C000E1D17 /* wrap_ParticleSystem.h in Headers */,
FA0B7E5C1A95902C000E1D17 /* wrap_MotorJoint.h in Headers */,
FA4F2BA91DE1E36400CA37D7 /* wrap_RecordingDevice.h in Headers */,
FA0B7A7C1A958EA3000E1D17 /* b2Contact.h in Headers */,
FA0B7A881A958EA3000E1D17 /* b2PolygonAndCircleContact.h in Headers */,
FA4F2B7A1DE0125B00CA37D7 /* xxhash.h in Headers */,
FA0B7DDE1A95902C000E1D17 /* wrap_BezierCurve.h in Headers */,
FA0B7DED1A95902C000E1D17 /* Cursor.h in Headers */,
FA0B7E501A95902C000E1D17 /* wrap_Fixture.h in Headers */,
@@ -3452,7 +3534,9 @@
FA0B7EE31A95902D000E1D17 /* Window.cpp in Sources */,
FA0B7AAB1A958EA3000E1D17 /* b2WeldJoint.cpp in Sources */,
FA0B7EC21A95902C000E1D17 /* threads.cpp in Sources */,
FA4F2C051DE936C900CA37D7 /* compat.c in Sources */,
FA0B79371A958E3B000E1D17 /* macosx.mm in Sources */,
FA4F2BE81DE6651000CA37D7 /* wrap_Transform.cpp in Sources */,
FA0B7D011A95902C000E1D17 /* Filesystem.cpp in Sources */,
FA0B7ED91A95902D000E1D17 /* wrap_Timer.cpp in Sources */,
FA0B7DD11A95902C000E1D17 /* love.cpp in Sources */,
@@ -3464,6 +3548,7 @@
FA0B7E6D1A95902C000E1D17 /* wrap_RevoluteJoint.cpp in Sources */,
FA0B7A5F1A958EA3000E1D17 /* b2Body.cpp in Sources */,
FA0B7E641A95902C000E1D17 /* wrap_PolygonShape.cpp in Sources */,
FA4F2C031DE936C200CA37D7 /* auxiliar.c in Sources */,
FA0B7E731A95902C000E1D17 /* wrap_Shape.cpp in Sources */,
FA0B7CFE1A95902C000E1D17 /* File.cpp in Sources */,
FA27B39E1B498151008A9DCE /* Video.cpp in Sources */,
@@ -3481,6 +3566,7 @@
FA0B7D0A1A95902C000E1D17 /* wrap_FileData.cpp in Sources */,
FA0B7ABE1A958EA3000E1D17 /* compress.c in Sources */,
FA0B7CF21A95902C000E1D17 /* DroppedFile.cpp in Sources */,
FA4F2C141DE936FE00CA37D7 /* usocket.c in Sources */,
FA0B7D8A1A95902C000E1D17 /* CompressedImageData.cpp in Sources */,
FA0B7AD21A958EA3000E1D17 /* protocol.c in Sources */,
FA0B7D1F1A95902C000E1D17 /* ImageRasterizer.cpp in Sources */,
@@ -3498,12 +3584,15 @@
FA0B7E191A95902C000E1D17 /* MotorJoint.cpp in Sources */,
FA0B7E4F1A95902C000E1D17 /* wrap_Fixture.cpp in Sources */,
FA0B7EBF1A95902C000E1D17 /* Thread.cpp in Sources */,
FA4F2C121DE936FE00CA37D7 /* unixtcp.c in Sources */,
FA0B7EB91A95902C000E1D17 /* Channel.cpp in Sources */,
FA4B66CA1ABBCF1900558F15 /* Timer.cpp in Sources */,
FA0B7A3F1A958EA3000E1D17 /* b2ChainShape.cpp in Sources */,
FA0B7E921A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */,
FA0B7E521A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */,
FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */,
FA0B7A311A958EA3000E1D17 /* b2CollidePolygon.cpp in Sources */,
FA4F2C111DE936FE00CA37D7 /* unix.c in Sources */,
FA0B7A931A958EA3000E1D17 /* b2GearJoint.cpp in Sources */,
FA0B7E0D1A95902C000E1D17 /* Fixture.cpp in Sources */,
FA0B7D191A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */,
@@ -3520,6 +3609,7 @@
FA0B7ED21A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
FA0B7EE01A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
FA0B7A3C1A958EA3000E1D17 /* b2TimeOfImpact.cpp in Sources */,
FA4F2C081DE936DD00CA37D7 /* io.c in Sources */,
FA0B7CDD1A95902C000E1D17 /* Source.cpp in Sources */,
FA0B7DC51A95902C000E1D17 /* wrap_JoystickModule.cpp in Sources */,
FA0B7E701A95902C000E1D17 /* wrap_RopeJoint.cpp in Sources */,
@@ -3534,6 +3624,7 @@
FA0B7D681A95902C000E1D17 /* wrap_ParticleSystem.cpp in Sources */,
FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */,
FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */,
FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */,
FA0B7D651A95902C000E1D17 /* wrap_Mesh.cpp in Sources */,
FA0B7A531A958EA3000E1D17 /* b2Math.cpp in Sources */,
FA0B7CDA1A95902C000E1D17 /* Pool.cpp in Sources */,
@@ -3560,13 +3651,16 @@
FA0B7A871A958EA3000E1D17 /* b2PolygonAndCircleContact.cpp in Sources */,
FA0B7EF21A959D2C000E1D17 /* ios.mm in Sources */,
FA0B7D3A1A95902C000E1D17 /* Graphics.cpp in Sources */,
FA4F2BB21DE1E4B400CA37D7 /* RecordingDevice.cpp in Sources */,
FA0B7A2D1A958EA3000E1D17 /* b2CollideCircle.cpp in Sources */,
FA4F2C0F1DE936FE00CA37D7 /* timeout.c in Sources */,
FA59A2D31C06481400328DBA /* ParticleSystem.cpp in Sources */,
FA0B7E131A95902C000E1D17 /* GearJoint.cpp in Sources */,
FA0B7DC21A95902C000E1D17 /* wrap_Joystick.cpp in Sources */,
FA0B7CD41A95902C000E1D17 /* Source.cpp in Sources */,
FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */,
FA0B7CD11A95902C000E1D17 /* Audio.cpp in Sources */,
FA4F2B7B1DE0181B00CA37D7 /* xxhash.c in Sources */,
FA0B7D131A95902C000E1D17 /* Font.cpp in Sources */,
FA0B7EC91A95902C000E1D17 /* threads.cpp in Sources */,
FA0B7A781A958EA3000E1D17 /* b2CircleContact.cpp in Sources */,
@@ -3591,13 +3685,19 @@
FA0B7A7E1A958EA3000E1D17 /* b2ContactSolver.cpp in Sources */,
FA0B7D971A95902C000E1D17 /* ImageData.cpp in Sources */,
FA0B7E581A95902C000E1D17 /* wrap_Joint.cpp in Sources */,
FA4F2C0A1DE936E600CA37D7 /* mime.c in Sources */,
FA0B7E311A95902C000E1D17 /* Shape.cpp in Sources */,
FA0B7E491A95902C000E1D17 /* wrap_DistanceJoint.cpp in Sources */,
FA0B7A391A958EA3000E1D17 /* b2DynamicTree.cpp in Sources */,
FA4F2C101DE936FE00CA37D7 /* udp.c in Sources */,
FA0B7A681A958EA3000E1D17 /* b2Island.cpp in Sources */,
FA0B7E2B1A95902C000E1D17 /* RevoluteJoint.cpp in Sources */,
FA0B7B291A958EA3000E1D17 /* simplexnoise1234.cpp in Sources */,
FA0B7D261A95902C000E1D17 /* wrap_Font.cpp in Sources */,
FA4F2BB31DE1E4B800CA37D7 /* RecordingDevice.cpp in Sources */,
FA4F2C071DE936DA00CA37D7 /* inet.c in Sources */,
FA4F2C0B1DE936EA00CA37D7 /* options.c in Sources */,
FA4F2C0D1DE936F100CA37D7 /* serial.c in Sources */,
FA0B7E0A1A95902C000E1D17 /* EdgeShape.cpp in Sources */,
FAB17BF11ABFB37500F9BA27 /* wrap_CompressedData.cpp in Sources */,
FA0B7CF81A95902C000E1D17 /* FileData.cpp in Sources */,
@@ -3605,6 +3705,7 @@
FA0B7E981A95902C000E1D17 /* Sound.cpp in Sources */,
FA0B7E371A95902C000E1D17 /* WheelJoint.cpp in Sources */,
FA0B7A8A1A958EA3000E1D17 /* b2PolygonContact.cpp in Sources */,
FA4F2C0C1DE936ED00CA37D7 /* select.c in Sources */,
FA0B7D8E1A95902C000E1D17 /* ddsHandler.cpp in Sources */,
FA0B7DFE1A95902C000E1D17 /* ChainShape.cpp in Sources */,
FA0B7A451A958EA3000E1D17 /* b2EdgeShape.cpp in Sources */,
@@ -3629,6 +3730,7 @@
FA0B7CE31A95902C000E1D17 /* wrap_Audio.cpp in Sources */,
FA0B7B381A958EA3000E1D17 /* wuff_internal.c in Sources */,
FA0B7DF81A95902C000E1D17 /* Body.cpp in Sources */,
FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */,
FA0B7DF51A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,
FA0B7D6E1A95902C000E1D17 /* wrap_Shader.cpp in Sources */,
FA0B7E861A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */,
@@ -3647,12 +3749,14 @@
FA0B7E1C1A95902C000E1D17 /* MouseJoint.cpp in Sources */,
FA0B7CF51A95902C000E1D17 /* File.cpp in Sources */,
FA0B7E341A95902C000E1D17 /* WeldJoint.cpp in Sources */,
FA4F2C091DE936E200CA37D7 /* luasocket.c in Sources */,
FA0B7D5C1A95902C000E1D17 /* wrap_Font.cpp in Sources */,
FA0B7B221A958EA3000E1D17 /* luasocket.cpp in Sources */,
FA0B7D311A95902C000E1D17 /* Graphics.cpp in Sources */,
FA0B7E9E1A95902C000E1D17 /* WaveDecoder.cpp in Sources */,
FA0B7EB31A95902C000E1D17 /* System.cpp in Sources */,
FA0B7D1C1A95902C000E1D17 /* GlyphData.cpp in Sources */,
FA4F2C061DE936CD00CA37D7 /* except.c in Sources */,
FA0B7AAE1A958EA3000E1D17 /* b2WheelJoint.cpp in Sources */,
FA0B7E671A95902C000E1D17 /* wrap_PrismaticJoint.cpp in Sources */,
FA0B7DCE1A95902C000E1D17 /* wrap_Keyboard.cpp in Sources */,
@@ -3668,6 +3772,7 @@
FA620A3B1AA305F6005DB4C2 /* types.cpp in Sources */,
FA0B7DD41A95902C000E1D17 /* BezierCurve.cpp in Sources */,
FA0B7E7C1A95902C000E1D17 /* wrap_World.cpp in Sources */,
FA4F2C0E1DE936FE00CA37D7 /* tcp.c in Sources */,
FA0B7D911A95902C000E1D17 /* FormatHandler.cpp in Sources */,
FA0B7D431A95902C000E1D17 /* OpenGL.cpp in Sources */,
FA0B7DBF1A95902C000E1D17 /* JoystickModule.cpp in Sources */,
@@ -3684,6 +3789,7 @@
FA0B7E251A95902C000E1D17 /* PrismaticJoint.cpp in Sources */,
FA0B7A561A958EA3000E1D17 /* b2Settings.cpp in Sources */,
FA0B7E611A95902C000E1D17 /* wrap_Physics.cpp in Sources */,
FA4F2C041DE936C600CA37D7 /* buffer.c in Sources */,
FA0B7DC81A95902C000E1D17 /* Keyboard.cpp in Sources */,
FA4943551D9DDFFE00E8D38A /* HashFunction.cpp in Sources */,
FA0B7EAD1A95902C000E1D17 /* wrap_SoundData.cpp in Sources */,
@@ -3695,6 +3801,7 @@
FA0B7DE61A95902C000E1D17 /* Cursor.cpp in Sources */,
FA0B7EDC1A95902D000E1D17 /* Touch.cpp in Sources */,
FA0B7CE91A95902C000E1D17 /* Event.cpp in Sources */,
FA4F2C131DE936FE00CA37D7 /* unixudp.c in Sources */,
FA0B7A961A958EA3000E1D17 /* b2Joint.cpp in Sources */,
FA0B7A621A958EA3000E1D17 /* b2ContactManager.cpp in Sources */,
FA0B7A2F1A958EA3000E1D17 /* b2CollideEdge.cpp in Sources */,
@@ -3750,7 +3857,9 @@
FA0B7E631A95902C000E1D17 /* wrap_PolygonShape.cpp in Sources */,
FA0B7E721A95902C000E1D17 /* wrap_Shape.cpp in Sources */,
FA0B7CFD1A95902C000E1D17 /* File.cpp in Sources */,
FA4F2BB01DE1E37B00CA37D7 /* RecordingDevice.cpp in Sources */,
FA27B39D1B498151008A9DCE /* Video.cpp in Sources */,
FA4F2BAC1DE1E37000CA37D7 /* RecordingDevice.cpp in Sources */,
FA0B7A2E1A958EA3000E1D17 /* b2CollideEdge.cpp in Sources */,
FA0B7E691A95902C000E1D17 /* wrap_PulleyJoint.cpp in Sources */,
FA0B7DB71A95902C000E1D17 /* Joystick.cpp in Sources */,
@@ -3806,6 +3915,7 @@
FA0B7EDF1A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
FA0B794A1A958E3B000E1D17 /* wrap_Data.cpp in Sources */,
217DFBFB1D9F6D490055D849 /* serial.c in Sources */,
FA4F2BE51DE6650600CA37D7 /* wrap_Transform.cpp in Sources */,
217DFC0D1D9F6D490055D849 /* unixudp.c in Sources */,
FA0B7CDC1A95902C000E1D17 /* Source.cpp in Sources */,
FA0B7DC41A95902C000E1D17 /* wrap_JoystickModule.cpp in Sources */,
@@ -3894,6 +4004,7 @@
FA0B7DA51A95902C000E1D17 /* PNGHandler.cpp in Sources */,
FA0B7B371A958EA3000E1D17 /* wuff_internal.c in Sources */,
FA0B7E971A95902C000E1D17 /* Sound.cpp in Sources */,
FA4F2B791DE0125B00CA37D7 /* xxhash.c in Sources */,
FA0B7E361A95902C000E1D17 /* WheelJoint.cpp in Sources */,
FA0B7A471A958EA3000E1D17 /* b2PolygonShape.cpp in Sources */,
FA0B7D8D1A95902C000E1D17 /* ddsHandler.cpp in Sources */,
@@ -3906,6 +4017,7 @@
FA0B7A981A958EA3000E1D17 /* b2MotorJoint.cpp in Sources */,
FA0B7E061A95902C000E1D17 /* DistanceJoint.cpp in Sources */,
FA620A321AA2F8DB005DB4C2 /* wrap_Quad.cpp in Sources */,
FA4F2BA61DE1E36400CA37D7 /* RecordingDevice.cpp in Sources */,
FA0B793B1A958E3B000E1D17 /* runtime.cpp in Sources */,
FA0B7E5D1A95902C000E1D17 /* wrap_MouseJoint.cpp in Sources */,
FA0B7A741A958EA3000E1D17 /* b2ChainAndPolygonContact.cpp in Sources */,
@@ -3988,6 +4100,7 @@
FA0B7CDF1A95902C000E1D17 /* Source.cpp in Sources */,
FA0B7ECE1A95902C000E1D17 /* wrap_LuaThread.cpp in Sources */,
FA0B79431A958E3B000E1D17 /* Variant.cpp in Sources */,
FA4F2BE31DE6650600CA37D7 /* Transform.cpp in Sources */,
FA0B7EA01A95902C000E1D17 /* Sound.cpp in Sources */,
FA0B7DE51A95902C000E1D17 /* Cursor.cpp in Sources */,
FA0B7EDB1A95902D000E1D17 /* Touch.cpp in Sources */,
@@ -3998,6 +4111,7 @@
FA0B7A891A958EA3000E1D17 /* b2PolygonContact.cpp in Sources */,
FA0B7E1E1A95902C000E1D17 /* Physics.cpp in Sources */,
FA0B7E811A95902C000E1D17 /* Shape.cpp in Sources */,
FA4F2BA81DE1E36400CA37D7 /* wrap_RecordingDevice.cpp in Sources */,
FA0B7B251A958EA3000E1D17 /* lutf8lib.c in Sources */,
FA0B7ED41A95902D000E1D17 /* Timer.cpp in Sources */,
FA19C4C51B4B0BD50059B0B3 /* wrap_Video.cpp in Sources */,
@@ -4323,6 +4437,7 @@
DYLIB_CURRENT_VERSION = 9.0;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks",
/Library/Frameworks,
"$(LOCAL_LIBRARY_DIR)/Frameworks",
);
@@ -4330,6 +4445,9 @@
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks/FreeType.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
/Library/Frameworks/FreeType.framework/Headers,
/Library/Frameworks/Lua.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
@@ -4357,6 +4475,7 @@
DYLIB_CURRENT_VERSION = 9.0;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks",
/Library/Frameworks,
"$(LOCAL_LIBRARY_DIR)/Frameworks",
);
@@ -4364,6 +4483,9 @@
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks/FreeType.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
/Library/Frameworks/FreeType.framework/Headers,
/Library/Frameworks/Lua.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
@@ -4392,6 +4514,7 @@
DYLIB_CURRENT_VERSION = 9.0;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks",
/Library/Frameworks,
"$(LOCAL_LIBRARY_DIR)/Frameworks",
);
@@ -4399,6 +4522,9 @@
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks/FreeType.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
/Library/Frameworks/FreeType.framework/Headers,
/Library/Frameworks/Lua.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
@@ -430,6 +430,7 @@
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks",
/Library/Frameworks,
"\"$(SRCROOT)/build/Release\"",
"\"$(SRCROOT)/build/Debug\"",
@@ -439,6 +440,8 @@
GCC_OPTIMIZATION_LEVEL = 0;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
/Library/Frameworks/Lua.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
);
@@ -458,6 +461,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks",
/Library/Frameworks,
"\"$(SRCROOT)/build/Release\"",
"\"$(SRCROOT)/build/Debug\"",
@@ -465,6 +469,8 @@
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
/Library/Frameworks/Lua.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
);
@@ -841,6 +847,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks",
/Library/Frameworks,
"\"$(SRCROOT)/build/Release\"",
"\"$(SRCROOT)/build/Debug\"",
@@ -848,6 +855,8 @@
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
/Library/Frameworks/Lua.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
);
+12
View File
@@ -73,6 +73,12 @@ Matrix4::Matrix4()
{
setIdentity();
}
Matrix4::Matrix4(const float elements[16])
{
memcpy(e, elements, sizeof(float) * 16);
}
Matrix4::Matrix4(float t00, float t10, float t01, float t11, float x, float y)
{
@@ -143,6 +149,12 @@ void Matrix4::setShear(float kx, float ky)
e[1] = ky;
e[4] = kx;
}
void Matrix4::getApproximateScale(float &sx, float &sy) const
{
sx = sqrtf(e[0] * e[0] + e[4] * e[4]);
sy = sqrtf(e[1] * e[1] + e[5] * e[5]);
}
void Matrix4::setRawTransformation(float t00, float t10, float t01, float t11, float x, float y)
{
+15
View File
@@ -52,6 +52,12 @@ public:
**/
Matrix4(float t00, float t10, float t01, float t11, float x, float y);
/**
* Creates a new matrix from the specified elements. Be sure to pass
* exactly 16 elements in!
**/
Matrix4(const float elements[16]);
/**
* Creates a new matrix set to a transformation.
**/
@@ -112,6 +118,12 @@ public:
* @param ky Shear along y-axis.
**/
void setShear(float kx, float ky);
/**
* Calculates the scale factors for a 2D affine transform. The output values
* are absolute (not signed).
**/
void getApproximateScale(float &sx, float &sy) const;
/**
* Sets a transformation's values directly. Useful if you want to modify them inplace,
@@ -182,6 +194,9 @@ public:
template <typename V>
void transform(V *dst, const V *src, int size) const;
/**
* Computes and returns the inverse of the matrix.
**/
Matrix4 inverse() const;
/**
+889
View File
@@ -0,0 +1,889 @@
/*
* xxHash - Fast Hash algorithm
* Copyright (C) 2012-2016, Yann Collet
*
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - xxHash homepage: http://www.xxhash.com
* - xxHash source repository : https://github.com/Cyan4973/xxHash
*/
/* *************************************
* Tuning parameters
***************************************/
/*!XXH_FORCE_MEMORY_ACCESS :
* By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
* Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
* The below switch allow to select different access method for improved performance.
* Method 0 (default) : use `memcpy()`. Safe and portable.
* Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
* This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
* Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
* It can generate buggy code on targets which do not support unaligned memory accesses.
* But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
* See http://stackoverflow.com/a/32095106/646947 for details.
* Prefer these methods in priority order (0 > 1 > 2)
*/
#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
# define XXH_FORCE_MEMORY_ACCESS 2
# elif defined(__INTEL_COMPILER) || \
(defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
# define XXH_FORCE_MEMORY_ACCESS 1
# endif
#endif
/*!XXH_ACCEPT_NULL_INPUT_POINTER :
* If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
* When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
* By default, this option is disabled. To enable it, uncomment below define :
*/
/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */
/*!XXH_FORCE_NATIVE_FORMAT :
* By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
* Results are therefore identical for little-endian and big-endian CPU.
* This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
* Should endian-independence be of no importance for your application, you may set the #define below to 1,
* to improve speed for Big-endian CPU.
* This option has no impact on Little_Endian CPU.
*/
#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */
# define XXH_FORCE_NATIVE_FORMAT 0
#endif
/*!XXH_FORCE_ALIGN_CHECK :
* This is a minor performance trick, only useful with lots of very small keys.
* It means : check for aligned/unaligned input.
* The check costs one initial branch per hash; set to 0 when the input data
* is guaranteed to be aligned.
*/
#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
# define XXH_FORCE_ALIGN_CHECK 0
# else
# define XXH_FORCE_ALIGN_CHECK 1
# endif
#endif
/* *************************************
* Includes & Memory related functions
***************************************/
/*! Modify the local functions below should you wish to use some other memory routines
* for malloc(), free() */
#include <stdlib.h>
static void* XXH_malloc(size_t s) { return malloc(s); }
static void XXH_free (void* p) { free(p); }
/*! and for memcpy() */
#include <string.h>
static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
#define XXH_STATIC_LINKING_ONLY
#include "xxhash.h"
/* *************************************
* Compiler Specific Options
***************************************/
#ifdef _MSC_VER /* Visual Studio */
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
# define FORCE_INLINE static __forceinline
#else
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
# ifdef __GNUC__
# define FORCE_INLINE static inline __attribute__((always_inline))
# else
# define FORCE_INLINE static inline
# endif
# else
# define FORCE_INLINE static
# endif /* __STDC_VERSION__ */
#endif
/* *************************************
* Basic Types
***************************************/
#ifndef MEM_MODULE
# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
# include <stdint.h>
typedef uint8_t BYTE;
typedef uint16_t U16;
typedef uint32_t U32;
typedef int32_t S32;
# else
typedef unsigned char BYTE;
typedef unsigned short U16;
typedef unsigned int U32;
typedef signed int S32;
# endif
#endif
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
/* currently only defined for gcc and icc */
typedef union { U32 u32; } __attribute__((packed)) unalign;
static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
#else
/* portable and safe solution. Generally efficient.
* see : http://stackoverflow.com/a/32095106/646947
*/
static U32 XXH_read32(const void* memPtr)
{
U32 val;
memcpy(&val, memPtr, sizeof(val));
return val;
}
#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
/* ****************************************
* Compiler-specific Functions and Macros
******************************************/
#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
#if defined(_MSC_VER)
# define XXH_rotl32(x,r) _rotl(x,r)
# define XXH_rotl64(x,r) _rotl64(x,r)
#else
# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
#endif
#if defined(_MSC_VER) /* Visual Studio */
# define XXH_swap32 _byteswap_ulong
#elif XXH_GCC_VERSION >= 403
# define XXH_swap32 __builtin_bswap32
#else
static U32 XXH_swap32 (U32 x)
{
return ((x << 24) & 0xff000000 ) |
((x << 8) & 0x00ff0000 ) |
((x >> 8) & 0x0000ff00 ) |
((x >> 24) & 0x000000ff );
}
#endif
/* *************************************
* Architecture Macros
***************************************/
typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
#ifndef XXH_CPU_LITTLE_ENDIAN
static const int g_one = 1;
# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one))
#endif
/* ***************************
* Memory reads
*****************************/
typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
{
if (align==XXH_unaligned)
return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
else
return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
}
FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
{
return XXH_readLE32_align(ptr, endian, XXH_unaligned);
}
static U32 XXH_readBE32(const void* ptr)
{
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
}
/* *************************************
* Macros
***************************************/
#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
/* *******************************************************************
* 32-bits hash functions
*********************************************************************/
static const U32 PRIME32_1 = 2654435761U;
static const U32 PRIME32_2 = 2246822519U;
static const U32 PRIME32_3 = 3266489917U;
static const U32 PRIME32_4 = 668265263U;
static const U32 PRIME32_5 = 374761393U;
static U32 XXH32_round(U32 seed, U32 input)
{
seed += input * PRIME32_2;
seed = XXH_rotl32(seed, 13);
seed *= PRIME32_1;
return seed;
}
FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
{
const BYTE* p = (const BYTE*)input;
const BYTE* bEnd = p + len;
U32 h32;
#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (p==NULL) {
len=0;
bEnd=p=(const BYTE*)(size_t)16;
}
#endif
if (len>=16) {
const BYTE* const limit = bEnd - 16;
U32 v1 = seed + PRIME32_1 + PRIME32_2;
U32 v2 = seed + PRIME32_2;
U32 v3 = seed + 0;
U32 v4 = seed - PRIME32_1;
do {
v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
} while (p<=limit);
h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
} else {
h32 = seed + PRIME32_5;
}
h32 += (U32) len;
while (p+4<=bEnd) {
h32 += XXH_get32bits(p) * PRIME32_3;
h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
p+=4;
}
while (p<bEnd) {
h32 += (*p) * PRIME32_5;
h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
p++;
}
h32 ^= h32 >> 15;
h32 *= PRIME32_2;
h32 ^= h32 >> 13;
h32 *= PRIME32_3;
h32 ^= h32 >> 16;
return h32;
}
XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
{
#if 0
/* Simple version, good for code maintenance, but unfortunately slow for small inputs */
XXH32_state_t state;
XXH32_reset(&state, seed);
XXH32_update(&state, input, len);
return XXH32_digest(&state);
#else
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if (XXH_FORCE_ALIGN_CHECK) {
if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
else
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
} }
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
else
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
#endif
}
/*====== Hash streaming ======*/
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
{
return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
}
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
{
XXH_free(statePtr);
return XXH_OK;
}
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
{
memcpy(dstState, srcState, sizeof(*dstState));
}
XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
{
XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
memset(&state, 0, sizeof(state)-4); /* do not write into reserved, for future removal */
state.v1 = seed + PRIME32_1 + PRIME32_2;
state.v2 = seed + PRIME32_2;
state.v3 = seed + 0;
state.v4 = seed - PRIME32_1;
memcpy(statePtr, &state, sizeof(state));
return XXH_OK;
}
FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
{
const BYTE* p = (const BYTE*)input;
const BYTE* const bEnd = p + len;
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (input==NULL) return XXH_ERROR;
#endif
state->total_len_32 += (unsigned)len;
state->large_len |= (len>=16) | (state->total_len_32>=16);
if (state->memsize + len < 16) { /* fill in tmp buffer */
XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
state->memsize += (unsigned)len;
return XXH_OK;
}
if (state->memsize) { /* some data left from previous update */
XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
{ const U32* p32 = state->mem32;
state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;
state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;
state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;
state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++;
}
p += 16-state->memsize;
state->memsize = 0;
}
if (p <= bEnd-16) {
const BYTE* const limit = bEnd - 16;
U32 v1 = state->v1;
U32 v2 = state->v2;
U32 v3 = state->v3;
U32 v4 = state->v4;
do {
v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;
v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;
v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;
v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;
} while (p<=limit);
state->v1 = v1;
state->v2 = v2;
state->v3 = v3;
state->v4 = v4;
}
if (p < bEnd) {
XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
state->memsize = (unsigned)(bEnd-p);
}
return XXH_OK;
}
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
else
return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
}
FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
{
const BYTE * p = (const BYTE*)state->mem32;
const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;
U32 h32;
if (state->large_len) {
h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
} else {
h32 = state->v3 /* == seed */ + PRIME32_5;
}
h32 += state->total_len_32;
while (p+4<=bEnd) {
h32 += XXH_readLE32(p, endian) * PRIME32_3;
h32 = XXH_rotl32(h32, 17) * PRIME32_4;
p+=4;
}
while (p<bEnd) {
h32 += (*p) * PRIME32_5;
h32 = XXH_rotl32(h32, 11) * PRIME32_1;
p++;
}
h32 ^= h32 >> 15;
h32 *= PRIME32_2;
h32 ^= h32 >> 13;
h32 *= PRIME32_3;
h32 ^= h32 >> 16;
return h32;
}
XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_digest_endian(state_in, XXH_littleEndian);
else
return XXH32_digest_endian(state_in, XXH_bigEndian);
}
/*====== Canonical representation ======*/
/*! Default XXH result types are basic unsigned 32 and 64 bits.
* The canonical representation follows human-readable write convention, aka big-endian (large digits first).
* These functions allow transformation of hash result into and from its canonical format.
* This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs.
*/
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
{
XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
memcpy(dst, &hash, sizeof(*dst));
}
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
{
return XXH_readBE32(src);
}
#ifndef XXH_NO_LONG_LONG
/* *******************************************************************
* 64-bits hash functions
*********************************************************************/
/*====== Memory access ======*/
#ifndef MEM_MODULE
# define MEM_MODULE
# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
# include <stdint.h>
typedef uint64_t U64;
# else
typedef unsigned long long U64; /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */
# endif
#endif
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
/* currently only defined for gcc and icc */
typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;
static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }
#else
/* portable and safe solution. Generally efficient.
* see : http://stackoverflow.com/a/32095106/646947
*/
static U64 XXH_read64(const void* memPtr)
{
U64 val;
memcpy(&val, memPtr, sizeof(val));
return val;
}
#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
#if defined(_MSC_VER) /* Visual Studio */
# define XXH_swap64 _byteswap_uint64
#elif XXH_GCC_VERSION >= 403
# define XXH_swap64 __builtin_bswap64
#else
static U64 XXH_swap64 (U64 x)
{
return ((x << 56) & 0xff00000000000000ULL) |
((x << 40) & 0x00ff000000000000ULL) |
((x << 24) & 0x0000ff0000000000ULL) |
((x << 8) & 0x000000ff00000000ULL) |
((x >> 8) & 0x00000000ff000000ULL) |
((x >> 24) & 0x0000000000ff0000ULL) |
((x >> 40) & 0x000000000000ff00ULL) |
((x >> 56) & 0x00000000000000ffULL);
}
#endif
FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
{
if (align==XXH_unaligned)
return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
else
return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
}
FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
{
return XXH_readLE64_align(ptr, endian, XXH_unaligned);
}
static U64 XXH_readBE64(const void* ptr)
{
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
}
/*====== xxh64 ======*/
static const U64 PRIME64_1 = 11400714785074694791ULL;
static const U64 PRIME64_2 = 14029467366897019727ULL;
static const U64 PRIME64_3 = 1609587929392839161ULL;
static const U64 PRIME64_4 = 9650029242287828579ULL;
static const U64 PRIME64_5 = 2870177450012600261ULL;
static U64 XXH64_round(U64 acc, U64 input)
{
acc += input * PRIME64_2;
acc = XXH_rotl64(acc, 31);
acc *= PRIME64_1;
return acc;
}
static U64 XXH64_mergeRound(U64 acc, U64 val)
{
val = XXH64_round(0, val);
acc ^= val;
acc = acc * PRIME64_1 + PRIME64_4;
return acc;
}
FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
{
const BYTE* p = (const BYTE*)input;
const BYTE* bEnd = p + len;
U64 h64;
#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (p==NULL) {
len=0;
bEnd=p=(const BYTE*)(size_t)32;
}
#endif
if (len>=32) {
const BYTE* const limit = bEnd - 32;
U64 v1 = seed + PRIME64_1 + PRIME64_2;
U64 v2 = seed + PRIME64_2;
U64 v3 = seed + 0;
U64 v4 = seed - PRIME64_1;
do {
v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
} while (p<=limit);
h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
h64 = XXH64_mergeRound(h64, v1);
h64 = XXH64_mergeRound(h64, v2);
h64 = XXH64_mergeRound(h64, v3);
h64 = XXH64_mergeRound(h64, v4);
} else {
h64 = seed + PRIME64_5;
}
h64 += (U64) len;
while (p+8<=bEnd) {
U64 const k1 = XXH64_round(0, XXH_get64bits(p));
h64 ^= k1;
h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
p+=8;
}
if (p+4<=bEnd) {
h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;
h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
p+=4;
}
while (p<bEnd) {
h64 ^= (*p) * PRIME64_5;
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
p++;
}
h64 ^= h64 >> 33;
h64 *= PRIME64_2;
h64 ^= h64 >> 29;
h64 *= PRIME64_3;
h64 ^= h64 >> 32;
return h64;
}
XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
{
#if 0
/* Simple version, good for code maintenance, but unfortunately slow for small inputs */
XXH64_state_t state;
XXH64_reset(&state, seed);
XXH64_update(&state, input, len);
return XXH64_digest(&state);
#else
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if (XXH_FORCE_ALIGN_CHECK) {
if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
else
return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
} }
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
else
return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
#endif
}
/*====== Hash Streaming ======*/
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
{
return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
}
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
{
XXH_free(statePtr);
return XXH_OK;
}
XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
{
memcpy(dstState, srcState, sizeof(*dstState));
}
XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
{
XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
memset(&state, 0, sizeof(state)-8); /* do not write into reserved, for future removal */
state.v1 = seed + PRIME64_1 + PRIME64_2;
state.v2 = seed + PRIME64_2;
state.v3 = seed + 0;
state.v4 = seed - PRIME64_1;
memcpy(statePtr, &state, sizeof(state));
return XXH_OK;
}
FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
{
const BYTE* p = (const BYTE*)input;
const BYTE* const bEnd = p + len;
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (input==NULL) return XXH_ERROR;
#endif
state->total_len += len;
if (state->memsize + len < 32) { /* fill in tmp buffer */
XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
state->memsize += (U32)len;
return XXH_OK;
}
if (state->memsize) { /* tmp buffer is full */
XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));
state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));
state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));
state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));
p += 32-state->memsize;
state->memsize = 0;
}
if (p+32 <= bEnd) {
const BYTE* const limit = bEnd - 32;
U64 v1 = state->v1;
U64 v2 = state->v2;
U64 v3 = state->v3;
U64 v4 = state->v4;
do {
v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;
v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;
v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;
v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;
} while (p<=limit);
state->v1 = v1;
state->v2 = v2;
state->v3 = v3;
state->v4 = v4;
}
if (p < bEnd) {
XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
state->memsize = (unsigned)(bEnd-p);
}
return XXH_OK;
}
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
else
return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
}
FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
{
const BYTE * p = (const BYTE*)state->mem64;
const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;
U64 h64;
if (state->total_len >= 32) {
U64 const v1 = state->v1;
U64 const v2 = state->v2;
U64 const v3 = state->v3;
U64 const v4 = state->v4;
h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
h64 = XXH64_mergeRound(h64, v1);
h64 = XXH64_mergeRound(h64, v2);
h64 = XXH64_mergeRound(h64, v3);
h64 = XXH64_mergeRound(h64, v4);
} else {
h64 = state->v3 + PRIME64_5;
}
h64 += (U64) state->total_len;
while (p+8<=bEnd) {
U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian));
h64 ^= k1;
h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
p+=8;
}
if (p+4<=bEnd) {
h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;
h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
p+=4;
}
while (p<bEnd) {
h64 ^= (*p) * PRIME64_5;
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
p++;
}
h64 ^= h64 >> 33;
h64 *= PRIME64_2;
h64 ^= h64 >> 29;
h64 *= PRIME64_3;
h64 ^= h64 >> 32;
return h64;
}
XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_digest_endian(state_in, XXH_littleEndian);
else
return XXH64_digest_endian(state_in, XXH_bigEndian);
}
/*====== Canonical representation ======*/
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
{
XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
memcpy(dst, &hash, sizeof(*dst));
}
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
{
return XXH_readBE64(src);
}
#endif /* XXH_NO_LONG_LONG */
+293
View File
@@ -0,0 +1,293 @@
/*
xxHash - Extremely Fast Hash algorithm
Header File
Copyright (C) 2012-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- xxHash source repository : https://github.com/Cyan4973/xxHash
*/
/* Notice extracted from xxHash homepage :
xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
It also successfully passes all tests from the SMHasher suite.
Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
Name Speed Q.Score Author
xxHash 5.4 GB/s 10
CrapWow 3.2 GB/s 2 Andrew
MumurHash 3a 2.7 GB/s 10 Austin Appleby
SpookyHash 2.0 GB/s 10 Bob Jenkins
SBox 1.4 GB/s 9 Bret Mulvey
Lookup3 1.2 GB/s 9 Bob Jenkins
SuperFastHash 1.2 GB/s 1 Paul Hsieh
CityHash64 1.05 GB/s 10 Pike & Alakuijala
FNV 0.55 GB/s 5 Fowler, Noll, Vo
CRC32 0.43 GB/s 9
MD5-32 0.33 GB/s 10 Ronald L. Rivest
SHA1-32 0.28 GB/s 10
Q.Score is a measure of quality of the hash function.
It depends on successfully passing SMHasher test set.
10 is a perfect score.
A 64-bits version, named XXH64, is available since r35.
It offers much better speed, but for 64-bits applications only.
Name Speed on 64 bits Speed on 32 bits
XXH64 13.8 GB/s 1.9 GB/s
XXH32 6.8 GB/s 6.0 GB/s
*/
#ifndef XXHASH_H_5627135585666179
#define XXHASH_H_5627135585666179 1
#if defined (__cplusplus)
extern "C" {
#endif
/* ****************************
* Definitions
******************************/
#include <stddef.h> /* size_t */
typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
/* ****************************
* API modifier
******************************/
/** XXH_PRIVATE_API
* This is useful to include xxhash functions in `static` mode
* in order to inline them, and remove their symbol from the public list.
* Methodology :
* #define XXH_PRIVATE_API
* #include "xxhash.h"
* `xxhash.c` is automatically included.
* It's not useful to compile and link it as a separate module.
*/
#ifdef XXH_PRIVATE_API
# ifndef XXH_STATIC_LINKING_ONLY
# define XXH_STATIC_LINKING_ONLY
# endif
# if defined(__GNUC__)
# define XXH_PUBLIC_API static __inline __attribute__((unused))
# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# define XXH_PUBLIC_API static inline
# elif defined(_MSC_VER)
# define XXH_PUBLIC_API static __inline
# else
# define XXH_PUBLIC_API static /* this version may generate warnings for unused static functions; disable the relevant warning */
# endif
#else
# define XXH_PUBLIC_API /* do nothing */
#endif /* XXH_PRIVATE_API */
/*!XXH_NAMESPACE, aka Namespace Emulation :
If you want to include _and expose_ xxHash functions from within your own library,
but also want to avoid symbol collisions with other libraries which may also include xxHash,
you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).
Note that no change is required within the calling program as long as it includes `xxhash.h` :
regular symbol name will be automatically translated by this header.
*/
#ifdef XXH_NAMESPACE
# define XXH_CAT(A,B) A##B
# define XXH_NAME2(A,B) XXH_CAT(A,B)
# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
#endif
/* *************************************
* Version
***************************************/
#define XXH_VERSION_MAJOR 0
#define XXH_VERSION_MINOR 6
#define XXH_VERSION_RELEASE 2
#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
XXH_PUBLIC_API unsigned XXH_versionNumber (void);
/*-**********************************************************************
* 32-bits hash
************************************************************************/
typedef unsigned int XXH32_hash_t;
/*! XXH32() :
Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input".
The memory between input & input+length must be valid (allocated and read-accessible).
"seed" can be used to alter the result predictably.
Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */
XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
/*====== Streaming ======*/
typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed);
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
/*
These functions generate the xxHash of an input provided in multiple segments.
Note that, for small input, they are slower than single-call functions, due to state management.
For small input, prefer `XXH32()` and `XXH64()` .
XXH state must first be allocated, using XXH*_createState() .
Start a new hash by initializing state with a seed, using XXH*_reset().
Then, feed the hash state by calling XXH*_update() as many times as necessary.
Obviously, input must be allocated and read accessible.
The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
Finally, a hash value can be produced anytime, by using XXH*_digest().
This function returns the nn-bits hash as an int or long long.
It's still possible to continue inserting input into the hash state after a digest,
and generate some new hashes later on, by calling again XXH*_digest().
When done, free XXH state space if it was allocated dynamically.
*/
/*====== Canonical representation ======*/
typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
* The canonical representation uses human-readable write convention, aka big-endian (large digits first).
* These functions allow transformation of hash result into and from its canonical format.
* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
*/
#ifndef XXH_NO_LONG_LONG
/*-**********************************************************************
* 64-bits hash
************************************************************************/
typedef unsigned long long XXH64_hash_t;
/*! XXH64() :
Calculate the 64-bits hash of sequence of length "len" stored at memory address "input".
"seed" can be used to alter the result predictably.
This function runs faster on 64-bits systems, but slower on 32-bits systems (see benchmark).
*/
XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
/*====== Streaming ======*/
typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
/*====== Canonical representation ======*/
typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
#endif /* XXH_NO_LONG_LONG */
#ifdef XXH_STATIC_LINKING_ONLY
/* ================================================================================================
This section contains definitions which are not guaranteed to remain stable.
They may change in future versions, becoming incompatible with a different version of the library.
They shall only be used with static linking.
Never use these definitions in association with dynamic linking !
=================================================================================================== */
/* These definitions are only meant to make possible
static allocation of XXH state, on stack or in a struct for example.
Never use members directly. */
struct XXH32_state_s {
unsigned total_len_32;
unsigned large_len;
unsigned v1;
unsigned v2;
unsigned v3;
unsigned v4;
unsigned mem32[4]; /* buffer defined as U32 for alignment */
unsigned memsize;
unsigned reserved; /* never read nor write, will be removed in a future version */
}; /* typedef'd to XXH32_state_t */
#ifndef XXH_NO_LONG_LONG /* remove 64-bits support */
struct XXH64_state_s {
unsigned long long total_len;
unsigned long long v1;
unsigned long long v2;
unsigned long long v3;
unsigned long long v4;
unsigned long long mem64[4]; /* buffer defined as U64 for alignment */
unsigned memsize;
unsigned reserved[2]; /* never read nor write, will be removed in a future version */
}; /* typedef'd to XXH64_state_t */
#endif
#ifdef XXH_PRIVATE_API
# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */
#endif
#endif /* XXH_STATIC_LINKING_ONLY */
#if defined (__cplusplus)
}
#endif
#endif /* XXHASH_H_5627135585666179 */
+3 -27
View File
@@ -28,6 +28,7 @@
#include "common/Module.h"
#include "common/StringMap.h"
#include "Source.h"
#include "RecordingDevice.h"
namespace love
{
@@ -189,34 +190,9 @@ public:
virtual float getDopplerScale() const = 0;
/**
* Begins recording audio input from the microphone.
* @return Reference to a vector of pointers to recording devices. May be empty.
**/
virtual void record() = 0;
/**
* Gets a section of recorded audio.
* Per OpenAL, the measurement begins from the start of the
* audio data in memory, which is after the last time this function
* was called. If this function has not been called yet this recording
* session, it just grabs from the beginning.
* @return All the recorded SoundData thus far.
**/
virtual love::sound::SoundData *getRecordedData() = 0;
/**
* Stops recording and, if passed true, returns all the recorded audio
* not already gotten by getRecordedData.
* @param returnData Whether to return recorded audio.
* @return if returnData, all the recorded audio yet to be gotten,
* otherwise NULL.
**/
virtual love::sound::SoundData *stopRecording(bool returnData) = 0;
/**
* Checks whether LOVE is able to record audio input.
* @return hasMic Whether LOVE has a microphone enabled.
**/
virtual bool canRecord() = 0;
virtual const std::vector<RecordingDevice*> &getRecordingDevices() = 0;
/**
* Gets the distance model used for attenuation.
+39
View File
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2006-2016 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 "RecordingDevice.h"
namespace love
{
namespace audio
{
love::Type RecordingDevice::type("RecordingDevice", &Object::type);
RecordingDevice::RecordingDevice()
{
}
RecordingDevice::~RecordingDevice()
{
}
} //audio
} //love
+104
View File
@@ -0,0 +1,104 @@
/**
* Copyright (c) 2006-2016 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 = 0; 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_AUDIO_RECORDING_DEVICE_H
#define LOVE_AUDIO_RECORDING_DEVICE_H
#include "common/Object.h"
#include "sound/SoundData.h"
#include <string>
namespace love
{
namespace audio
{
class RecordingDevice : public love::Object
{
public:
static love::Type type;
RecordingDevice();
virtual ~RecordingDevice();
/**
* Begins audio input recording process. using default (previous) parameters.
* @return True if recording started successfully.
**/
virtual bool start() = 0;
/**
* Begins audio input recording process.
* @param samples Number of samples to buffer.
* @param sampleRate Desired sample rate.
* @param bitDepth Desired bit depth (8 or 16).
* @param channels Desired number of channels.
* @return True if recording started successfully.
**/
virtual bool start(int samples, int sampleRate, int bitDepth, int channels) = 0;
/**
* Stops audio input recording.
**/
virtual void stop() = 0;
/**
* Retreives recorded data.
* @return SoundData containing data obtained from recording device.
**/
virtual love::sound::SoundData *getData() = 0;
/**
* @return C string device name.
**/
virtual const char *getName() const = 0;
/**
* @return Number of samples currently recorded.
**/
virtual int getSampleCount() const = 0;
/**
* @return Sample rate for recording.
**/
virtual int getSampleRate() const = 0;
/**
* @return Bit depth for recording.
**/
virtual int getBitDepth() const = 0;
/**
* @return Number of channels for recording.
**/
virtual int getChannels() const = 0;
/**
* @return True if currently recording.
**/
virtual bool isRecording() const = 0;
}; //RecordingDevice
} //audio
} //love
#endif //LOVE_AUDIO_RECORDING_DEVICE_H
+2 -16
View File
@@ -144,23 +144,9 @@ float Audio::getDopplerScale() const
return 1.0f;
}
void Audio::record()
const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
{
}
love::sound::SoundData *Audio::getRecordedData()
{
return NULL;
}
love::sound::SoundData *Audio::stopRecording(bool)
{
return NULL;
}
bool Audio::canRecord()
{
return false;
return capture;
}
Audio::DistanceModel Audio::getDistanceModel() const
+3 -4
View File
@@ -24,6 +24,7 @@
// LOVE
#include "audio/Audio.h"
#include "RecordingDevice.h"
#include "Source.h"
namespace love
@@ -70,10 +71,7 @@ public:
void setDopplerScale(float scale);
float getDopplerScale() const;
void record();
love::sound::SoundData *getRecordedData();
love::sound::SoundData *stopRecording(bool returnData);
bool canRecord();
const std::vector<love::audio::RecordingDevice*> &getRecordingDevices();
DistanceModel getDistanceModel() const;
void setDistanceModel(DistanceModel distanceModel);
@@ -81,6 +79,7 @@ public:
private:
float volume;
DistanceModel distanceModel;
std::vector<love::audio::RecordingDevice*> capture;
}; // Audio
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2006-2016 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 = 0; 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 "RecordingDevice.h"
#include "Audio.h"
namespace love
{
namespace audio
{
namespace null
{
const char *RecordingDevice::name = "null";
RecordingDevice::RecordingDevice(const char *)
{
}
RecordingDevice::~RecordingDevice()
{
}
bool RecordingDevice::start()
{
return false;
}
bool RecordingDevice::start(int, int, int, int)
{
return false;
}
void RecordingDevice::stop()
{
}
love::sound::SoundData *RecordingDevice::getData()
{
return nullptr;
}
int RecordingDevice::getSampleCount() const
{
return 0;
}
int RecordingDevice::getSampleRate() const
{
return 8000;
}
int RecordingDevice::getBitDepth() const
{
return 16;
}
int RecordingDevice::getChannels() const
{
return 1;
}
const char *RecordingDevice::getName() const
{
return name;
}
bool RecordingDevice::isRecording() const
{
return false;
}
} //null
} //audio
} //love
+58
View File
@@ -0,0 +1,58 @@
/**
* Copyright (c) 2006-2016 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 = 0; 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_AUDIO_NULL_RECORDING_DEVICE_H
#define LOVE_AUDIO_NULL_RECORDING_DEVICE_H
#include "audio/RecordingDevice.h"
#include "sound/SoundData.h"
namespace love
{
namespace audio
{
namespace null
{
class RecordingDevice : public love::audio::RecordingDevice
{
public:
RecordingDevice(const char *name);
virtual ~RecordingDevice();
virtual bool start();
virtual bool start(int samples, int sampleRate, int bitDepth, int channels);
virtual void stop();
virtual love::sound::SoundData *getData();
virtual const char *getName() const;
virtual int getSampleCount() const;
virtual int getSampleRate() const;
virtual int getBitDepth() const;
virtual int getChannels() const;
virtual bool isRecording() const;
private:
static const char *name;
}; //RecordingDevice
} //null
} //audio
} //love
#endif //LOVE_AUDIO_NULL_RECORDING_DEVICE_H
+98 -62
View File
@@ -20,10 +20,11 @@
#include "Audio.h"
#include "common/delay.h"
#include "RecordingDevice.h"
#include "sound/Decoder.h"
#include <cstdlib>
#include <iostream>
namespace love
{
@@ -67,9 +68,29 @@ void Audio::PoolThread::setFinish()
finish = true;
}
ALenum Audio::getFormat(int bitDepth, int channels)
{
if (bitDepth != 8 && bitDepth != 16)
return AL_NONE;
if (channels == 1)
return bitDepth == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
else if (channels == 2)
return bitDepth == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
#ifdef AL_EXT_MCFORMATS
else if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if (channels == 6)
return bitDepth == 8 ? AL_FORMAT_51CHN8 : AL_FORMAT_51CHN16;
else if (channels == 8)
return bitDepth == 8 ? AL_FORMAT_71CHN8 : AL_FORMAT_71CHN16;
}
#endif
return AL_NONE;
}
Audio::Audio()
: device(nullptr)
, capture(nullptr)
, context(nullptr)
, pool(nullptr)
, poolThread(nullptr)
@@ -89,26 +110,6 @@ Audio::Audio()
if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
throw love::Exception("Could not make context current.");
/*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
const ALCchar * devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
while (*devices)
{
std::string device(devices);
devices += device.size() + 1;
if (device.find("Mic") != std::string::npos || device.find("mic") != std::string::npos)
{
captureName = device;
}
}
capture = alcCaptureOpenDevice(captureName.c_str(), 8000, AL_FORMAT_MONO16, 262144); // about 32 seconds
if (!capture)
{
// We're not going to prevent LOVE from running without a microphone, but we should warn, at least
std::cerr << "Warning, couldn't open capture device! No audio input!" << std::endl;
}*/
// pool must be allocated after AL context.
try
{
@@ -118,8 +119,10 @@ Audio::Audio()
{
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
//if (capture) alcCaptureCloseDevice(capture);
alcCloseDevice(device);
for (auto c : capture)
delete c;
throw;
}
@@ -137,8 +140,9 @@ Audio::~Audio()
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
//if (capture) alcCaptureCloseDevice(capture);
alcCloseDevice(device);
for (auto c : capture)
delete c;
}
@@ -265,44 +269,6 @@ float Audio::getDopplerScale() const
return alGetFloat(AL_DOPPLER_FACTOR);
}
void Audio::record()
{
if (!canRecord()) return;
alcCaptureStart(capture);
}
love::sound::SoundData *Audio::getRecordedData()
{
if (!canRecord())
return NULL;
int samplerate = 8000;
ALCint samples;
alcGetIntegerv(capture, ALC_CAPTURE_SAMPLES, 4, &samples);
void *data = malloc(samples * (2/sizeof(char)));
alcCaptureSamples(capture, data, samples);
love::sound::SoundData *sd = new love::sound::SoundData(data, samples, samplerate, 16, 1);
free(data);
return sd;
}
love::sound::SoundData *Audio::stopRecording(bool returnData)
{
if (!canRecord())
return NULL;
love::sound::SoundData *sd = NULL;
if (returnData)
{
sd = getRecordedData();
}
alcCaptureStop(capture);
return sd;
}
bool Audio::canRecord()
{
return (capture != NULL);
}
Audio::DistanceModel Audio::getDistanceModel() const
{
return distanceModel;
@@ -347,6 +313,76 @@ void Audio::setDistanceModel(DistanceModel distanceModel)
}
}
const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
{
std::vector<std::string> devnames;
std::vector<love::audio::RecordingDevice*> devices;
std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
//no device name obtained from AL, fallback to reading from device
if (defaultname.length() == 0)
{
//use some safe basic parameters - 8 kHz, 8 bits, 1 channel
ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1);
if (alGetError() == AL_NO_ERROR)
{
defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER);
alcCaptureCloseDevice(defaultdevice);
}
else
{
//failed to open default recording device - bail, return empty list
capture.clear();
return capture;
}
}
devnames.reserve(capture.size());
devnames.push_back(defaultname);
//find devices name list
const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
size_t offset = 0;
while (true)
{
if (devstr[offset] == '\0')
break;
std::string str((ALCchar*)&devstr[offset]);
if (str != defaultname)
devnames.push_back(str);
offset += str.length() + 1;
}
devices.reserve(devnames.size());
//build ordered list of devices
for (int i = 0; i < (int) devnames.size(); i++)
{
devices.push_back(nullptr);
auto d = devices.end() - 1;
for (auto c : capture)
if (devnames[i] == c->getName())
*d = c;
if (*d == nullptr)
*d = new RecordingDevice(devnames[i].c_str());
else
(*d)->retain();
}
for (auto c : capture)
c->release();
capture.clear();
capture.reserve(devices.size());
//this needs to be executed in specific order
for (unsigned int i = 0; i < devnames.size(); i++)
capture.push_back(devices[i]);
return capture;
}
} // openal
} // audio
} // love
+13 -6
View File
@@ -29,6 +29,7 @@
// LOVE
#include "audio/Audio.h"
#include "audio/RecordingDevice.h"
#include "common/config.h"
#include "sound/SoundData.h"
@@ -65,6 +66,15 @@ public:
Audio();
~Audio();
/**
* Gets the OpenAL format identifier based on number of
* channels and bits.
* @param channels.
* @param bitDepth Either 8-bit samples, or 16-bit samples.
* @return One of AL_FORMAT_*, or AL_NONE if unsupported format.
**/
static ALenum getFormat(int bitDepth, int channels);
// Implements Module.
const char *getName() const;
@@ -95,10 +105,7 @@ public:
void setDopplerScale(float scale);
float getDopplerScale() const;
void record();
love::sound::SoundData *getRecordedData();
love::sound::SoundData *stopRecording(bool returnData);
bool canRecord();
const std::vector<love::audio::RecordingDevice*> &getRecordingDevices();
DistanceModel getDistanceModel() const;
void setDistanceModel(DistanceModel distanceModel);
@@ -108,8 +115,8 @@ private:
// The OpenAL device.
ALCdevice *device;
// The OpenAL capture device (microphone).
ALCdevice *capture;
// The OpenAL capture devices.
std::vector<love::audio::RecordingDevice*> capture;
// The OpenAL context.
ALCcontext *context;
@@ -0,0 +1,157 @@
/**
* Copyright (c) 2006-2016 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 = 0; 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 "RecordingDevice.h"
#include "Audio.h"
#include "sound/Sound.h"
namespace love
{
namespace audio
{
namespace openal
{
#define soundInstance() (Module::getInstance<love::sound::Sound>(Module::M_SOUND))
class InvalidFormatException : public love::Exception
{
public:
InvalidFormatException(int channels, int bitdepth)
: Exception("Recording %d channels with %d bits per sample is not supported.", channels, bitdepth)
{
}
};
RecordingDevice::RecordingDevice(const char *name)
: name(name)
{
}
RecordingDevice::~RecordingDevice()
{
if (!isRecording())
return;
alcCaptureStop(device);
alcCaptureCloseDevice(device);
}
bool RecordingDevice::start()
{
return start(samples, sampleRate, bitDepth, channels);
}
bool RecordingDevice::start(int samples, int sampleRate, int bitDepth, int channels)
{
if (isRecording())
{
alcCaptureStop(device);
alcCaptureCloseDevice(device);
}
ALenum format = Audio::getFormat(bitDepth, channels);
if (format == AL_NONE)
throw InvalidFormatException(channels, bitDepth);
if (samples <= 0)
throw love::Exception("Invalid number of samples.");
if (sampleRate <= 0)
throw love::Exception("Invalid sample rate.");
device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples);
if (device == nullptr)
return false;
alcCaptureStart(device);
this->samples = samples;
this->sampleRate = sampleRate;
this->bitDepth = bitDepth;
this->channels = channels;
return true;
}
void RecordingDevice::stop()
{
if (!isRecording())
return;
alcCaptureStop(device);
alcCaptureCloseDevice(device);
device = nullptr;
}
love::sound::SoundData *RecordingDevice::getData()
{
if (!isRecording())
return nullptr;
int samples = getSampleCount();
if (samples == 0)
return nullptr;
love::sound::SoundData *soundData = soundInstance()->newSoundData(samples, sampleRate, bitDepth, channels);
alcCaptureSamples(device, soundData->getData(), samples);
return soundData;
}
int RecordingDevice::getSampleCount() const
{
if (!isRecording())
return 0;
ALCint samples;
alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, sizeof(ALCint), &samples);
return (int)samples;
}
int RecordingDevice::getSampleRate() const
{
return sampleRate;
}
int RecordingDevice::getBitDepth() const
{
return bitDepth;
}
int RecordingDevice::getChannels() const
{
return channels;
}
const char *RecordingDevice::getName() const
{
return name.c_str();
}
bool RecordingDevice::isRecording() const
{
return device != nullptr;
}
} //openal
} //audio
} //love
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2006-2016 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 = 0; 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_AUDIO_OPENAL_RECORDING_DEVICE_H
#define LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H
#include "common/config.h"
#ifdef LOVE_APPLE_USE_FRAMEWORKS
#ifdef LOVE_IOS
#include <OpenAL/alc.h>
#include <OpenAL/al.h>
#else
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#endif
#else
#include <AL/alc.h>
#include <AL/al.h>
#endif
#include "audio/RecordingDevice.h"
#include "sound/SoundData.h"
namespace love
{
namespace audio
{
namespace openal
{
class RecordingDevice : public love::audio::RecordingDevice
{
public:
RecordingDevice(const char *name);
virtual ~RecordingDevice();
virtual bool start();
virtual bool start(int samples, int sampleRate, int bitDepth, int channels);
virtual void stop();
virtual love::sound::SoundData *getData();
virtual const char *getName() const;
virtual int getSampleCount() const;
virtual int getSampleRate() const;
virtual int getBitDepth() const;
virtual int getChannels() const;
virtual bool isRecording() const;
private:
int samples = 8192;
int sampleRate = 8000;
int bitDepth = 16;
int channels = 1;
std::string name;
ALCdevice *device = nullptr;
}; //RecordingDevice
} //openal
} //audio
} //love
#endif //LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H
+54 -55
View File
@@ -20,6 +20,7 @@
#include "Source.h"
#include "Pool.h"
#include "Audio.h"
#include "common/math.h"
// STD
@@ -119,8 +120,8 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
, channels(soundData->getChannels())
, bitDepth(soundData->getBitDepth())
{
ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth());
if (fmt == 0)
ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
if (fmt == AL_NONE)
throw InvalidFormatException(soundData->getChannels(), soundData->getBitDepth());
staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), sampleRate), Acquire::NORETAIN);
@@ -141,7 +142,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
, decoder(decoder)
, unusedBufferTop(MAX_BUFFERS - 1)
{
if (getFormat(decoder->getChannels(), decoder->getBitDepth()) == 0)
if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
alGenBuffers(MAX_BUFFERS, streamBuffers);
@@ -162,8 +163,8 @@ Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels)
, channels(channels)
, bitDepth(bitDepth)
{
ALenum fmt = getFormat(channels, bitDepth);
if (fmt == 0)
ALenum fmt = Audio::getFormat(bitDepth, channels);
if (fmt == AL_NONE)
throw InvalidFormatException(channels, bitDepth);
alGenBuffers(MAX_BUFFERS, streamBuffers);
@@ -280,10 +281,12 @@ bool Source::update()
switch (sourceType)
{
case TYPE_STATIC:
{
// Looping mode could have changed.
// FIXME: make looping mode not change without you noticing so that this is not needed
// FIXME: make looping mode change atomically so this is not needed
alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE);
return !isFinished();
}
case TYPE_STREAM:
if (!isFinished())
{
@@ -401,24 +404,26 @@ void Source::seekAtomic(float offset, void *unit)
break;
}
bool wasPlaying = isPlaying();
switch (sourceType)
{
case TYPE_STATIC:
alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
offsetSamples = offsetSeconds = 0;
if (valid)
{
alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
offsetSamples = offsetSeconds = 0;
}
break;
case TYPE_STREAM:
{
bool wasPlaying = isPlaying();
// To drain all buffers
if (valid)
stopAtomic();
stop();
decoder->seek(offsetSeconds);
if (wasPlaying)
playAtomic(source);
play();
break;
}
@@ -454,6 +459,13 @@ void Source::seekAtomic(float offset, void *unit)
case TYPE_MAX_ENUM:
break;
}
if (wasPlaying && (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && !isPlaying())))
{
stop();
if (isLooping())
play();
return;
}
this->offsetSamples = offsetSamples;
this->offsetSeconds = offsetSeconds;
}
@@ -688,7 +700,7 @@ bool Source::queueAtomic(void *data, ALsizei length)
if (buffer == AL_NONE)
return false;
alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate);
alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
alSourceQueueBuffers(source, 1, &buffer);
unusedBufferPop();
}
@@ -699,7 +711,7 @@ bool Source::queueAtomic(void *data, ALsizei length)
return false;
//stack acts as queue while stopped
alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate);
alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
unusedBufferQueue(buffer);
}
bufferedBytes += length;
@@ -734,9 +746,6 @@ void Source::prepareAtomic()
{
case TYPE_STATIC:
alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
//source can be seeked while not valid
if (offsetSamples >= 0)
alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
break;
case TYPE_STREAM:
while (unusedBufferPeek() != AL_NONE)
@@ -760,8 +769,6 @@ void Source::prepareAtomic()
for (unsigned int i = top + 1; i < MAX_BUFFERS; i++)
unusedBufferPush(unusedBuffers[i]);
if (offsetSamples >= 0)
alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
break;
}
case TYPE_MAX_ENUM:
@@ -774,7 +781,6 @@ void Source::teardownAtomic()
switch (sourceType)
{
case TYPE_STATIC:
alSourcef(source, AL_SAMPLE_OFFSET, 0);
break;
case TYPE_STREAM:
{
@@ -832,16 +838,32 @@ bool Source::playAtomic(ALuint source)
alSourcePlay(source);
// alSourcePlay may fail if the system has reached its limit of simultaneous
// playing sources.
bool success = alGetError() == AL_NO_ERROR;
valid = true; //if it fails it will be set to false again
//but this prevents a horrible, horrible bug
if (sourceType == TYPE_STREAM)
{
valid = true; //isPlaying() needs source to be valid
if (!isPlaying())
success = false;
}
else if (success)
{
alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
success = alGetError() == AL_NO_ERROR;
}
if (!success)
{
valid = true; //stop() needs source to be valid
stop();
}
if (sourceType != TYPE_STREAM)
offsetSamples = offsetSeconds = 0;
//this is set to success state afterwards anyway, but setting it here
//to true preemptively avoids race condition with update bug
valid = true;
return success;
}
@@ -862,7 +884,12 @@ void Source::pauseAtomic()
void Source::resumeAtomic()
{
if (valid && !isPlaying())
{
alSourcePlay(source);
if (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && unusedBufferTop == MAX_BUFFERS - 1))
stop();
}
}
bool Source::playAtomic(const std::vector<love::audio::Source*> &sources, const std::vector<ALuint> &ids, const std::vector<char> &wasPlaying)
@@ -941,7 +968,7 @@ void Source::pauseAtomic(const std::vector<love::audio::Source*> &sources)
void Source::reset()
{
alSourcei(source, AL_BUFFER, 0);
alSourcei(source, AL_BUFFER, AL_NONE);
alSourcefv(source, AL_POSITION, position);
alSourcefv(source, AL_VELOCITY, velocity);
alSourcefv(source, AL_DIRECTION, direction);
@@ -966,34 +993,6 @@ void Source::setFloatv(float *dst, const float *src) const
dst[2] = src[2];
}
ALenum Source::getFormat(int channels, int bitDepth) const
{
if (channels == 1 && bitDepth == 8)
return AL_FORMAT_MONO8;
else if (channels == 1 && bitDepth == 16)
return AL_FORMAT_MONO16;
else if (channels == 2 && bitDepth == 8)
return AL_FORMAT_STEREO8;
else if (channels == 2 && bitDepth == 16)
return AL_FORMAT_STEREO16;
#ifdef AL_EXT_MCFORMATS
if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if (channels == 6 && bitDepth == 8)
return AL_FORMAT_51CHN8;
else if (channels == 6 && bitDepth == 16)
return AL_FORMAT_51CHN16;
else if (channels == 8 && bitDepth == 8)
return AL_FORMAT_71CHN8;
else if (channels == 8 && bitDepth == 16)
return AL_FORMAT_71CHN16;
}
#endif
return 0;
}
ALuint Source::unusedBufferPeek()
{
return (unusedBufferTop < 0) ? AL_NONE : unusedBuffers[unusedBufferTop];
@@ -1029,9 +1028,9 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
// OpenAL implementations are allowed to ignore 0-size alBufferData calls.
if (decoded > 0)
{
int fmt = getFormat(d->getChannels(), d->getBitDepth());
int fmt = Audio::getFormat(d->getBitDepth(), d->getChannels());
if (fmt != 0)
if (fmt != AL_NONE)
alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
else
decoded = 0;
-9
View File
@@ -163,15 +163,6 @@ private:
void setFloatv(float *dst, const float *src) const;
/**
* Gets the OpenAL format identifier based on number of
* channels and bits.
* @param channels Either 1 (mono) or 2 (stereo).
* @param bitDepth Either 8-bit samples, or 16-bit samples.
* @return One of AL_FORMAT_*, or 0 if unsupported format.
**/
ALenum getFormat(int channels, int bitDepth) const;
int streamAtomic(ALuint buffer, love::sound::Decoder *d);
ALuint unusedBufferPeek();
+17 -46
View File
@@ -257,49 +257,6 @@ int w_getDopplerScale(lua_State *L)
return 1;
}
int w_record(lua_State *)
{
instance()->record();
return 0;
}
int w_getRecordedData(lua_State *L)
{
love::sound::SoundData *sd = instance()->getRecordedData();
if (!sd)
lua_pushnil(L);
else
{
luax_pushtype(L, sd);
sd->release();
}
return 1;
}
int w_stopRecording(lua_State *L)
{
if (luax_optboolean(L, 1, true))
{
love::sound::SoundData *sd = instance()->stopRecording(true);
if (!sd)
lua_pushnil(L);
else
{
luax_pushtype(L, sd);
sd->release();
}
return 1;
}
instance()->stopRecording(false);
return 0;
}
int w_canRecord(lua_State *L)
{
luax_pushboolean(L, instance()->canRecord());
return 1;
}
int w_setDistanceModel(lua_State *L)
{
const char *modelStr = luaL_checkstring(L, 1);
@@ -320,6 +277,21 @@ int w_getDistanceModel(lua_State *L)
return 1;
}
int w_getRecordingDevices(lua_State *L)
{
const std::vector<RecordingDevice*> &devices = instance()->getRecordingDevices();
lua_createtable(L, devices.size(), 0);
for (unsigned int i = 0; i < devices.size(); i++)
{
luax_pushtype(L, devices[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
@@ -339,17 +311,16 @@ static const luaL_Reg functions[] =
{ "getVelocity", w_getVelocity },
{ "setDopplerScale", w_setDopplerScale },
{ "getDopplerScale", w_getDopplerScale },
/*{ "record", w_record },
{ "getRecordedData", w_getRecordedData },
{ "stopRecording", w_stopRecording },*/
{ "setDistanceModel", w_setDistanceModel },
{ "getDistanceModel", w_getDistanceModel },
{ "getRecordingDevices", w_getRecordingDevices },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_source,
luaopen_recordingdevice,
0
};
+1
View File
@@ -26,6 +26,7 @@
#include "common/runtime.h"
#include "Audio.h"
#include "wrap_Source.h"
#include "wrap_RecordingDevice.h"
namespace love
{
+155
View File
@@ -0,0 +1,155 @@
/**
* Copyright (c) 2006-2016 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_RecordingDevice.h"
#include "wrap_Audio.h"
#include "sound/SoundData.h"
namespace love
{
namespace audio
{
RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx)
{
return luax_checktype<RecordingDevice>(L, idx);
}
int w_RecordingDevice_start(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
if (lua_gettop(L) > 1)
{
int samples = (int) luaL_checkinteger(L, 2);
int sampleRate = (int) luaL_checkinteger(L, 3);
int bitDepth = (int) luaL_checkinteger(L, 4);
int channels = (int) luaL_checkinteger(L, 5);
luax_catchexcept(L, [&](){
lua_pushboolean(L, d->start(samples, sampleRate, bitDepth, channels));
});
}
else
luax_catchexcept(L, [&](){ lua_pushboolean(L, d->start()); });
return 1;
}
int w_RecordingDevice_stop(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
love::sound::SoundData *s = nullptr;
luax_catchexcept(L, [&](){ s = d->getData(); });
d->stop();
if (s != nullptr)
{
luax_pushtype(L, s);
s->release();
}
else
lua_pushnil(L);
return 1;
}
int w_RecordingDevice_getData(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
love::sound::SoundData *s = nullptr;
luax_catchexcept(L, [&](){ s = d->getData(); });
if (s != nullptr)
{
luax_pushtype(L, s);
s->release();
}
else
lua_pushnil(L);
return 1;
}
int w_RecordingDevice_getSampleCount(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
lua_pushnumber(L, d->getSampleCount());
return 1;
}
int w_RecordingDevice_getSampleRate(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
lua_pushnumber(L, d->getSampleRate());
return 1;
}
int w_RecordingDevice_getBitDepth(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
lua_pushnumber(L, d->getBitDepth());
return 1;
}
int w_RecordingDevice_getChannels(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
lua_pushnumber(L, d->getChannels());
return 1;
}
int w_RecordingDevice_getName(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
lua_pushstring(L, d->getName());
return 1;
}
int w_RecordingDevice_isRecording(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
lua_pushboolean(L, d->isRecording());
return 1;
}
static const luaL_Reg w_RecordingDevice_functions[] =
{
{ "start", w_RecordingDevice_start },
{ "stop", w_RecordingDevice_stop },
{ "getData", w_RecordingDevice_getData },
{ "getSampleCount", w_RecordingDevice_getSampleCount },
{ "getSampleRate", w_RecordingDevice_getSampleRate },
{ "getBitDepth", w_RecordingDevice_getBitDepth },
{ "getChannels", w_RecordingDevice_getChannels },
{ "getName", w_RecordingDevice_getName },
{ "isRecording", w_RecordingDevice_isRecording },
{ 0, 0 }
};
extern "C" int luaopen_recordingdevice(lua_State *L)
{
int ret = luax_register_type(L, &RecordingDevice::type, w_RecordingDevice_functions, nullptr);
return ret;
}
} //audio
} //love
+40
View File
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2006-2016 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_AUDIO_WRAP_RECORDING_DEVICE_H
#define LOVE_AUDIO_WRAP_RECORDING_DEVICE_H
// LOVE
#include "common/runtime.h"
#include "RecordingDevice.h"
namespace love
{
namespace audio
{
RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx);
extern "C" int luaopen_recordingdevice(lua_State *L);
} // audio
} // love
#endif //LOVE_AUDIO_WRAP_RECORDING_DEVICE_H
+17
View File
@@ -112,6 +112,8 @@ Event::~Event()
void Event::pump()
{
exceptionIfInRenderPass();
SDL_Event e;
while (SDL_PollEvent(&e))
@@ -127,6 +129,8 @@ void Event::pump()
Message *Event::wait()
{
exceptionIfInRenderPass();
SDL_Event e;
if (SDL_WaitEvent(&e) != 1)
@@ -137,6 +141,8 @@ Message *Event::wait()
void Event::clear()
{
exceptionIfInRenderPass();
SDL_Event e;
while (SDL_PollEvent(&e))
@@ -147,6 +153,17 @@ void Event::clear()
love::event::Event::clear();
}
void Event::exceptionIfInRenderPass()
{
// Some core OS graphics functionality (e.g. swap buffers on some platforms)
// happens inside SDL_PumpEvents - which is called by SDL_PollEvent and
// friends. It's probably a bad idea to call those functions while we're in
// a render pass.
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr && gfx->isPassActive())
throw love::Exception("Cannot call this function while a render pass is active in love.graphics.");
}
Message *Event::convert(const SDL_Event &e) const
{
Message *msg = nullptr;
+2
View File
@@ -68,6 +68,8 @@ public:
private:
void exceptionIfInRenderPass();
Message *convert(const SDL_Event &e) const;
Message *convertJoystickEvent(const SDL_Event &e) const;
Message *convertWindowEvent(const SDL_Event &e) const;
+6 -5
View File
@@ -53,15 +53,16 @@ int w_poll(lua_State *L)
return 1;
}
int w_pump(lua_State *)
int w_pump(lua_State *L)
{
instance()->pump();
luax_catchexcept(L, [&]() { instance()->pump(); });
return 0;
}
int w_wait(lua_State *L)
{
Message *m = instance()->wait();
Message *m = nullptr;
luax_catchexcept(L, [&]() { m = instance()->wait(); });
if (m)
{
int args = m->toLua(L);
@@ -85,9 +86,9 @@ int w_push(lua_State *L)
return 1;
}
int w_clear(lua_State *)
int w_clear(lua_State *L)
{
instance()->clear();
luax_catchexcept(L, [&]() { instance()->clear(); });
return 0;
}
+3
View File
@@ -260,6 +260,8 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
{ "multicanvasformats", FEATURE_MULTI_CANVAS_FORMATS },
{ "clampzero", FEATURE_CLAMP_ZERO },
{ "lighten", FEATURE_LIGHTEN },
{ "fullnpot", FEATURE_FULL_NPOT },
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
};
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
@@ -270,6 +272,7 @@ StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::syst
{ "texturesize", LIMIT_TEXTURE_SIZE },
{ "multicanvas", LIMIT_MULTI_CANVAS },
{ "canvasmsaa", LIMIT_CANVAS_MSAA },
{ "anisotropy", LIMIT_ANISOTROPY },
};
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
+16 -1
View File
@@ -34,6 +34,8 @@ namespace love
namespace graphics
{
const int MAX_COLOR_RENDER_TARGETS = 16;
/**
* Globally sets whether gamma correction is enabled. Ideally this should be set
* prior to using any Graphics module function.
@@ -59,6 +61,14 @@ void gammaCorrectColor(Colorf &c);
**/
void unGammaCorrectColor(Colorf &c);
class RenderOutsidePassException : public love::Exception
{
public:
RenderOutsidePassException()
: Exception("Cannot draw outside of a render pass!")
{}
};
class Graphics : public Module
{
public:
@@ -143,6 +153,8 @@ public:
FEATURE_MULTI_CANVAS_FORMATS,
FEATURE_CLAMP_ZERO,
FEATURE_LIGHTEN,
FEATURE_FULL_NPOT,
FEATURE_PIXEL_SHADER_HIGHP,
FEATURE_MAX_ENUM
};
@@ -159,6 +171,7 @@ public:
LIMIT_TEXTURE_SIZE,
LIMIT_MULTI_CANVAS,
LIMIT_CANVAS_MSAA,
LIMIT_ANISOTROPY,
LIMIT_MAX_ENUM
};
@@ -180,7 +193,7 @@ public:
struct Stats
{
int drawCalls;
int canvasSwitches;
int renderPasses;
int shaderSwitches;
int canvases;
int images;
@@ -259,6 +272,8 @@ public:
**/
virtual bool isActive() const = 0;
virtual bool isPassActive() const = 0;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
+56 -409
View File
@@ -36,11 +36,10 @@ namespace opengl
static GLenum createFBO(GLuint &framebuffer, GLuint texture)
{
// get currently bound fbo to reset to it later
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
glGenFramebuffers(1, &framebuffer);
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
if (texture != 0)
{
@@ -53,14 +52,19 @@ static GLenum createFBO(GLuint &framebuffer, GLuint texture)
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
// unbind framebuffer
gl.bindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
return status;
}
static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
static bool createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
{
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
// Temporary FBO used to clear the renderbuffer.
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
@@ -73,7 +77,7 @@ static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iform
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE)
if (status == GL_FRAMEBUFFER_COMPLETE && samples > 1)
{
// Initialize the buffer to transparent black.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -83,23 +87,22 @@ static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iform
{
glDeleteRenderbuffers(1, &buffer);
buffer = 0;
samples = 0;
}
return status;
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
gl.deleteFramebuffer(fbo);
return status == GL_FRAMEBUFFER_COMPLETE && samples > 1;
}
love::Type Canvas::type("Canvas", &Texture::type);
Canvas *Canvas::current = nullptr;
OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
bool Canvas::screenHasSRGB = false;
int Canvas::canvasCount = 0;
Canvas::Canvas(int width, int height, Format format, int msaa)
: fbo(0)
, resolve_fbo(0)
, texture(0)
, msaa_buffer(0)
, depth_stencil(0)
, format(format)
, requested_samples(msaa)
, actual_samples(0)
@@ -144,65 +147,18 @@ Canvas::Canvas(int width, int height, Format format, int msaa)
Canvas::~Canvas()
{
--canvasCount;
// reset framebuffer if still using this one
if (current == this)
stopGrab();
unloadVolatile();
}
bool Canvas::createMSAAFBO(GLenum internalformat)
{
actual_samples = requested_samples;
if (actual_samples <= 1)
{
actual_samples = 0;
return false;
}
// Create our FBO without a texture.
status = createFBO(fbo, 0);
GLuint previous = gl.getDefaultFBO();
if (current != this)
{
if (current != nullptr)
previous = current->fbo;
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
}
// Create and attach the MSAA buffer for our FBO.
status = createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer);
// Create the FBO used for the MSAA resolve, and attach the texture.
if (status == GL_FRAMEBUFFER_COMPLETE)
status = createFBO(resolve_fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
// Clean up.
glDeleteFramebuffers(1, &fbo);
glDeleteFramebuffers(1, &resolve_fbo);
glDeleteRenderbuffers(1, &msaa_buffer);
fbo = msaa_buffer = resolve_fbo = 0;
actual_samples = 0;
}
if (current != this)
gl.bindFramebuffer(GL_FRAMEBUFFER, previous);
return status == GL_FRAMEBUFFER_COMPLETE;
}
bool Canvas::loadVolatile()
{
if (texture != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Canvas load");
fbo = depth_stencil = texture = 0;
resolve_fbo = msaa_buffer = 0;
fbo = texture = 0;
msaa_buffer = 0;
status = GL_FRAMEBUFFER_COMPLETE;
// glTexImage2D is guaranteed to error in this case.
@@ -240,8 +196,8 @@ bool Canvas::loadVolatile()
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0,
externalformat, textype, nullptr);
glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, externalformat,
textype, nullptr);
if (glGetError() != GL_NO_ERROR)
{
@@ -251,24 +207,27 @@ bool Canvas::loadVolatile()
return false;
}
// Try to create a MSAA FBO if requested. On failure (or no requested MSAA),
// fall back to a regular FBO.
if (!createMSAAFBO(internalformat))
status = createFBO(fbo, texture);
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (fbo != 0)
{
glDeleteFramebuffers(1, &fbo);
fbo = 0;
}
{
if (fbo != 0)
{
gl.deleteFramebuffer(fbo);
fbo = 0;
}
return false;
}
}
actual_samples = requested_samples == 1 ? 0 : requested_samples;
if (actual_samples > 0 && !createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer))
actual_samples = 0;
size_t prevmemsize = texture_memory;
texture_memory = (getFormatBitsPerPixel(format) * width * height) / 8;
texture_memory = ((getFormatBitsPerPixel(format) * width) / 8) * height;
if (msaa_buffer != 0)
texture_memory += (texture_memory * actual_samples);
@@ -279,33 +238,35 @@ bool Canvas::loadVolatile()
void Canvas::unloadVolatile()
{
glDeleteFramebuffers(1, &fbo);
glDeleteFramebuffers(1, &resolve_fbo);
if (fbo != 0)
gl.deleteFramebuffer(fbo);
glDeleteRenderbuffers(1, &depth_stencil);
glDeleteRenderbuffers(1, &msaa_buffer);
if (msaa_buffer != 0)
glDeleteRenderbuffers(1, &msaa_buffer);
gl.deleteTexture(texture);
if (texture != 0)
gl.deleteTexture(texture);
fbo = 0;
resolve_fbo = 0;
depth_stencil = 0;
msaa_buffer = 0;
texture = 0;
attachedCanvases.clear();
gl.updateTextureMemorySize(texture_memory, 0);
texture_memory = 0;
}
void Canvas::drawv(const Matrix4 &t, const Vertex *v)
{
// FIXME: This doesn't handle cases where the Canvas is used as a texture
// in a SpriteBatch, Mesh, or ParticleSystem, or when the Canvas is used in
// a shader as a non-default texture.
if (Canvas::current == this)
throw love::Exception("Cannot draw a Canvas to itself.");
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
{
const PassInfo &info = gfx->getActivePass();
for (const auto &attachment : info.colorAttachments)
{
if (attachment.canvas == this)
throw love::Exception("Cannot render a Canvas to itself!");
}
}
OpenGL::TempDebugGroup debuggroup("Canvas draw");
@@ -378,320 +339,6 @@ const void *Canvas::getHandle() const
return &texture;
}
void Canvas::setupGrab()
{
// already grabbing
if (current == this)
return;
// cleanup after previous Canvas
if (current != nullptr)
{
systemViewport = current->systemViewport;
current->stopGrab(true);
}
else
systemViewport = gl.getViewport();
// indicate we are using this Canvas.
current = this;
// bind the framebuffer object.
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
gl.setViewport({0, 0, width, height});
// Set up the projection matrix
gl.matrices.projection.push_back(Matrix4::ortho(0.0, (float) width, 0.0, (float) height));
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
{
// Whether the new canvas list is different from the old one.
// A more thorough check is done below.
bool canvaseschanged = canvases.size() != attachedCanvases.size();
bool hasSRGBcanvas = getSizedFormat(format) == FORMAT_SRGB;
if (canvases.size() > 0)
{
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1);
if (actual_samples != 0)
throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
}
bool multiformatsupported = isMultiFormatMultiCanvasSupported();
for (size_t i = 0; i < canvases.size(); i++)
{
if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
throw love::Exception("All canvases must have the same dimensions.");
Format otherformat = canvases[i]->getTextureFormat();
if (otherformat != format && !multiformatsupported)
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
if (canvases[i]->getMSAA() != 0)
throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
canvaseschanged = true;
if (getSizedFormat(otherformat) == FORMAT_SRGB)
hasSRGBcanvas = true;
}
OpenGL::TempDebugGroup debuggroup("Canvas set");
setupGrab();
// Make sure the correct sRGB setting is used when drawing to the canvases.
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
{
if (hasSRGBcanvas && !gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(true);
else if (!hasSRGBcanvas && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
// Don't attach anything if there's nothing to change.
if (!canvaseschanged)
return;
// Attach the canvas textures to the active FBO and set up MRTs.
std::vector<GLenum> drawbuffers;
drawbuffers.reserve(canvases.size() + 1);
drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
// Attach the canvas textures to the currently bound framebuffer.
for (int i = 0; i < (int) canvases.size(); i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
GL_TEXTURE_2D, *(GLuint *) canvases[i]->getHandle(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
}
// set up multiple render targets
glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
// We want to avoid reference cycles, so we don't retain the attached
// Canvases here. The code in Graphics::setCanvas retains them.
attachedCanvases = canvases;
}
void Canvas::startGrab()
{
OpenGL::TempDebugGroup debuggroup("Canvas set");
setupGrab();
// Make sure the correct sRGB setting is used when drawing to the canvas.
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
{
bool isSRGB = getSizedFormat(format) == FORMAT_SRGB;
if (isSRGB && !gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(true);
else if (!isSRGB && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
if (attachedCanvases.size() > 0)
{
// Make sure the FBO is only using a single draw buffer.
// GLES3 only has glDrawBuffers, so we avoid using glDrawBuffer.
const GLenum buffers[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, buffers);
attachedCanvases.clear();
}
}
void Canvas::stopGrab(bool switchingToOtherCanvas)
{
// i am not grabbing. leave me alone
if (current != this)
return;
OpenGL::TempDebugGroup debuggroup("Canvas un-set");
// Make sure the canvas texture is up to date if we're using MSAA.
resolveMSAA(false);
if (gl.matrices.projection.size() > 1)
gl.matrices.projection.pop_back();
if (!switchingToOtherCanvas)
{
// bind system framebuffer.
gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
current = nullptr;
gl.setViewport(systemViewport);
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
{
if (screenHasSRGB && !gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(true);
else if (!screenHasSRGB && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
}
}
bool Canvas::checkCreateStencil()
{
// Do nothing if we've already created the stencil buffer.
if (depth_stencil != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Canvas create stencil");
if (current != this)
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
GLenum format = GL_STENCIL_INDEX8;
std::vector<GLenum> attachments = {GL_STENCIL_ATTACHMENT};
// Prefer a combined depth/stencil buffer.
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
{
format = GL_DEPTH24_STENCIL8;
attachments = {GL_DEPTH_STENCIL_ATTACHMENT};
}
else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
{
format = GL_DEPTH24_STENCIL8;
attachments = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
}
glGenRenderbuffers(1, &depth_stencil);
glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
if (requested_samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, requested_samples, format, width, height);
else
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
// Attach the buffer to the framebuffer object.
for (GLenum attachment : attachments)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, depth_stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
bool success = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
// We don't want the stencil buffer filled with garbage.
if (success)
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
else
{
glDeleteRenderbuffers(1, &depth_stencil);
depth_stencil = 0;
}
if (current && current != this)
gl.bindFramebuffer(GL_FRAMEBUFFER, current->fbo);
else if (!current)
gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
return success;
}
love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, int y, int w, int h)
{
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > width || (y + h) > height)
throw love::Exception("Invalid ImageData rectangle dimensions.");
GLenum datatype = GL_UNSIGNED_BYTE;
image::ImageData::Format imageformat = image::ImageData::FORMAT_RGBA8;
switch (getSizedFormat(format))
{
case FORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES
datatype = GL_UNSIGNED_SHORT;
imageformat = image::ImageData::FORMAT_RGBA16;
break;
case FORMAT_R16F:
case FORMAT_RG16F:
case FORMAT_RGBA16F:
case FORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES
datatype = GL_HALF_FLOAT;
imageformat = image::ImageData::FORMAT_RGBA16F;
break;
case FORMAT_R32F:
case FORMAT_RG32F:
case FORMAT_RGBA32F:
datatype = GL_FLOAT;
imageformat = image::ImageData::FORMAT_RGBA32F;
break;
default:
break;
}
size_t size = w * h * image::ImageData::getPixelSize(imageformat);
uint8 *pixels = nullptr;
try
{
pixels = new uint8[size];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
// Make sure the canvas texture is up to date if we're using MSAA.
if (current == this)
resolveMSAA(false);
// Our texture is attached to 'resolve_fbo' when we use MSAA.
if (resolve_fbo != 0)
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
else
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
glReadPixels(x, y, w, h, GL_RGBA, datatype, pixels);
GLuint prevfbo = current ? current->fbo : gl.getDefaultFBO();
gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo);
// The new ImageData now owns the pixel data, so we don't delete it here.
return image->newImageData(w, h, imageformat, pixels, true);
}
bool Canvas::resolveMSAA(bool restoreprev)
{
if (resolve_fbo == 0 || msaa_buffer == 0)
return false;
OpenGL::TempDebugGroup debuggroup("Canvas MSAA resolve");
GLint w = width;
GLint h = height;
// Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
// For many of the MSAA extensions that add suffixes to the functions, we
// assign function pointers in OpenGL.cpp so we can call the core functions.
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
if (GLAD_APPLE_framebuffer_multisample)
glResolveMultisampleFramebufferAPPLE();
else
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
if (restoreprev)
{
GLuint fbo = current ? current->fbo : gl.getDefaultFBO();
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
}
return true;
}
Canvas::Format Canvas::getSizedFormat(Canvas::Format format)
{
switch (format)
@@ -950,7 +597,7 @@ bool Canvas::isFormatSupported(Canvas::Format format)
GLuint fbo = 0;
supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
glDeleteFramebuffers(1, &fbo);
gl.deleteFramebuffer(fbo);
gl.deleteTexture(texture);
+15 -37
View File
@@ -84,26 +84,6 @@ public:
bool setWrap(const Texture::Wrap &w) override;
const void *getHandle() const override;
/**
* @param canvases A list of other canvases to temporarily attach to this one,
* to allow drawing to multiple canvases at once.
**/
void startGrab(const std::vector<Canvas *> &canvases);
void startGrab();
void stopGrab(bool switchingToOtherCanvas = false);
/**
* Create and attach a stencil buffer to this Canvas' framebuffer, if necessary.
**/
bool checkCreateStencil();
love::image::ImageData *newImageData(love::image::Image *image, int x, int y, int w, int h);
inline const std::vector<Canvas *> &getAttachedCanvases() const
{
return attachedCanvases;
}
inline GLenum getStatus() const
{
return status;
@@ -119,19 +99,26 @@ public:
return actual_samples;
}
inline int getRequestedMSAA() const
{
return requested_samples;
}
inline ptrdiff_t getMSAAHandle() const
{
return msaa_buffer;
}
inline GLuint getFBO() const
{
return fbo;
}
static Format getSizedFormat(Format format);
static bool isSupported();
static bool isMultiFormatMultiCanvasSupported();
static bool isFormatSupported(Format format);
static Canvas *current;
// The viewport dimensions of the system (default) framebuffer.
static OpenGL::Viewport systemViewport;
// Whether the main screen should have linear -> sRGB conversions enabled.
static bool screenHasSRGB;
static int canvasCount;
static bool getConstant(const char *in, Format &out);
@@ -139,29 +126,20 @@ public:
private:
void setupGrab();
bool createMSAAFBO(GLenum internalformat);
bool resolveMSAA(bool restoreprev);
void drawv(const Matrix4 &t, const Vertex *v);
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
static size_t getFormatBitsPerPixel(Format format);
GLuint fbo;
GLuint resolve_fbo;
GLuint texture;
GLuint msaa_buffer;
GLuint depth_stencil;
Format format;
GLenum status;
std::vector<Canvas *> attachedCanvases;
int requested_samples;
int actual_samples;
File diff suppressed because it is too large Load Diff
+105 -38
View File
@@ -24,6 +24,7 @@
// STD
#include <stack>
#include <vector>
#include <unordered_map>
// OpenGL
#include "OpenGL.h"
@@ -35,10 +36,10 @@
#include "image/Image.h"
#include "image/ImageData.h"
#include "window/Window.h"
#include "video/VideoStream.h"
#include "math/Transform.h"
#include "Font.h"
#include "Image.h"
#include "graphics/Quad.h"
@@ -53,21 +54,63 @@
namespace love
{
class Reference;
namespace graphics
{
namespace opengl
{
struct PassInfo
{
enum BeginAction
{
BEGIN_LOAD,
BEGIN_CLEAR,
BEGIN_DISCARD,
};
enum EndAction
{
END_STORE,
END_DISCARD,
};
struct ColorAttachment
{
Canvas *canvas = nullptr;
Colorf clearColor = Colorf(0.0f, 0.0f, 0.0f, 0.0f);
BeginAction beginAction = BEGIN_LOAD;
};
ColorAttachment colorAttachments[MAX_COLOR_RENDER_TARGETS];
int colorAttachmentCount = 0;
bool stencil = false;
bool addColorAttachment(const ColorAttachment &attachment)
{
if (colorAttachmentCount + 1 < MAX_COLOR_RENDER_TARGETS)
{
colorAttachments[colorAttachmentCount++] = attachment;
return true;
}
return false;
}
};
class Graphics : public love::graphics::Graphics
{
public:
struct OptionalColorf
{
float r, g, b, a;
bool enabled;
typedef void (*ScreenshotCallback)(love::image::ImageData *i, Reference *ref, void *ud);
Colorf toColor() const { return Colorf(r, g, b, a); }
struct ScreenshotInfo
{
ScreenshotCallback callback;
Reference *ref;
};
Graphics();
@@ -92,25 +135,19 @@ public:
**/
void reset();
/**
* Clears the screen to a specific color.
**/
void clear(Colorf c);
void beginPass(PassInfo::BeginAction beginAction, Colorf clearColor);
void beginPass(const PassInfo &info);
/**
* Clears each active canvas to a different color.
**/
void clear(const std::vector<OptionalColorf> &colors);
void endPass();
void endPass(int sX, int sY, int sW, int sH, const ScreenshotInfo *info, void *screenshotCallbackData);
/**
* Discards the contents of the screen.
**/
void discard(const std::vector<bool> &colorbuffers, bool stencil);
const PassInfo &getActivePass() const;
virtual bool isPassActive() const;
/**
* Flips buffers. (Rendered geometry is presented on screen).
**/
void present();
void present(void *screenshotCallbackData);
/**
* Gets the width of the current graphics viewport.
@@ -122,6 +159,9 @@ public:
**/
int getHeight() const;
int getPassWidth() const;
int getPassHeight() const;
/**
* True if a graphics viewport is set.
**/
@@ -231,13 +271,6 @@ public:
Shader *getShader() const;
void setCanvas(Canvas *canvas);
void setCanvas(const std::vector<Canvas *> &canvases);
void setCanvas(const std::vector<StrongRef<Canvas>> &canvases);
void setCanvas();
std::vector<Canvas *> getCanvas() const;
/**
* Sets the enabled color components when rendering.
**/
@@ -330,6 +363,9 @@ public:
**/
bool isWireframe() const;
void draw(Drawable *drawable, const Matrix4 &m);
void drawq(Texture *texture, Quad *quad, const Matrix4 &m);
/**
* Draws text at the specified coordinates
**/
@@ -422,12 +458,7 @@ public:
**/
void polygon(DrawMode mode, const float *coords, size_t count);
/**
* Creates a screenshot of the view and saves it to the default folder.
* @param image The love.image module.
* @param copyAlpha If the alpha channel should be copied or set to full opacity (1.0).
**/
love::image::ImageData *newScreenshot(love::image::Image *image, bool copyAlpha = true);
void captureScreenshot(const ScreenshotInfo &info);
/**
* Returns system-dependent renderer information.
@@ -459,6 +490,10 @@ public:
void translate(float x, float y);
void shear(float kx, float ky);
void origin();
void applyTransform(love::math::Transform *transform);
void replaceTransform(love::math::Transform *transform);
Vector transformPoint(Vector point);
Vector inverseTransformPoint(Vector point);
@@ -488,8 +523,6 @@ private:
StrongRef<Font> font;
StrongRef<Shader> shader;
std::vector<StrongRef<Canvas>> canvases;
ColorMask colorMask = ColorMask(true, true, true, true);
bool wireframe = false;
@@ -500,18 +533,46 @@ private:
float defaultMipmapSharpness = 0.0f;
};
struct CurrentPass
{
PassInfo info;
bool active = false;
};
struct PassBufferInfo
{
bool stencil;
Canvas *canvases[MAX_COLOR_RENDER_TARGETS];
};
struct CachedRenderbuffer
{
int w;
int h;
int samples;
GLenum attachments[2];
GLuint renderbuffer;
};
void restoreState(const DisplayState &s);
void restoreStateChecked(const DisplayState &s);
void bindCachedFBOForPass(const PassInfo &pass);
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
GLuint attachCachedStencilBuffer(int w, int h, int samples);
void checkSetDefaultFont();
int calculateEllipsePoints(float rx, float ry) const;
StrongRef<love::window::Window> currentWindow;
StrongRef<Font> defaultFont;
std::vector<double> pixelSizeStack; // stores current size of a pixel (needed for line drawing)
std::vector<double> pixelScaleStack;
std::vector<ScreenshotInfo> pendingScreenshotCallbacks;
std::unordered_map<uint32, GLuint> framebufferObjects;
std::vector<CachedRenderbuffer> stencilBuffers;
QuadIndices *quadIndices;
@@ -520,11 +581,17 @@ private:
bool created;
bool active;
bool canCaptureScreenshot;
CurrentPass currentPass;
bool writingToStencil;
std::vector<DisplayState> states;
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
int renderPassCount;
static const size_t MAX_USER_STACK_DEPTH = 64;
}; // Graphics
+3
View File
@@ -283,6 +283,9 @@ void Image::loadFromImageData()
bool Image::loadVolatile()
{
if (texture != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Image load");
if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB))
+21 -3
View File
@@ -311,7 +311,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
return it->second.enabled;
}
void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname)
{
if (mesh != this)
{
@@ -333,10 +333,10 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
newattrib.mesh = mesh;
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
newattrib.index = mesh->getAttributeIndex(name);
newattrib.index = mesh->getAttributeIndex(attachname);
if (newattrib.index < 0)
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
if (newattrib.mesh != this)
newattrib.mesh->retain();
@@ -347,6 +347,24 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
oldattrib.mesh->release();
}
bool Mesh::detachAttribute(const std::string &name)
{
auto it = attachedAttributes.find(name);
if (it != attachedAttributes.end() && it->second.mesh != this)
{
it->second.mesh->release();
attachedAttributes.erase(it);
if (getAttributeIndex(name) != -1)
attachAttribute(name, this, name);
return true;
}
return false;
}
void *Mesh::mapVertexData()
{
return vbo->map();
+2 -1
View File
@@ -140,7 +140,8 @@ public:
* Attaches a vertex attribute from another Mesh to this one. The attribute
* will be used when drawing this Mesh.
**/
void attachAttribute(const std::string &name, Mesh *mesh);
void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname);
bool detachAttribute(const std::string &name);
void *mapVertexData();
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
+109 -16
View File
@@ -23,7 +23,6 @@
#include "OpenGL.h"
#include "Shader.h"
#include "Canvas.h"
#include "common/Exception.h"
// C++
@@ -66,6 +65,7 @@ static void *LOVEGetProcAddress(const char *name)
OpenGL::OpenGL()
: stats()
, contextInitialized(false)
, pixelShaderHighpSupported(false)
, maxAnisotropy(1.0f)
, maxTextureSize(0)
, maxRenderTargets(1)
@@ -75,7 +75,6 @@ OpenGL::OpenGL()
, state()
{
matrices.transform.reserve(10);
matrices.projection.reserve(2);
}
bool OpenGL::initContext()
@@ -135,6 +134,10 @@ void OpenGL::setupContext()
else
state.pointSize = 1.0f;
for (int i = 0; i < 2; i++)
state.boundFramebuffers[i] = std::numeric_limits<GLuint>::max();
bindFramebuffer(FRAMEBUFFER_ALL, getDefaultFBO());
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|| GLAD_EXT_sRGB_write_control)
{
@@ -266,6 +269,16 @@ void OpenGL::initOpenGLFunctions()
void OpenGL::initMaxValues()
{
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
{
GLint range = 0;
GLint precision = 0;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, &range, &precision);
pixelShaderHighpSupported = range > 0;
}
else
pixelShaderHighpSupported = true;
// We'll need this value to clamp anisotropy.
if (GLAD_EXT_texture_filter_anisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
@@ -283,7 +296,7 @@ void OpenGL::initMaxValues()
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
}
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
maxRenderTargets = std::max(std::min(maxattachments, maxdrawbuffers), 1);
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
|| GLAD_EXT_framebuffer_multisample || GLAD_APPLE_framebuffer_multisample
@@ -304,10 +317,9 @@ void OpenGL::initMaxValues()
void OpenGL::initMatrices()
{
matrices.transform.clear();
matrices.projection.clear();
matrices.transform.push_back(Matrix4());
matrices.projection.push_back(Matrix4());
matrices.projection = Matrix4();
}
void OpenGL::createDefaultTexture()
@@ -361,7 +373,7 @@ void OpenGL::prepareDraw()
// because uniform uploads can be significantly slower than glLoadMatrix.
if (GLAD_VERSION_1_0)
{
const Matrix4 &curproj = matrices.projection.back();
const Matrix4 &curproj = matrices.projection;
const Matrix4 &curxform = matrices.transform.back();
const Matrix4 &lastproj = state.lastProjectionMatrix;
@@ -374,7 +386,7 @@ void OpenGL::prepareDraw()
glLoadMatrixf(curproj.getElements());
glMatrixMode(GL_MODELVIEW);
state.lastProjectionMatrix = matrices.projection.back();
state.lastProjectionMatrix = matrices.projection;
}
// Same with the transform matrix.
@@ -463,7 +475,7 @@ void OpenGL::useVertexAttribArrays(uint32 arraybits)
glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
}
void OpenGL::setViewport(const OpenGL::Viewport &v)
void OpenGL::setViewport(const OpenGL::Viewport &v, bool canvasActive)
{
glViewport(v.x, v.y, v.w, v.h);
state.viewport = v;
@@ -471,7 +483,7 @@ void OpenGL::setViewport(const OpenGL::Viewport &v)
// glScissor starts from the lower left, so we compensate when setting the
// scissor. When the viewport is changed, we need to manually update the
// scissor again.
setScissor(state.scissor);
setScissor(state.scissor, canvasActive);
}
OpenGL::Viewport OpenGL::getViewport() const
@@ -479,9 +491,9 @@ OpenGL::Viewport OpenGL::getViewport() const
return state.viewport;
}
void OpenGL::setScissor(const OpenGL::Viewport &v)
void OpenGL::setScissor(const OpenGL::Viewport &v, bool canvasActive)
{
if (Canvas::current)
if (canvasActive)
glScissor(v.x, v.y, v.w, v.h);
else
{
@@ -526,12 +538,53 @@ bool OpenGL::hasFramebufferSRGB() const
return state.framebufferSRGBEnabled;
}
void OpenGL::bindFramebuffer(GLenum target, GLuint framebuffer)
void OpenGL::bindFramebuffer(FramebufferTarget target, GLuint framebuffer)
{
glBindFramebuffer(target, framebuffer);
bool bindingmodified = false;
if (target == GL_FRAMEBUFFER)
++stats.framebufferBinds;
if ((target & FRAMEBUFFER_DRAW) && state.boundFramebuffers[0] != framebuffer)
{
bindingmodified = true;
state.boundFramebuffers[0] = framebuffer;
}
if ((target & FRAMEBUFFER_READ) && state.boundFramebuffers[1] != framebuffer)
{
bindingmodified = true;
state.boundFramebuffers[1] = framebuffer;
}
if (bindingmodified)
{
GLenum gltarget = GL_FRAMEBUFFER;
if (target == FRAMEBUFFER_DRAW)
gltarget = GL_DRAW_FRAMEBUFFER;
else if (target == FRAMEBUFFER_READ)
gltarget = GL_READ_FRAMEBUFFER;
glBindFramebuffer(gltarget, framebuffer);
}
}
GLenum OpenGL::getFramebuffer(FramebufferTarget target) const
{
if (target & FRAMEBUFFER_DRAW)
return state.boundFramebuffers[0];
else if (target & FRAMEBUFFER_READ)
return state.boundFramebuffers[1];
else
return 0;
}
void OpenGL::deleteFramebuffer(GLuint framebuffer)
{
glDeleteFramebuffers(1, &framebuffer);
for (int i = 0; i < 2; i++)
{
if (state.boundFramebuffers[i] == framebuffer)
state.boundFramebuffers[i] = 0;
}
}
void OpenGL::useProgram(GLuint program)
@@ -673,6 +726,11 @@ bool OpenGL::isClampZeroTextureWrapSupported() const
return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp;
}
bool OpenGL::isPixelShaderHighpSupported() const
{
return pixelShaderHighpSupported;
}
int OpenGL::getMaxTextureSize() const
{
return maxTextureSize;
@@ -680,7 +738,7 @@ int OpenGL::getMaxTextureSize() const
int OpenGL::getMaxRenderTargets() const
{
return maxRenderTargets;
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
}
int OpenGL::getMaxRenderbufferSamples() const
@@ -698,6 +756,11 @@ float OpenGL::getMaxPointSize() const
return maxPointSize;
}
float OpenGL::getMaxAnisotropy() const
{
return maxAnisotropy;
}
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
{
int64 memsize = (int64) stats.textureMemory + ((int64) newsize - (int64) oldsize);
@@ -739,6 +802,36 @@ const char *OpenGL::errorString(GLenum errorcode)
return text;
}
const char *OpenGL::framebufferStatusString(GLenum status)
{
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE:
return "complete (success)";
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
return "Texture format cannot be rendered to on this system.";
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
return "Error in graphics driver (missing render texture attachment)";
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
return "Error in graphics driver (incomplete draw buffer)";
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
return "Error in graphics driver (incomplete read buffer)";
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
return "Canvas with the specified MSAA count cannot be rendered to on this system.";
case GL_FRAMEBUFFER_UNSUPPORTED:
return "Renderable textures are unsupported";
default:
break;
}
static char text[64] = {};
memset(text, 0, sizeof(text));
sprintf(text, "0x%x", status);
return text;
}
const char *OpenGL::debugSeverityString(GLenum severity)
{
switch (severity)
+24 -5
View File
@@ -104,6 +104,13 @@ public:
VENDOR_UNKNOWN
};
enum FramebufferTarget
{
FRAMEBUFFER_READ = (1 << 0),
FRAMEBUFFER_DRAW = (1 << 1),
FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW),
};
// A rectangle representing an OpenGL viewport or a scissor box.
struct Viewport
{
@@ -119,7 +126,7 @@ public:
struct
{
std::vector<Matrix4> transform;
std::vector<Matrix4> projection;
Matrix4 projection;
} matrices;
class TempTransform
@@ -173,7 +180,6 @@ public:
{
size_t textureMemory;
int drawCalls;
int framebufferBinds;
int shaderSwitches;
} stats;
@@ -282,7 +288,7 @@ public:
* Sets the OpenGL rendering viewport to the specified rectangle.
* The y-coordinate starts at the top.
**/
void setViewport(const Viewport &v);
void setViewport(const Viewport &v, bool canvasActive);
/**
* Gets the current OpenGL rendering viewport rectangle.
@@ -293,7 +299,7 @@ public:
* Sets the scissor box to the specified rectangle.
* The y-coordinate starts at the top and is flipped internally.
**/
void setScissor(const Viewport &v);
void setScissor(const Viewport &v, bool canvasActive);
/**
* Gets the current scissor box (regardless of whether scissoring is enabled.)
@@ -323,7 +329,9 @@ public:
/**
* Binds a Framebuffer Object to the specified target.
**/
void bindFramebuffer(GLenum target, GLuint framebuffer);
void bindFramebuffer(FramebufferTarget target, GLuint framebuffer);
GLuint getFramebuffer(FramebufferTarget target) const;
void deleteFramebuffer(GLuint framebuffer);
/**
* Calls glUseProgram.
@@ -375,6 +383,7 @@ public:
void setTextureWrap(const graphics::Texture::Wrap &w);
bool isClampZeroTextureWrapSupported() const;
bool isPixelShaderHighpSupported() const;
/**
* Returns the maximum supported width or height of a texture.
@@ -401,6 +410,12 @@ public:
**/
float getMaxPointSize() const;
/**
* Returns the maximum anisotropic filtering value that can be used for
* Texture filtering.
**/
float getMaxAnisotropy() const;
void updateTextureMemorySize(size_t oldsize, size_t newsize);
@@ -413,6 +428,7 @@ public:
static GLint getGLWrapMode(Texture::WrapMode wmode);
static const char *errorString(GLenum errorcode);
static const char *framebufferStatusString(GLenum status);
// Get human-readable strings for debug info.
static const char *debugSeverityString(GLenum severity);
@@ -429,6 +445,7 @@ private:
bool contextInitialized;
bool pixelShaderHighpSupported;
float maxAnisotropy;
int maxTextureSize;
int maxRenderTargets;
@@ -456,6 +473,8 @@ private:
float pointSize;
GLuint boundFramebuffers[2];
bool framebufferSRGBEnabled;
GLuint defaultTexture;
+315 -199
View File
@@ -22,11 +22,12 @@
#include "common/config.h"
#include "Shader.h"
#include "Canvas.h"
#include "Graphics.h"
// C++
#include <algorithm>
#include <limits>
#include <sstream>
namespace love
{
@@ -41,11 +42,12 @@ namespace
// reattaches the originally active program when destroyed
struct TemporaryAttacher
{
TemporaryAttacher(Shader *shader)
TemporaryAttacher(Shader *shader, bool attachNow)
: curShader(shader)
, prevShader(Shader::current)
{
curShader->attach(true);
if (attachNow)
attach();
}
~TemporaryAttacher()
@@ -56,6 +58,11 @@ namespace
curShader->detach();
}
void attach()
{
curShader->attach(true);
}
Shader *curShader;
Shader *prevShader;
};
@@ -70,14 +77,12 @@ Shader *Shader::defaultVideoShader = nullptr;
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2];
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2];
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSource &source)
: shaderSource(source)
, program(0)
, builtinUniforms()
, builtinAttributes()
, lastCanvas((Canvas *) -1)
, canvasWasActive(false)
, lastViewport()
, lastPointSize(0.0f)
, videoTextureUnits()
@@ -85,10 +90,6 @@ Shader::Shader(const ShaderSource &source)
if (source.vertex.empty() && source.pixel.empty())
throw love::Exception("Cannot create shader: no source code!");
// initialize global texture id counters if needed
if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1)
textureCounters.resize(gl.getMaxTextureUnits() - 1, 0);
// load shader source and create program object
loadVolatile();
}
@@ -98,12 +99,25 @@ Shader::~Shader()
if (current == this)
detach();
for (const auto &retainable : boundRetainables)
retainable.second->release();
boundRetainables.clear();
unloadVolatile();
for (const auto &p : uniforms)
{
// Allocated with malloc().
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
}
}
}
GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
@@ -177,8 +191,6 @@ void Shader::mapActiveUniforms()
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
uniforms.clear();
GLint activeprogram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
@@ -190,6 +202,9 @@ void Shader::mapActiveUniforms()
GLchar cname[256];
const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar));
std::map<std::string, UniformInfo> olduniforms = uniforms;
uniforms.clear();
for (int i = 0; i < numuniforms; i++)
{
GLsizei namelen = 0;
@@ -207,12 +222,6 @@ void Shader::mapActiveUniforms()
else
u.components = getUniformTypeComponents(gltype);
// Initialize all samplers to 0. Both GLSL and GLSL ES are supposed to
// do this themselves, but some Android devices (galaxy tab 3 and 4)
// don't seem to do it...
if (u.baseType == UNIFORM_SAMPLER)
glUniform1i(u.location, 0);
// glGetActiveUniform appends "[0]" to the end of array uniform names...
if (u.name.length() > 3)
{
@@ -226,8 +235,138 @@ void Shader::mapActiveUniforms()
if (builtinNames.find(u.name.c_str(), builtin))
builtinUniforms[int(builtin)] = u.location;
if (u.location != -1)
uniforms[u.name] = u;
if (u.location == -1)
continue;
// Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved.
auto oldu = olduniforms.find(u.name);
if (oldu != olduniforms.end())
{
u.data = oldu->second.data;
u.textures = oldu->second.textures;
updateUniform(&u, u.count, true);
if (u.baseType == UNIFORM_SAMPLER)
{
// Make sure all stored textures have their Volatiles loaded
// before the sendTextures call, since it calls getHandle().
for (int i = 0; i < u.count; i++)
{
if (u.textures[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
if (v != nullptr)
v->loadVolatile();
}
sendTextures(&u, u.textures, u.count, true);
}
}
else
{
size_t datasize = 0;
switch (u.baseType)
{
case UNIFORM_FLOAT:
datasize = sizeof(float) * u.components * u.count;
u.data = malloc(datasize);
break;
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
datasize = sizeof(int) * u.components * u.count;
u.data = malloc(datasize);
break;
case UNIFORM_MATRIX:
datasize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
u.data = malloc(datasize);
break;
default:
break;
}
if (datasize > 0)
{
memset(u.data, 0, datasize);
if (u.baseType == UNIFORM_SAMPLER)
{
// Initialize all samplers to 0. Both GLSL and GLSL ES are
// supposed to do this themselves, but some Android devices
// (galaxy tab 3 and 4) don't seem to do it...
glUniform1iv(u.location, u.count, u.ints);
u.textures = new Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
size_t offset = 0;
// Store any shader-initialized values in our own memory.
for (int i = 0; i < u.count; i++)
{
GLint location = u.location;
if (u.count > 1)
{
std::ostringstream ss;
ss << i;
std::string indexname = u.name + "[" + ss.str() + "]";
location = glGetUniformLocation(program, indexname.c_str());
}
if (location == -1)
continue;
switch (u.baseType)
{
case UNIFORM_FLOAT:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.components;
break;
case UNIFORM_INT:
case UNIFORM_BOOL:
glGetUniformiv(program, location, &u.ints[offset]);
offset += u.components;
break;
case UNIFORM_MATRIX:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.matrix.rows * u.matrix.columns;
break;
default:
break;
}
}
}
uniforms[u.name] = u;
}
// Make sure uniforms that existed before but don't exist anymore are
// cleaned up. This theoretically shouldn't happen, but...
for (const auto &p : olduniforms)
{
if (uniforms.find(p.first) == uniforms.end())
{
free(p.second.data);
if (p.second.baseType != UNIFORM_SAMPLER)
continue;
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
}
}
gl.useProgram(activeprogram);
@@ -238,7 +377,7 @@ bool Shader::loadVolatile()
OpenGL::TempDebugGroup debuggroup("Shader load");
// Recreating the shader program will invalidate uniforms that rely on these.
lastCanvas = (Canvas *) -1;
canvasWasActive = false;
lastViewport = OpenGL::Viewport();
lastPointSize = -1.0f;
@@ -252,8 +391,8 @@ bool Shader::loadVolatile()
videoTextureUnits[i] = 0;
// zero out active texture list
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
textureUnits.clear();
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
std::vector<GLuint> shaderids;
@@ -340,31 +479,21 @@ bool Shader::loadVolatile()
void Shader::unloadVolatile()
{
if (current == this)
gl.useProgram(0);
if (program != 0)
{
if (current == this)
gl.useProgram(0);
glDeleteProgram(program);
program = 0;
}
// decrement global texture id counters for texture units which had textures bound from this shader
for (size_t i = 0; i < activeTexUnits.size(); ++i)
{
if (activeTexUnits[i] > 0)
textureCounters[i] = std::max(textureCounters[i] - 1, 0);
}
// active texture list is probably invalid, clear it
activeTexUnits.clear();
activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0);
textureUnits.clear();
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
attributes.clear();
// same with uniform location list
uniforms.clear();
// And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
@@ -421,10 +550,10 @@ void Shader::attach(bool temporary)
{
// make sure all sent textures are properly bound to their respective texture units
// note: list potentially contains texture ids of deleted/invalid textures!
for (int i = 0; i < (int) activeTexUnits.size(); ++i)
for (int i = 1; i < (int) textureUnits.size(); ++i)
{
if (activeTexUnits[i] > 0)
gl.bindTextureToUnit(activeTexUnits[i], i + 1, false);
if (textureUnits[i].active)
gl.bindTextureToUnit(textureUnits[i].texture, i, false);
}
}
}
@@ -456,160 +585,148 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
return &(it->second);
}
void Shader::sendInts(const UniformInfo *info, const int *vec, int count)
void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_INT && info->baseType != UNIFORM_BOOL)
return;
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, !internalUpdate);
int location = info->location;
UniformType type = info->baseType;
switch (info->components)
if (type == UNIFORM_FLOAT)
{
case 4:
glUniform4iv(location, count, vec);
break;
case 3:
glUniform3iv(location, count, vec);
break;
case 2:
glUniform2iv(location, count, vec);
break;
case 1:
default:
glUniform1iv(location, count, vec);
break;
switch (info->components)
{
case 1:
glUniform1fv(location, count, info->floats);
break;
case 2:
glUniform2fv(location, count, info->floats);
break;
case 3:
glUniform3fv(location, count, info->floats);
break;
case 4:
glUniform4fv(location, count, info->floats);
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
{
switch (info->components)
{
case 1:
glUniform1iv(location, count, info->ints);
break;
case 2:
glUniform2iv(location, count, info->ints);
break;
case 3:
glUniform3iv(location, count, info->ints);
break;
case 4:
glUniform4iv(location, count, info->ints);
break;
}
}
else if (type == UNIFORM_MATRIX)
{
int columns = info->matrix.columns;
int rows = info->matrix.rows;
if (columns == 2 && rows == 2)
glUniformMatrix2fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 3)
glUniformMatrix3fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 4)
glUniformMatrix4fv(location, count, GL_FALSE, info->floats);
else if (columns == 2 && rows == 3)
glUniformMatrix2x3fv(location, count, GL_FALSE, info->floats);
else if (columns == 2 && rows == 4)
glUniformMatrix2x4fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 2)
glUniformMatrix3x2fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 4)
glUniformMatrix3x4fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 2)
glUniformMatrix4x2fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 3)
glUniformMatrix4x3fv(location, count, GL_FALSE, info->floats);
}
}
void Shader::sendFloats(const UniformInfo *info, const float *vec, int count)
int Shader::getFreeTextureUnits(int count)
{
if (info->baseType != UNIFORM_FLOAT && info->baseType != UNIFORM_BOOL)
return;
int startunit = -1;
TemporaryAttacher attacher(this);
int location = info->location;
switch (info->components)
// Ignore the first texture unit for Shader-local texture bindings.
for (int i = 1; i < (int) textureUnits.size(); i++)
{
case 4:
glUniform4fv(location, count, vec);
break;
case 3:
glUniform3fv(location, count, vec);
break;
case 2:
glUniform2fv(location, count, vec);
break;
case 1:
default:
glUniform1fv(location, count, vec);
break;
if (!textureUnits[i].active && i + count <= (int) textureUnits.size())
{
startunit = i;
break;
}
}
if (startunit == -1)
throw love::Exception("No more texture units available for shader.");
return startunit;
}
void Shader::sendMatrices(const UniformInfo *info, const float *m, int count)
{
if (info->baseType != UNIFORM_MATRIX)
return;
TemporaryAttacher attacher(this);
int location = info->location;
int columns = info->matrix.columns;
int rows = info->matrix.rows;
if (columns == 2 && rows == 2)
glUniformMatrix2fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 3)
glUniformMatrix3fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 4)
glUniformMatrix4fv(location, count, GL_FALSE, m);
else if (columns == 2 && rows == 3)
glUniformMatrix2x3fv(location, count, GL_FALSE, m);
else if (columns == 2 && rows == 4)
glUniformMatrix2x4fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 2)
glUniformMatrix3x2fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 4)
glUniformMatrix3x4fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 2)
glUniformMatrix4x2fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 3)
glUniformMatrix4x3fv(location, count, GL_FALSE, m);
}
void Shader::sendTexture(const UniformInfo *info, Texture *texture)
void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_SAMPLER)
return;
GLuint gltex = *(GLuint *) texture->getHandle();
count = std::min(count, info->count);
bool updateuniform = false;
TemporaryAttacher attacher(this);
int texunit = getTextureUnit(info->name);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(gltex, texunit, false);
glUniform1i(info->location, texunit);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTexUnits[texunit-1] == 0)
++textureCounters[texunit-1];
// store texture id so it can be re-bound to the proper texture unit later
activeTexUnits[texunit-1] = gltex;
retainObject(info->name, texture);
}
void Shader::retainObject(const std::string &name, Object *object)
{
object->retain();
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
boundRetainables[name] = object;
}
int Shader::getTextureUnit(const std::string &name)
{
auto it = texUnitPool.find(name);
if (it != texUnitPool.end())
return it->second;
int texunit = 1;
// prefer texture units which are unused by all other shaders
auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
if (freeunit_it != textureCounters.end())
// Make sure the shader's samplers are associated with texture units.
for (int i = 0; i < count; i++)
{
// we don't want to use unit 0
texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1;
}
else
{
// no completely unused texture units exist, try to use next free slot in our own list
auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0);
if (info->ints[i] == 0 && textures[i] != nullptr)
{
int texunit = getFreeTextureUnits(1);
textureUnits[texunit].active = true;
if (nextunit_it == activeTexUnits.end())
throw love::Exception("No more texture units available for shader.");
// we don't want to use unit 0
texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1;
info->ints[i] = texunit;
updateuniform = true;
}
}
texUnitPool[name] = texunit;
return texunit;
if (updateuniform)
updateUniform(info, count, internalUpdate);
// Bind the textures to the texture units.
for (int i = 0; i < count; i++)
{
if (textures[i] != nullptr)
textures[i]->retain();
if (info->textures[i] != nullptr)
info->textures[i]->release();
info->textures[i] = textures[i];
int texunit = info->ints[i];
if (textures[i] != nullptr)
{
GLuint gltex = *(GLuint *) textures[i]->getHandle();
gl.bindTextureToUnit(gltex, texunit, false);
// store texture id so it can be re-bound to the proper texture unit later
textureUnits[texunit].texture = gltex;
}
else
{
gl.bindTextureToUnit(0, texunit, false);
textureUnits[texunit].texture = 0;
textureUnits[texunit].active = false;
}
}
}
bool Shader::hasUniform(const std::string &name) const
@@ -636,12 +753,12 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
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)
{
TemporaryAttacher attacher(this, true);
const GLint locs[3] = {
builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL],
builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL],
@@ -657,14 +774,15 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
{
if (locs[i] >= 0 && names[i] != nullptr)
{
videoTextureUnits[i] = getTextureUnit(names[i]);
const UniformInfo *info = getUniformInfo(names[i]);
if (info != nullptr)
{
videoTextureUnits[i] = getFreeTextureUnits(1);
textureUnits[videoTextureUnits[i]].active = true;
// 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]);
info->ints[0] = videoTextureUnits[i];
updateUniform(info, 1);
}
}
}
}
@@ -677,7 +795,7 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
if (videoTextureUnits[i] != 0)
{
// Store texture id so it can be re-bound later.
activeTexUnits[videoTextureUnits[i] - 1] = textures[i];
textureUnits[videoTextureUnits[i]].texture = textures[i];
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
}
}
@@ -687,7 +805,10 @@ void Shader::checkSetScreenParams()
{
OpenGL::Viewport view = gl.getViewport();
if (view == lastViewport && lastCanvas == Canvas::current)
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
bool canvasActive = gfx->getActivePass().colorAttachmentCount > 0;
if (view == lastViewport && canvasWasActive == canvasActive)
return;
// In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
@@ -698,7 +819,7 @@ void Shader::checkSetScreenParams()
0.0f, 0.0f,
};
if (Canvas::current != nullptr)
if (canvasActive)
{
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
params[2] = 1.0f;
@@ -716,11 +837,11 @@ void Shader::checkSetScreenParams()
if (location >= 0)
{
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, true);
glUniform4fv(location, 1, params);
}
lastCanvas = Canvas::current;
canvasWasActive = canvasActive;
lastViewport = view;
}
@@ -733,7 +854,7 @@ void Shader::checkSetPointSize(float size)
if (location >= 0)
{
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, true);
glUniform1f(location, size);
}
@@ -751,9 +872,9 @@ void Shader::checkSetBuiltinUniforms()
checkSetPointSize(gl.getPointSize());
const Matrix4 &curxform = gl.matrices.transform.back();
const Matrix4 &curproj = gl.matrices.projection.back();
const Matrix4 &curproj = gl.matrices.projection;
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, true);
bool tpmatrixneedsupdate = false;
@@ -800,11 +921,6 @@ void Shader::checkSetBuiltinUniforms()
}
}
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
{
return boundRetainables;
}
std::string Shader::getGLSLVersion()
{
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
+22 -34
View File
@@ -41,8 +41,6 @@ namespace graphics
namespace opengl
{
class Canvas;
// A GLSL shader
class Shader : public Object, public Volatile
{
@@ -100,13 +98,24 @@ public:
{
int location;
int count;
union
{
int components;
MatrixSize matrix;
};
UniformType baseType;
std::string name;
union
{
void *data;
float *floats;
int *ints;
};
Texture **textures;
};
// Pointer to currently active Shader.
@@ -151,11 +160,9 @@ public:
std::string getWarnings() const;
const UniformInfo *getUniformInfo(const std::string &name) const;
void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false);
void sendInts(const UniformInfo *info, const int *vec, int count);
void sendFloats(const UniformInfo *info, const float *vec, int count);
void sendMatrices(const UniformInfo *info, const float *m, int count);
void sendTexture(const UniformInfo *info, Texture *texture);
void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false);
/**
* Gets whether a uniform with the specified name exists and is actively
@@ -175,24 +182,11 @@ public:
void checkSetPointSize(float size);
void checkSetBuiltinUniforms();
const std::map<std::string, Object *> &getBoundRetainables() const;
GLuint getProgram() const
{
return program;
}
template <typename T>
T *getScratchBuffer(size_t count)
{
size_t bytes = sizeof(T) * count;
if (scratchBuffer.size() < bytes)
scratchBuffer.resize(bytes);
return (T *) scratchBuffer.data();
}
static std::string getGLSLVersion();
static bool isSupported();
@@ -204,6 +198,12 @@ public:
private:
struct TextureUnit
{
GLuint texture = 0;
bool active = false;
};
// Map active uniform names to their locations.
void mapActiveUniforms();
@@ -213,9 +213,7 @@ private:
GLuint compileCode(ShaderStage stage, const std::string &code);
int getTextureUnit(const std::string &name);
void retainObject(const std::string &name, Object *object);
int getFreeTextureUnits(int count);
// Get any warnings or errors generated only by the shader program object.
std::string getProgramWarnings() const;
@@ -241,14 +239,9 @@ private:
std::map<std::string, UniformInfo> uniforms;
// Texture unit pool for setting images
std::map<std::string, GLint> texUnitPool; // texUnitPool[name] = textureunit
std::vector<GLuint> activeTexUnits; // activeTexUnits[textureunit-1] = textureid
std::vector<TextureUnit> textureUnits;
// Uniform name to retainable objects
std::map<std::string, Object*> boundRetainables;
// Pointer to the active Canvas when the screen params were last checked.
Canvas *lastCanvas;
bool canvasWasActive;
OpenGL::Viewport lastViewport;
float lastPointSize;
@@ -258,11 +251,6 @@ private:
GLuint videoTextureUnits[3];
std::vector<char> scratchBuffer;
// Counts total number of textures bound to each texture unit in all shaders
static std::vector<int> textureCounters;
static StringMap<ShaderStage, STAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<ShaderStage, STAGE_MAX_ENUM> stageNames;
@@ -33,55 +33,6 @@ Canvas *luax_checkcanvas(lua_State *L, int idx)
return luax_checktype<Canvas>(L, idx);
}
int w_Canvas_renderTo(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (graphics)
{
// Save the current Canvas so we can restore it when we're done.
std::vector<Canvas *> oldcanvases = graphics->getCanvas();
for (Canvas *c : oldcanvases)
c->retain();
luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); });
lua_settop(L, 2); // make sure the function is on top of the stack
int status = lua_pcall(L, 0, 0, 0);
graphics->setCanvas(oldcanvases);
for (Canvas *c : oldcanvases)
c->release();
if (status != 0)
return lua_error(L);
}
return 0;
}
int w_Canvas_newImageData(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
love::image::Image *image = luax_getmodule<love::image::Image>(L);
int x = (int) luaL_optnumber(L, 2, 0);
int y = (int) luaL_optnumber(L, 3, 0);
int w = (int) luaL_optnumber(L, 4, canvas->getWidth());
int h = (int) luaL_optnumber(L, 5, canvas->getHeight());
love::image::ImageData *img = nullptr;
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, x, y, w, h); });
luax_pushtype(L, img);
img->release();
return 1;
}
int w_Canvas_getFormat(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
@@ -103,8 +54,6 @@ int w_Canvas_getMSAA(lua_State *L)
static const luaL_Reg w_Canvas_functions[] =
{
{ "renderTo", w_Canvas_renderTo },
{ "newImageData", w_Canvas_newImageData },
{ "getFormat", w_Canvas_getFormat },
{ "getMSAA", w_Canvas_getMSAA },
{ 0, 0 }
+410 -223
View File
@@ -27,6 +27,8 @@
#include "filesystem/wrap_Filesystem.h"
#include "video/VideoStream.h"
#include "image/wrap_Image.h"
#include "common/Reference.h"
#include "math/wrap_Transform.h"
#include <cassert>
#include <cstring>
@@ -60,79 +62,9 @@ int w_reset(lua_State *)
return 0;
}
int w_clear(lua_State *L)
int w_present(lua_State *L)
{
Colorf color;
if (lua_isnoneornil(L, 1))
color.set(0.0, 0.0, 0.0, 0.0);
else if (lua_istable(L, 1))
{
std::vector<Graphics::OptionalColorf> colors((size_t) lua_gettop(L));
for (int i = 0; i < lua_gettop(L); i++)
{
if (lua_isnoneornil(L, i + 1) || luax_objlen(L, i + 1) == 0)
{
colors[i].enabled = false;
continue;
}
for (int j = 1; j <= 4; j++)
lua_rawgeti(L, i + 1, j);
colors[i].enabled = true;
colors[i].r = (float) luaL_checknumber(L, -4);
colors[i].g = (float) luaL_checknumber(L, -3);
colors[i].b = (float) luaL_checknumber(L, -2);
colors[i].a = (float) luaL_optnumber(L, -1, 1.0);
lua_pop(L, 4);
}
luax_catchexcept(L, [&]() { instance()->clear(colors); });
return 0;
}
else
{
color.r = (float) luaL_checknumber(L, 1);
color.g = (float) luaL_checknumber(L, 2);
color.b = (float) luaL_checknumber(L, 3);
color.a = (float) luaL_optnumber(L, 4, 1.0);
}
luax_catchexcept(L, [&]() { instance()->clear(color); });
return 0;
}
int w_discard(lua_State *L)
{
std::vector<bool> colorbuffers;
if (lua_istable(L, 1))
{
for (size_t i = 1; i <= luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
colorbuffers.push_back(luax_optboolean(L, -1, true));
lua_pop(L, 1);
}
}
else
{
bool discardcolor = luax_optboolean(L, 1, true);
size_t numbuffers = std::max((size_t) 1, instance()->getCanvas().size());
colorbuffers = std::vector<bool>(numbuffers, discardcolor);
}
bool stencil = luax_optboolean(L, 2, true);
instance()->discard(colorbuffers, stencil);
return 0;
}
int w_present(lua_State *)
{
instance()->present();
luax_catchexcept(L, [&]() { instance()->present(L); });
return 0;
}
@@ -173,6 +105,274 @@ int w_getDimensions(lua_State *L)
return 2;
}
int w_getPassWidth(lua_State *L)
{
lua_pushinteger(L, instance()->getPassWidth());
return 1;
}
int w_getPassHeight(lua_State *L)
{
lua_pushinteger(L, instance()->getPassHeight());
return 1;
}
int w_getPassDimensions(lua_State *L)
{
lua_pushinteger(L, instance()->getPassWidth());
lua_pushinteger(L, instance()->getPassHeight());
return 2;
}
static int w__beginPass(lua_State *L)
{
int nextstartidx = 1;
if (lua_isnoneornil(L, 1))
{
luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_LOAD, Colorf()); });
nextstartidx = 1;
}
else if (lua_isnumber(L, 1))
{
Colorf c;
c.r = (float) luaL_checknumber(L, 1);
c.g = (float) luaL_checknumber(L, 2);
c.b = (float) luaL_checknumber(L, 3);
if (lua_isnumber(L, 4))
{
c.a = (float) lua_tonumber(L, 4);
nextstartidx = 5;
}
else
{
c.a = 1.0f;
nextstartidx = 4;
}
luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_CLEAR, c); });
}
else if (luax_istype(L, 1, Canvas::type))
{
PassInfo::ColorAttachment attachment;
attachment.canvas = luax_checkcanvas(L, 1);
attachment.beginAction = PassInfo::BEGIN_LOAD;
if (lua_isnumber(L, 2))
{
attachment.beginAction = PassInfo::BEGIN_CLEAR;
attachment.clearColor.r = (float) luaL_checknumber(L, 2);
attachment.clearColor.g = (float) luaL_checknumber(L, 3);
attachment.clearColor.b = (float) luaL_checknumber(L, 4);
if (lua_isnumber(L, 5))
{
attachment.clearColor.a = (float) lua_tonumber(L, 5);
nextstartidx = 6;
}
else
{
attachment.clearColor.a = 1.0f;
nextstartidx = 5;
}
}
else
nextstartidx = 2;
PassInfo info;
info.addColorAttachment(attachment);
if (lua_isboolean(L, nextstartidx))
{
info.stencil = luax_toboolean(L, nextstartidx);
nextstartidx++;
}
else
info.stencil = false;
luax_catchexcept(L, [&]() { instance()->beginPass(info); });
}
else
{
luaL_checktype(L, 1, LUA_TTABLE);
int nattachments = std::max((int) luax_objlen(L, 1), 1);
if (nattachments > MAX_COLOR_RENDER_TARGETS)
return luaL_error(L, "Cannot render to %d Canvases at once!", nattachments);
PassInfo info;
for (int i = 1; i <= nattachments; i++)
{
lua_rawgeti(L, 1, i);
luaL_checktype(L, -1, LUA_TTABLE);
PassInfo::ColorAttachment attachment;
attachment.beginAction = PassInfo::BEGIN_LOAD;
lua_rawgeti(L, -1, 1);
attachment.canvas = luax_checkcanvas(L, -1);
lua_rawgeti(L, -2, 2);
if (!lua_isnoneornil(L, -1))
{
attachment.beginAction = PassInfo::BEGIN_CLEAR;
for (int j = 3; j < 6; j++)
lua_rawgeti(L, -j, j);
attachment.clearColor.r = (float) luaL_checknumber(L, -4);
attachment.clearColor.g = (float) luaL_checknumber(L, -3);
attachment.clearColor.b = (float) luaL_checknumber(L, -2);
attachment.clearColor.a = (float) luaL_optnumber(L, -1, 1.0);
}
lua_pop(L, 2 + (attachment.beginAction == PassInfo::BEGIN_CLEAR ? 4 : 1));
info.addColorAttachment(attachment);
}
info.stencil = luax_boolflag(L, 1, "stencil", false);
luax_catchexcept(L, [&]() { instance()->beginPass(info); });
nextstartidx = 2;
}
return nextstartidx;
}
int w_beginPass(lua_State *L)
{
w__beginPass(L);
return 0;
}
static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd)
{
if (i != nullptr)
{
lua_State *L = (lua_State *) gd;
ref->push(L);
delete ref;
luax_pushtype(L, i);
lua_call(L, 1, 0);
}
else
delete ref;
}
static int w__endPass(lua_State *L, int startidx)
{
if (lua_isnoneornil(L, startidx))
{
luax_catchexcept(L, []() { instance()->endPass(); });
}
else
{
int x, y, w, h;
if (lua_isnumber(L, startidx))
{
x = (int) luaL_checknumber(L, 1);
y = (int) luaL_checknumber(L, 2);
w = (int) luaL_checknumber(L, 3);
h = (int) luaL_checknumber(L, 4);
startidx += 4;
}
else
{
x = 0;
y = 0;
w = instance()->getPassWidth();
h = instance()->getPassHeight();
}
luaL_checktype(L, startidx, LUA_TFUNCTION);
Graphics::ScreenshotInfo info;
info.callback = screenshotCallback;
lua_pushvalue(L, startidx);
info.ref = luax_refif(L, LUA_TFUNCTION);
lua_pop(L, 1);
luax_catchexcept(L,
[&]() { instance()->endPass(x, y, w, h, &info, L); },
[&](bool except) { if (except) delete info.ref; }
);
}
return 0;
}
int w_endPass(lua_State *L)
{
return w__endPass(L, 1);
}
int w_renderPass(lua_State *L)
{
int startidx = w__beginPass(L);
if (lua_type(L, startidx) != LUA_TFUNCTION)
{
w__endPass(L, startidx + 1);
luaL_checktype(L, startidx, LUA_TFUNCTION);
return 0;
}
int nargs = lua_gettop(L) - startidx;
int status = lua_pcall(L, nargs, 0, 0);
w__endPass(L, startidx + 1);
if (status != 0)
return lua_error(L);
return 0;
}
int w_isPassActive(lua_State *L)
{
luax_pushboolean(L, instance()->isPassActive());
return 1;
}
int w_getPassCanvases(lua_State *L)
{
if (!instance()->isPassActive())
return 0;
const PassInfo &info = instance()->getActivePass();
for (const auto &attachment : info.colorAttachments)
luax_pushtype(L, attachment.canvas);
return info.colorAttachmentCount;
}
int w_captureScreenshot(lua_State *L)
{
luaL_checktype(L, 1, LUA_TFUNCTION);
Graphics::ScreenshotInfo info;
info.callback = screenshotCallback;
lua_pushvalue(L, 1);
info.ref = luax_refif(L, LUA_TFUNCTION);
lua_pop(L, 1);
luax_catchexcept(L,
[&]() { instance()->captureScreenshot(info); },
[&](bool except) { if (except) delete info.ref; }
);
return 0;
}
int w_setScissor(lua_State *L)
{
int nargs = lua_gettop(L);
@@ -243,13 +443,13 @@ int w_stencil(lua_State *L)
if (lua_toboolean(L, 4) == 0)
instance()->clearStencil();
instance()->drawToStencilBuffer(action, stencilvalue);
luax_catchexcept(L, [&](){ instance()->drawToStencilBuffer(action, stencilvalue); });
// Call stencilfunc()
lua_pushvalue(L, 1);
lua_call(L, 0, 0);
instance()->stopDrawToStencilBuffer();
luax_catchexcept(L, [&](){ instance()->stopDrawToStencilBuffer(); });
return 0;
}
@@ -268,7 +468,7 @@ int w_setStencilTest(lua_State *L)
comparevalue = (int) luaL_checknumber(L, 2);
}
instance()->setStencilTest(compare, comparevalue);
luax_catchexcept(L, [&](){ instance()->setStencilTest(compare, comparevalue); });
return 0;
}
@@ -1246,79 +1446,6 @@ int w_isWireframe(lua_State *L)
return 1;
}
int w_newScreenshot(lua_State *L)
{
love::image::Image *image = luax_getmodule<love::image::Image>(L);
bool copyAlpha = luax_optboolean(L, 1, false);
love::image::ImageData *i = 0;
luax_catchexcept(L, [&](){ i = instance()->newScreenshot(image, copyAlpha); });
luax_pushtype(L, i);
i->release();
return 1;
}
int w_setCanvas(lua_State *L)
{
// Disable stencil writes.
instance()->stopDrawToStencilBuffer();
// called with none -> reset to default buffer
if (lua_isnoneornil(L, 1))
{
instance()->setCanvas();
return 0;
}
bool is_table = lua_istable(L, 1);
std::vector<Canvas *> canvases;
if (is_table)
{
for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
canvases.push_back(luax_checkcanvas(L, -1));
lua_pop(L, 1);
}
}
else
{
for (int i = 1; i <= lua_gettop(L); i++)
canvases.push_back(luax_checkcanvas(L, i));
}
luax_catchexcept(L, [&]() {
if (canvases.size() > 0)
instance()->setCanvas(canvases);
else
instance()->setCanvas();
});
return 0;
}
int w_getCanvas(lua_State *L)
{
const std::vector<Canvas *> canvases = instance()->getCanvas();
int n = 0;
for (Canvas *c : canvases)
{
luax_pushtype(L, c);
n++;
}
if (n == 0)
{
lua_pushnil(L);
n = 1;
}
return n;
}
int w_setShader(lua_State *L)
{
if (lua_isnoneornil(L,1))
@@ -1497,8 +1624,8 @@ int w_getStats(lua_State *L)
lua_pushinteger(L, stats.drawCalls);
lua_setfield(L, -2, "drawcalls");
lua_pushinteger(L, stats.canvasSwitches);
lua_setfield(L, -2, "canvasswitches");
lua_pushinteger(L, stats.renderPasses);
lua_setfield(L, -2, "renderpasses");
lua_pushinteger(L, stats.shaderSwitches);
lua_setfield(L, -2, "shaderswitches");
@@ -1541,24 +1668,37 @@ int w_draw(lua_State *L)
startidx = 2;
}
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
if (luax_istype(L, startidx, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, startidx);
luax_catchexcept(L, [&]() {
if (texture && quad)
instance()->drawq(texture, quad, tf->getMatrix());
else
instance()->draw(drawable, tf->getMatrix());
});
}
else
{
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&]() {
if (texture && quad)
texture->drawq(quad, m);
else if (drawable)
drawable->draw(m);
});
luax_catchexcept(L, [&]() {
if (texture && quad)
instance()->drawq(texture, quad, m);
else if (drawable)
instance()->draw(drawable, m);
});
}
return 0;
}
@@ -1568,19 +1708,27 @@ int w_print(lua_State *L)
std::vector<Font::ColoredString> 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);
float sx = (float)luaL_optnumber(L, 5, 1.0f);
float sy = (float)luaL_optnumber(L, 6, sx);
float ox = (float)luaL_optnumber(L, 7, 0.0f);
float oy = (float)luaL_optnumber(L, 8, 0.0f);
float kx = (float)luaL_optnumber(L, 9, 0.0f);
float ky = (float)luaL_optnumber(L, 10, 0.0f);
if (luax_istype(L, 2, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, 2);
luax_catchexcept(L, [&](){ instance()->print(str, tf->getMatrix()); });
}
else
{
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);
float sx = (float)luaL_optnumber(L, 5, 1.0f);
float sy = (float)luaL_optnumber(L, 6, sx);
float ox = (float)luaL_optnumber(L, 7, 0.0f);
float oy = (float)luaL_optnumber(L, 8, 0.0f);
float kx = (float)luaL_optnumber(L, 9, 0.0f);
float ky = (float)luaL_optnumber(L, 10, 0.0f);
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&](){ instance()->print(str, m); });
luax_catchexcept(L, [&](){ instance()->print(str, m); });
}
return 0;
}
@@ -1589,36 +1737,38 @@ int w_printf(lua_State *L)
std::vector<Font::ColoredString> 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);
float angle = 0.0f;
float sx = 1.0f, sy = 1.0f;
float ox = 0.0f, oy = 0.0f;
float kx = 0.0f, ky = 0.0f;
Font::AlignMode align = Font::ALIGN_LEFT;
Matrix4 m;
if (lua_gettop(L) >= 5)
int formatidx = 4;
if (luax_istype(L, 2, math::Transform::type))
{
if (!lua_isnil(L, 5))
{
const char *str = luaL_checkstring(L, 5);
if (!Font::getConstant(str, align))
return luaL_error(L, "Incorrect alignment: %s", str);
}
math::Transform *tf = luax_totype<math::Transform>(L, 2);
m = tf->getMatrix();
formatidx = 3;
}
else
{
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
angle = (float) luaL_optnumber(L, 6, 0.0f);
sx = (float) luaL_optnumber(L, 7, 1.0f);
sy = (float) luaL_optnumber(L, 8, sx);
ox = (float) luaL_optnumber(L, 9, 0.0f);
oy = (float) luaL_optnumber(L, 10, 0.0f);
kx = (float) luaL_optnumber(L, 11, 0.0f);
ky = (float) luaL_optnumber(L, 12, 0.0f);
float angle = (float) luaL_optnumber(L, 6, 0.0f);
float sx = (float) luaL_optnumber(L, 7, 1.0f);
float sy = (float) luaL_optnumber(L, 8, sx);
float ox = (float) luaL_optnumber(L, 9, 0.0f);
float oy = (float) luaL_optnumber(L, 10, 0.0f);
float kx = (float) luaL_optnumber(L, 11, 0.0f);
float ky = (float) luaL_optnumber(L, 12, 0.0f);
m = Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky);
}
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
float wrap = (float)luaL_checknumber(L, formatidx);
const char *astr = lua_isnoneornil(L, formatidx + 1) ? nullptr : luaL_checkstring(L, formatidx + 1);
if (astr != nullptr && !Font::getConstant(astr, align))
return luaL_error(L, "Incorrect alignment: %s", astr);
luax_catchexcept(L, [&](){ instance()->printf(str, wrap, align, m); });
return 0;
@@ -1698,11 +1848,14 @@ int w_points(lua_State *L)
coords[i] = luax_tofloat(L, i + 1);
}
instance()->points(coords, colors, numpoints);
delete[] coords;
if (colors)
delete[] colors;
luax_catchexcept(L,
[&](){ instance()->points(coords, colors, numpoints); },
[&](bool) {
delete[] coords;
if (colors)
delete[] colors;
}
);
return 0;
}
@@ -1738,9 +1891,11 @@ int w_line(lua_State *L)
coords[i] = luax_tofloat(L, i + 1);
}
instance()->polyline(coords, args);
luax_catchexcept(L,
[&](){ instance()->polyline(coords, args); },
[&](bool) { delete[] coords; }
);
delete[] coords;
return 0;
}
@@ -1766,11 +1921,11 @@ int w_rectangle(lua_State *L)
float ry = (float)luaL_optnumber(L, 7, rx);
if (lua_isnoneornil(L, 8))
instance()->rectangle(mode, x, y, w, h, rx, ry);
luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry); });
else
{
int points = (int) luaL_checknumber(L, 8);
instance()->rectangle(mode, x, y, w, h, rx, ry, points);
luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry, points); });
}
return 0;
@@ -1788,11 +1943,11 @@ int w_circle(lua_State *L)
float radius = (float)luaL_checknumber(L, 4);
if (lua_isnoneornil(L, 5))
instance()->circle(mode, x, y, radius);
luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius); });
else
{
int points = (int) luaL_checknumber(L, 5);
instance()->circle(mode, x, y, radius, points);
luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius, points); });
}
return 0;
@@ -1811,11 +1966,11 @@ int w_ellipse(lua_State *L)
float b = (float)luaL_optnumber(L, 5, a);
if (lua_isnoneornil(L, 6))
instance()->ellipse(mode, x, y, a, b);
luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b); });
else
{
int points = (int) luaL_checknumber(L, 6);
instance()->ellipse(mode, x, y, a, b, points);
luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b, points); });
}
return 0;
@@ -1848,11 +2003,11 @@ int w_arc(lua_State *L)
float angle2 = (float) luaL_checknumber(L, startidx + 4);
if (lua_isnoneornil(L, startidx + 5))
instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2);
luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2); });
else
{
int points = (int) luaL_checknumber(L, startidx + 5);
instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points);
luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); });
}
return 0;
@@ -1900,8 +2055,11 @@ int w_polygon(lua_State *L)
// make a closed loop
coords[args] = coords[0];
coords[args+1] = coords[1];
instance()->polygon(mode, coords, args+2);
delete[] coords;
luax_catchexcept(L,
[&](){ instance()->polygon(mode, coords, args+2); },
[&](bool) { delete[] coords; }
);
return 0;
}
@@ -1914,6 +2072,13 @@ int w_push(lua_State *L)
return luaL_error(L, "Invalid graphics stack type: %s", sname);
luax_catchexcept(L, [&](){ instance()->push(stype); });
if (luax_istype(L, 2, math::Transform::type))
{
math::Transform *t = luax_totype<math::Transform>(L, 2);
instance()->applyTransform(t);
}
return 0;
}
@@ -1960,6 +2125,20 @@ int w_origin(lua_State * /*L*/)
return 0;
}
int w_applyTransform(lua_State *L)
{
math::Transform *t = math::luax_checktransform(L, 1);
instance()->applyTransform(t);
return 0;
}
int w_replaceTransform(lua_State *L)
{
math::Transform *t = math::luax_checktransform(L, 1);
instance()->replaceTransform(t);
return 0;
}
int w_transformPoint(lua_State *L)
{
Vector p;
@@ -1987,8 +2166,6 @@ int w_inverseTransformPoint(lua_State *L)
static const luaL_Reg functions[] =
{
{ "reset", w_reset },
{ "clear", w_clear },
{ "discard", w_discard },
{ "present", w_present },
{ "newImage", w_newImage },
@@ -2030,9 +2207,6 @@ static const luaL_Reg functions[] =
{ "getPointSize", w_getPointSize },
{ "setWireframe", w_setWireframe },
{ "isWireframe", w_isWireframe },
{ "newScreenshot", w_newScreenshot },
{ "setCanvas", w_setCanvas },
{ "getCanvas", w_getCanvas },
{ "setShader", w_setShader },
{ "getShader", w_getShader },
@@ -2046,6 +2220,14 @@ static const luaL_Reg functions[] =
{ "getSystemLimits", w_getSystemLimits },
{ "getStats", w_getStats },
{ "beginPass", w_beginPass },
{ "endPass", w_endPass },
{ "renderPass", w_renderPass },
{ "isPassActive", w_isPassActive },
{ "getPassCanvases", w_getPassCanvases },
{ "captureScreenshot", w_captureScreenshot },
{ "draw", w_draw },
{ "print", w_print },
@@ -2057,6 +2239,9 @@ static const luaL_Reg functions[] =
{ "getWidth", w_getWidth },
{ "getHeight", w_getHeight },
{ "getDimensions", w_getDimensions },
{ "getPassWidth", w_getPassWidth },
{ "getPassHeight", w_getPassHeight },
{ "getPassDimensions", w_getPassDimensions },
{ "setScissor", w_setScissor },
{ "intersectScissor", w_intersectScissor },
@@ -2082,6 +2267,8 @@ static const luaL_Reg functions[] =
{ "translate", w_translate },
{ "shear", w_shear },
{ "origin", w_origin },
{ "applyTransform", w_applyTransform },
{ "replaceTransform", w_replaceTransform },
{ "transformPoint", w_transformPoint },
{ "inverseTransformPoint", w_inverseTransformPoint },
+13 -1
View File
@@ -338,10 +338,21 @@ int w_Mesh_attachAttribute(lua_State *L)
Mesh *t = luax_checkmesh(L, 1);
const char *name = luaL_checkstring(L, 2);
Mesh *mesh = luax_checkmesh(L, 3);
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh); });
const char *attachname = luaL_optstring(L, 4, name);
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname); });
return 0;
}
int w_Mesh_detachAttribute(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const char *name = luaL_checkstring(L, 2);
bool success = false;
luax_catchexcept(L, [&](){ success = t->detachAttribute(name); });
luax_pushboolean(L, success);
return 1;
}
int w_Mesh_flush(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -514,6 +525,7 @@ static const luaL_Reg w_Mesh_functions[] =
{ "setAttributeEnabled", w_Mesh_setAttributeEnabled },
{ "isAttributeEnabled", w_Mesh_isAttributeEnabled },
{ "attachAttribute", w_Mesh_attachAttribute },
{ "detachAttribute", w_Mesh_detachAttribute },
{ "flush", w_Mesh_flush },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
+33 -22
View File
@@ -21,6 +21,7 @@
#include "wrap_Shader.h"
#include "graphics/wrap_Texture.h"
#include "math/MathModule.h"
#include "math/Transform.h"
#include <string>
#include <algorithm>
@@ -48,14 +49,12 @@ int w_Shader_getWarnings(lua_State *L)
static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info)
{
return std::min(std::max(lua_gettop(L) - startidx, 1), info->count);
return std::min(std::max(lua_gettop(L) - startidx + 1, 1), info->count);
}
template <typename T>
static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components, int count)
static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count)
{
T *values = shader->getScratchBuffer<T>(components * count);
if (components == 1)
{
for (int i = 0; i < count; ++i)
@@ -76,16 +75,15 @@ static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components
lua_pop(L, components);
}
}
return values;
}
int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
{
int count = _getCount(L, startidx, info);
int components = info->components;
float *values = info->floats;
float *values = _getNumbers<float>(L, startidx, shader, components, count);
_updateNumbers(L, startidx, values, components, count);
if (colors && graphics::isGammaCorrect())
{
@@ -99,15 +97,15 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader
}
}
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); });
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0;
}
int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{
int count = _getCount(L, startidx, info);
int *values = _getNumbers<int>(L, startidx, shader, info->components, count);
luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); });
_updateNumbers(L, startidx, info->ints, info->components, count);
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0;
}
@@ -116,15 +114,15 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
int count = _getCount(L, startidx, info);
int components = info->components;
// We have to send booleans as ints or floats.
float *values = shader->getScratchBuffer<float>(components * count);
// We have to send booleans as ints.
int *values = info->ints;
if (components == 1)
{
for (int i = 0; i < count; i++)
{
luaL_checktype(L, startidx + i, LUA_TBOOLEAN);
values[i] = (float) lua_toboolean(L, startidx + i);
values[i] = (int) lua_toboolean(L, startidx + i);
}
}
else
@@ -137,14 +135,14 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
{
lua_rawgeti(L, startidx + i, k);
luaL_checktype(L, -1, LUA_TBOOLEAN);
values[i * components + k - 1] = (float) lua_toboolean(L, -1);
values[i * components + k - 1] = (int) lua_toboolean(L, -1);
}
lua_pop(L, components);
}
}
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); });
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0;
}
@@ -163,10 +161,17 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
int rows = info->matrix.rows;
int elements = columns * rows;
float *values = shader->getScratchBuffer<float>(elements * count);
float *values = info->floats;
for (int i = 0; i < count; i++)
{
if (columns == 4 && rows == 4 && luax_istype(L, startidx + i, math::Transform::type))
{
math::Transform *t = luax_totype<math::Transform>(L, startidx + i);
memcpy(&values[i * 16], t->getMatrix().getElements(), sizeof(float) * 16);
continue;
}
luaL_checktype(L, startidx + i, LUA_TTABLE);
lua_rawgeti(L, startidx + i, 1);
@@ -243,15 +248,21 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
}
}
shader->sendMatrices(info, values, count);
shader->updateUniform(info, count);
return 0;
}
int w_Shader_sendTexture(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{
// We don't support arrays of textures (yet).
Texture *texture = luax_checktexture(L, startidx);
luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); });
int count = _getCount(L, startidx, info);
std::vector<Texture *> textures;
textures.reserve(count);
for (int i = 0; i < count; i++)
textures.push_back(luax_checktexture(L, startidx + i));
luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); });
return 0;
}
@@ -277,7 +288,7 @@ int w_Shader_send(lua_State *L)
case Shader::UNIFORM_BOOL:
return w_Shader_sendBooleans(L, startidx, shader, info);
case Shader::UNIFORM_SAMPLER:
return w_Shader_sendTexture(L, startidx, shader, info);
return w_Shader_sendTextures(L, startidx, shader, info);
default:
return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
}
@@ -23,6 +23,7 @@
#include "Image.h"
#include "Canvas.h"
#include "graphics/wrap_Texture.h"
#include "math/wrap_Transform.h"
// C++
#include <typeinfo>
@@ -51,24 +52,37 @@ static inline int w_SpriteBatch_add_or_set(lua_State *L, SpriteBatch *t, int sta
else if (lua_isnil(L, startidx) && !lua_isnoneornil(L, startidx + 1))
return luax_typerror(L, startidx, "Quad");
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
if (luax_istype(L, startidx, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, startidx);
luax_catchexcept(L, [&]() {
if (quad)
index = t->addq(quad, tf->getMatrix(), index);
else
index = t->add(tf->getMatrix(), index);
});
}
else
{
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&]() {
if (quad)
index = t->addq(quad, m, index);
else
index = t->add(m, index);
});
luax_catchexcept(L, [&]() {
if (quad)
index = t->addq(quad, m, index);
else
index = t->add(m, index);
});
}
return index;
}
+45 -26
View File
@@ -19,6 +19,7 @@
**/
#include "wrap_Text.h"
#include "math/wrap_Transform.h"
namespace love
{
@@ -126,24 +127,33 @@ int w_Text_add(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = 0;
std::vector<Font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
float x = (float) luaL_optnumber(L, 3, 0.0);
float y = (float) luaL_optnumber(L, 4, 0.0);
float a = (float) luaL_optnumber(L, 5, 0.0);
float sx = (float) luaL_optnumber(L, 6, 1.0);
float sy = (float) luaL_optnumber(L, 7, sx);
float ox = (float) luaL_optnumber(L, 8, 0.0);
float oy = (float) luaL_optnumber(L, 9, 0.0);
float kx = (float) luaL_optnumber(L, 10, 0.0);
float ky = (float) luaL_optnumber(L, 11, 0.0);
if (luax_istype(L, 3, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, 3);
luax_catchexcept(L, [&](){ index = t->add(text, tf->getMatrix()); });
}
else
{
float x = (float) luaL_optnumber(L, 3, 0.0);
float y = (float) luaL_optnumber(L, 4, 0.0);
float a = (float) luaL_optnumber(L, 5, 0.0);
float sx = (float) luaL_optnumber(L, 6, 1.0);
float sy = (float) luaL_optnumber(L, 7, sx);
float ox = (float) luaL_optnumber(L, 8, 0.0);
float oy = (float) luaL_optnumber(L, 9, 0.0);
float kx = (float) luaL_optnumber(L, 10, 0.0);
float ky = (float) luaL_optnumber(L, 11, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&](){ index = t->add(text, m); });
}
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
int index = 0;
luax_catchexcept(L, [&](){ index = t->add(text, m); });
lua_pushnumber(L, index + 1);
return 1;
}
@@ -151,6 +161,8 @@ int w_Text_addf(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = 0;
std::vector<Font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
@@ -162,21 +174,28 @@ int w_Text_addf(lua_State *L)
if (!Font::getConstant(alignstr, align))
return luaL_error(L, "Invalid align mode: %s", alignstr);
float x = (float) luaL_optnumber(L, 5, 0.0);
float y = (float) luaL_optnumber(L, 6, 0.0);
float a = (float) luaL_optnumber(L, 7, 0.0);
float sx = (float) luaL_optnumber(L, 8, 1.0);
float sy = (float) luaL_optnumber(L, 9, sx);
float ox = (float) luaL_optnumber(L, 10, 0.0);
float oy = (float) luaL_optnumber(L, 11, 0.0);
float kx = (float) luaL_optnumber(L, 12, 0.0);
float ky = (float) luaL_optnumber(L, 13, 0.0);
if (luax_istype(L, 5, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, 5);
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, tf->getMatrix()); });
}
else
{
float x = (float) luaL_optnumber(L, 5, 0.0);
float y = (float) luaL_optnumber(L, 6, 0.0);
float a = (float) luaL_optnumber(L, 7, 0.0);
float sx = (float) luaL_optnumber(L, 8, 1.0);
float sy = (float) luaL_optnumber(L, 9, sx);
float ox = (float) luaL_optnumber(L, 10, 0.0);
float oy = (float) luaL_optnumber(L, 11, 0.0);
float kx = (float) luaL_optnumber(L, 12, 0.0);
float ky = (float) luaL_optnumber(L, 13, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); });
}
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
int index = 0;
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); });
lua_pushnumber(L, index + 1);
return 1;
}
+11
View File
@@ -25,6 +25,7 @@
#include "common/int.h"
#include "common/StringMap.h"
#include "BezierCurve.h"
#include "Transform.h"
// STL
#include <cmath>
@@ -374,6 +375,16 @@ BezierCurve *Math::newBezierCurve(const std::vector<Vector> &points)
return new BezierCurve(points);
}
Transform *Math::newTransform()
{
return new Transform();
}
Transform *Math::newTransform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky)
{
return new Transform(x, y, a, sx, sy, ox, oy, kx, ky);
}
std::string hash(HashFunction::Function function, Data *input)
{
return hash(function, (const char*) input->getData(), input->getSize());
+4
View File
@@ -45,6 +45,7 @@ namespace math
{
class BezierCurve;
class Transform;
struct Triangle
{
@@ -176,6 +177,9 @@ public:
**/
BezierCurve *newBezierCurve(const std::vector<Vector> &points);
Transform *newTransform();
Transform *newTransform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky);
// Implements Module.
virtual ModuleType getModuleType() const
{
+133
View File
@@ -0,0 +1,133 @@
/**
* Copyright (c) 2006-2016 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 "Transform.h"
namespace love
{
namespace math
{
love::Type Transform::type("Transform", &Object::type);
Transform::Transform()
: matrix()
, inverseDirty(true)
, inverseMatrix()
{
}
Transform::Transform(const Matrix4 &m)
: matrix(m)
, inverseDirty(true)
, inverseMatrix()
{
}
Transform::Transform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky)
: matrix(x, y, a, sx, sy, ox, oy, kx, ky)
, inverseDirty(true)
, inverseMatrix()
{
}
Transform::~Transform()
{
}
Transform *Transform::clone()
{
return new Transform(*this);
}
Transform *Transform::inverse()
{
return new Transform(getInverseMatrix());
}
void Transform::apply(Transform *other)
{
matrix *= other->getMatrix();
inverseDirty = true;
}
void Transform::translate(float x, float y)
{
matrix.translate(x, y);
inverseDirty = true;
}
void Transform::rotate(float angle)
{
matrix.rotate(angle);
inverseDirty = true;
}
void Transform::scale(float x, float y)
{
matrix.scale(x, y);
inverseDirty = true;
}
void Transform::shear(float x, float y)
{
matrix.shear(x, y);
inverseDirty = true;
}
void Transform::reset()
{
matrix.setIdentity();
inverseDirty = true;
}
void Transform::setTransformation(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky)
{
matrix.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
inverseDirty = true;
}
love::Vector Transform::transformPoint(love::Vector p) const
{
love::Vector result;
matrix.transform(&result, &p, 1);
return result;
}
love::Vector Transform::inverseTransformPoint(love::Vector p)
{
love::Vector result;
getInverseMatrix().transform(&result, &p, 1);
return result;
}
const Matrix4 &Transform::getMatrix() const
{
return matrix;
}
void Transform::setMatrix(const Matrix4 &m)
{
matrix = m;
inverseDirty = true;
}
} // math
} // love
+85
View File
@@ -0,0 +1,85 @@
/**
* Copyright (c) 2006-2016 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/Object.h"
#include "common/Matrix.h"
#include "common/Vector.h"
namespace love
{
namespace math
{
class Transform : public Object
{
public:
static love::Type type;
Transform();
Transform(const Matrix4 &m);
Transform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky);
virtual ~Transform();
Transform *clone();
Transform *inverse();
void apply(Transform *other);
void translate(float x, float y);
void rotate(float angle);
void scale(float x, float y);
void shear(float x, float y);
void reset();
void setTransformation(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky);
love::Vector transformPoint(love::Vector p) const;
love::Vector inverseTransformPoint(love::Vector p);
const Matrix4 &getMatrix() const;
void setMatrix(const Matrix4 &m);
private:
inline const Matrix4 &getInverseMatrix()
{
if (inverseDirty)
{
inverseDirty = false;
inverseMatrix = matrix.inverse();
}
return inverseMatrix;
}
Matrix4 matrix;
bool inverseDirty;
Matrix4 inverseMatrix;
}; // Transform
} // math
} // love
+29
View File
@@ -22,8 +22,10 @@
#include "wrap_RandomGenerator.h"
#include "wrap_BezierCurve.h"
#include "wrap_CompressedData.h"
#include "wrap_Transform.h"
#include "MathModule.h"
#include "BezierCurve.h"
#include "Transform.h"
#include "common/b64.h"
#include <cmath>
@@ -118,6 +120,31 @@ int w_newBezierCurve(lua_State *L)
return 1;
}
int w_newTransform(lua_State *L)
{
Transform *t = nullptr;
if (lua_isnoneornil(L, 1))
t = Math::instance.newTransform();
else
{
float x = (float) luaL_checknumber(L, 1);
float y = (float) luaL_checknumber(L, 2);
float a = (float) luaL_optnumber(L, 3, 0.0);
float sx = (float) luaL_optnumber(L, 4, 1.0);
float sy = (float) luaL_optnumber(L, 5, sx);
float ox = (float) luaL_optnumber(L, 6, 0.0);
float oy = (float) luaL_optnumber(L, 7, 0.0);
float kx = (float) luaL_optnumber(L, 8, 0.0);
float ky = (float) luaL_optnumber(L, 9, 0.0);
t = Math::instance.newTransform(x, y, a, sx, sy, ox, oy, kx, ky);
}
luax_pushtype(L, t);
t->release();
return 1;
}
int w_triangulate(lua_State *L)
{
std::vector<love::Vector> vertices;
@@ -507,6 +534,7 @@ static const luaL_Reg functions[] =
{ "_getRandomGenerator", w__getRandomGenerator },
{ "newRandomGenerator", w_newRandomGenerator },
{ "newBezierCurve", w_newBezierCurve },
{ "newTransform", w_newTransform },
{ "triangulate", w_triangulate },
{ "isConvex", w_isConvex },
{ "gammaToLinear", w_gammaToLinear },
@@ -525,6 +553,7 @@ static const lua_CFunction types[] =
luaopen_randomgenerator,
luaopen_beziercurve,
luaopen_compresseddata,
luaopen_transform,
0
};
+305
View File
@@ -0,0 +1,305 @@
/**
* Copyright (c) 2006-2016 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_Transform.h"
namespace love
{
namespace math
{
Transform *luax_checktransform(lua_State *L, int idx)
{
return luax_checktype<Transform>(L, idx, Transform::type);
}
int w_Transform_clone(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
Transform *newtransform = t->clone();
luax_pushtype(L, newtransform);
newtransform->release();
return 1;
}
int w_Transform_inverse(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
Transform *inverse = t->inverse();
luax_pushtype(L, inverse);
inverse->release();
return 1;
}
int w_Transform_apply(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
Transform *other = luax_checktransform(L, 2);
t->apply(other);
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_translate(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
float x = (float) luaL_checknumber(L, 2);
float y = (float) luaL_checknumber(L, 3);
t->translate(x, y);
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_rotate(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
float angle = (float) luaL_checknumber(L, 2);
t->rotate(angle);
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_scale(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
float sx = (float) luaL_checknumber(L, 2);
float sy = (float) luaL_optnumber(L, 3, sx);
t->scale(sx, sy);
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_shear(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
float kx = (float) luaL_checknumber(L, 2);
float ky = (float) luaL_checknumber(L, 3);
t->shear(kx, ky);
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_reset(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
t->reset();
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_setTransformation(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
float x = (float) luaL_optnumber(L, 2, 0.0);
float y = (float) luaL_optnumber(L, 3, 0.0);
float a = (float) luaL_optnumber(L, 4, 0.0);
float sx = (float) luaL_optnumber(L, 5, 1.0);
float sy = (float) luaL_optnumber(L, 6, sx);
float ox = (float) luaL_optnumber(L, 7, 0.0);
float oy = (float) luaL_optnumber(L, 8, 0.0);
float kx = (float) luaL_optnumber(L, 9, 0.0);
float ky = (float) luaL_optnumber(L, 10, 0.0);
t->setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_setMatrix(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
bool columnmajor = false;
int idx = 2;
if (lua_isboolean(L, idx))
{
columnmajor = lua_toboolean(L, idx);
idx++;
}
float elements[16];
if (lua_istable(L, idx))
{
lua_rawgeti(L, idx, 1);
bool tableoftables = lua_istable(L, -1);
lua_pop(L, 1);
if (tableoftables)
{
if (columnmajor)
{
for (int column = 0; column < 4; column++)
{
lua_rawgeti(L, idx, column + 1);
for (int row = 0; row < 4; row++)
{
lua_rawgeti(L, -(row + 1), row + 1);
elements[column * 4 + row] = (float) luaL_checknumber(L, -1);
}
lua_pop(L, 4 + 1);
}
}
else
{
for (int row = 0; row < 4; row++)
{
lua_rawgeti(L, idx, row + 1);
for (int column = 0; column < 4; column++)
{
// The table has the matrix elements laid out in row-major
// order, but we need to store them column-major in memory.
lua_rawgeti(L, -(column + 1), column + 1);
elements[column * 4 + row] = (float) luaL_checknumber(L, -1);
}
lua_pop(L, 4 + 1);
}
}
}
else
{
if (columnmajor)
{
for (int column = 0; column < 4; column++)
{
for (int row = 0; row < 4; row++)
{
lua_rawgeti(L, idx, column * 4 + row + 1);
elements[column * 4 + row] = (float) luaL_checknumber(L, -1);
}
}
}
else
{
for (int column = 0; column < 4; column++)
{
for (int row = 0; row < 4; row++)
{
// The table has the matrix elements laid out in row-major
// order, but we need to store them column-major in memory.
lua_rawgeti(L, idx, row * 4 + column + 1);
elements[column * 4 + row] = (float) luaL_checknumber(L, -1);
}
}
}
lua_pop(L, 16);
}
}
else
{
if (columnmajor)
{
for (int i = 0; i < 16; i++)
elements[i] = (float) luaL_checknumber(L, idx + i);
}
else
{
for (int column = 0; column < 4; column++)
{
for (int row = 0; row < 4; row++)
elements[column * 4 + row] = (float) luaL_checknumber(L, row * 4 + column + idx);
}
}
}
t->setMatrix(Matrix4(elements));
lua_pushvalue(L, 1);
return 1;
}
int w_Transform_getMatrix(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
const float *elements = t->getMatrix().getElements();
// We want to push elements in row-major order, but they're stored column-
// major.
for (int row = 0; row < 4; row++)
{
for (int column = 0; column < 4; column++)
lua_pushnumber(L, elements[column * 4 + row]);
}
return 16;
}
int w_Transform_transformPoint(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
love::Vector p;
p.x = (float) luaL_checknumber(L, 2);
p.y = (float) luaL_checknumber(L, 3);
p = t->transformPoint(p);
lua_pushnumber(L, p.x);
lua_pushnumber(L, p.y);
return 2;
}
int w_Transform_inverseTransformPoint(lua_State *L)
{
Transform *t = luax_checktransform(L, 1);
love::Vector p;
p.x = (float) luaL_checknumber(L, 2);
p.y = (float) luaL_checknumber(L, 3);
p = t->inverseTransformPoint(p);
lua_pushnumber(L, p.x);
lua_pushnumber(L, p.y);
return 2;
}
int w_Transform__mul(lua_State *L)
{
Transform *t1 = luax_checktransform(L, 1);
Transform *t2 = luax_checktransform(L, 2);
Transform *t3 = new Transform(t1->getMatrix() * t2->getMatrix());
luax_pushtype(L, t3);
t3->release();
return 1;
}
static const luaL_Reg functions[] =
{
{ "clone", w_Transform_clone },
{ "inverse", w_Transform_inverse },
{ "apply", w_Transform_apply },
{ "translate", w_Transform_translate },
{ "rotate", w_Transform_rotate },
{ "scale", w_Transform_scale },
{ "shear", w_Transform_shear },
{ "reset", w_Transform_reset },
{ "setTransformation", w_Transform_setTransformation },
{ "transformPoint", w_Transform_transformPoint },
{ "inverseTransformPoint", w_Transform_inverseTransformPoint },
{ "__mul", w_Transform__mul },
{ 0, 0 }
};
extern "C" int luaopen_transform(lua_State *L)
{
return luax_register_type(L, &Transform::type, functions, nullptr);
}
} // math
} // love
+36
View File
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2006-2016 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 "Transform.h"
#include "common/runtime.h"
namespace love
{
namespace math
{
Transform *luax_checktransform(lua_State *L, int idx);
extern "C" int luaopen_transform(lua_State *L);
} // math
} // love
+8
View File
@@ -32,6 +32,12 @@
namespace love
{
namespace graphics
{
class Graphics;
}
namespace window
{
@@ -108,6 +114,8 @@ public:
// Implements Module.
virtual ModuleType getModuleType() const { return M_WINDOW; }
virtual void setGraphics(graphics::Graphics *graphics) = 0;
virtual bool setWindow(int width = 800, int height = 600, WindowSettings *settings = nullptr) = 0;
virtual void getWindow(int &width, int &height, WindowSettings &settings) = 0;
+30 -16
View File
@@ -79,9 +79,16 @@ Window::~Window()
{
close();
graphics.set(nullptr);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
void Window::setGraphics(graphics::Graphics *graphics)
{
this->graphics.set(graphics);
}
void Window::setGLFramebufferAttributes(int msaa, bool sRGB)
{
// Set GL window / framebuffer attributes.
@@ -401,6 +408,12 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
bool Window::setWindow(int width, int height, WindowSettings *settings)
{
if (!graphics.get())
graphics.set(Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS));
if (graphics.get() && graphics->isPassActive())
throw love::Exception("setMode cannot be called while a render pass is active in love.graphics.");
WindowSettings f;
if (settings)
@@ -507,9 +520,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
updateSettings(f, false);
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
gfx->setMode(pixelWidth, pixelHeight);
if (graphics.get())
graphics->setMode(pixelWidth, pixelHeight);
#ifdef LOVE_ANDROID
love::android::setImmersive(f.fullscreen);
@@ -528,9 +540,8 @@ bool Window::onSizeChanged(int width, int height)
SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
gfx->setViewportSize(pixelWidth, pixelHeight);
if (graphics.get())
graphics->setViewportSize(pixelWidth, pixelHeight);
return true;
}
@@ -596,13 +607,9 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
// May be 0 if the refresh rate can't be determined.
settings.refreshrate = (double) dmode.refresh_rate;
if (updateGraphicsViewport)
{
// Update the viewport size now instead of waiting for event polling.
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
gfx->setViewportSize(pixelWidth, pixelHeight);
}
// Update the viewport size now instead of waiting for event polling.
if (updateGraphicsViewport && graphics.get())
graphics->setViewportSize(pixelWidth, pixelHeight);
}
void Window::getWindow(int &width, int &height, WindowSettings &newsettings)
@@ -618,9 +625,13 @@ void Window::getWindow(int &width, int &height, WindowSettings &newsettings)
void Window::close()
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
gfx->unSetMode();
if (graphics.get())
{
if (graphics->isPassActive())
throw love::Exception("close cannot be called while a render pass is active in love.graphics.");
graphics->unSetMode();
}
if (context)
{
@@ -646,6 +657,9 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
if (!window)
return false;
if (graphics.get() && graphics->isPassActive())
throw love::Exception("setFullscreen cannot be called while a render pass is active in love.graphics.");
WindowSettings newsettings = settings;
newsettings.fullscreen = fullscreen;
newsettings.fstype = fstype;
+4
View File
@@ -41,6 +41,8 @@ public:
Window();
~Window();
void setGraphics(graphics::Graphics *graphics);
bool setWindow(int width = 800, int height = 600, WindowSettings *settings = nullptr);
void getWindow(int &width, int &height, WindowSettings &settings);
@@ -149,6 +151,8 @@ private:
bool hasSDL203orEarlier;
ContextAttribs contextAttribs;
StrongRef<graphics::Graphics> graphics;
}; // Window
} // sdl
+9 -7
View File
@@ -119,7 +119,7 @@ int w_setMode(lua_State *L)
if (lua_isnoneornil(L, 3))
{
luax_pushboolean(L, instance()->setWindow(w, h, nullptr));
luax_catchexcept(L, [&](){ luax_pushboolean(L, instance()->setWindow(w, h, nullptr)); });
return 1;
}
@@ -266,10 +266,12 @@ int w_setFullscreen(lua_State *L)
return luaL_error(L, "Invalid fullscreen type: %s", typestr);
bool success = false;
if (fstype == Window::FULLSCREEN_MAX_ENUM)
success = instance()->setFullscreen(fullscreen);
else
success = instance()->setFullscreen(fullscreen, fstype);
luax_catchexcept(L, [&]() {
if (fstype == Window::FULLSCREEN_MAX_ENUM)
success = instance()->setFullscreen(fullscreen);
else
success = instance()->setFullscreen(fullscreen, fstype);
});
luax_pushboolean(L, success);
return 1;
@@ -296,9 +298,9 @@ int w_isOpen(lua_State *L)
return 1;
}
int w_close(lua_State * /*L*/)
int w_close(lua_State *L)
{
instance()->close();
luax_catchexcept(L, [&]() { instance()->close(); });
return 0;
}
+19 -11
View File
@@ -122,11 +122,10 @@ function love.arg.parse_option(m, i)
m.arg = {}
for j=i,i+m.a-1 do
table.insert(m.arg, arg[j])
i = j
end
end
return i
return m.a
end
function love.arg.parse_options()
@@ -134,15 +133,17 @@ function love.arg.parse_options()
local game
local argc = #arg
for i=1,argc do
local i = 1
while i <= argc do
-- Look for options.
local s, e, m = string.find(arg[i], "%-%-(.+)")
local m = string.match(arg[i], "^%-%-(.+)")
if m and love.arg.options[m] then
i = love.arg.parse_option(love.arg.options[m], i+1)
i = i + love.arg.parse_option(love.arg.options[m], i+1)
elseif not game then
game = i
end
i = i + 1
end
if not love.arg.options.game.set then
@@ -545,9 +546,14 @@ function love.run()
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.graphics and love.graphics.isActive() then
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
if love.drawpasses then love.drawpasses() end
love.graphics.beginPass(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
love.graphics.endPass()
love.graphics.present()
end
@@ -598,15 +604,18 @@ function love.errhand(msg)
end
end
if love.audio then love.audio.stop() end
love.graphics.reset()
if love.graphics.isPassActive() then
love.graphics.endPass()
end
local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14)))
love.graphics.setBackgroundColor(89/255, 157/255, 220/255)
love.graphics.setColor(1, 1, 1, 1)
local trace = debug.traceback()
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
local err = {}
@@ -628,9 +637,7 @@ function love.errhand(msg)
local function draw()
local pos = love.window.toPixels(70)
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
love.graphics.present()
end
while true do
@@ -652,7 +659,8 @@ function love.errhand(msg)
end
end
draw()
love.graphics.renderPass(89/255, 157/255, 220/255, draw)
love.graphics.present()
if love.timer then
love.timer.sleep(0.1)
+33 -28
View File
@@ -218,30 +218,33 @@ const unsigned char boot_lua[] =
0x64, 0x6f, 0x0a,
0x09, 0x09, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6d, 0x2e,
0x61, 0x72, 0x67, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x5b, 0x6a, 0x5d, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x6a, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x0a,
0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x2e, 0x61, 0x0a,
0x65, 0x6e, 0x64, 0x0a,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e,
0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x67, 0x63, 0x20, 0x3d, 0x20, 0x23, 0x61, 0x72, 0x67, 0x0a,
0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x61, 0x72, 0x67, 0x63, 0x20, 0x64, 0x6f, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x0a,
0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x69, 0x20, 0x3c, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x63, 0x20, 0x64,
0x6f, 0x0a,
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x4c, 0x6f, 0x6f, 0x6b, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x2e, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x2c, 0x20, 0x65, 0x2c, 0x20, 0x6d, 0x20, 0x3d, 0x20,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x61, 0x72, 0x67, 0x5b, 0x69, 0x5d,
0x2c, 0x20, 0x22, 0x25, 0x2d, 0x25, 0x2d, 0x28, 0x2e, 0x2b, 0x29, 0x22, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 0x61, 0x72, 0x67, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x22, 0x5e, 0x25,
0x2d, 0x25, 0x2d, 0x28, 0x2e, 0x2b, 0x29, 0x22, 0x29, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72,
0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6d, 0x5d, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x70, 0x61,
0x72, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72,
0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6d, 0x5d, 0x2c, 0x20, 0x69, 0x2b, 0x31, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72,
0x67, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76,
0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6d, 0x5d, 0x2c, 0x20,
0x69, 0x2b, 0x31, 0x29, 0x0a,
0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x6f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68,
@@ -1005,14 +1008,19 @@ const unsigned char boot_lua[] =
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x2e, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63,
0x6c, 0x65, 0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x2e, 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f,
0x72, 0x28, 0x29, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f,
0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x70, 0x61, 0x73,
0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77,
0x70, 0x61, 0x73, 0x73, 0x65, 0x73, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x62,
0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70,
0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64,
0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x29, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x65,
0x6e, 0x64, 0x50, 0x61, 0x73, 0x73, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70,
0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
@@ -1098,23 +1106,21 @@ const unsigned char boot_lua[] =
0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65, 0x73,
0x65, 0x74, 0x28, 0x29, 0x0a,
0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e,
0x69, 0x73, 0x50, 0x61, 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x65, 0x6e,
0x64, 0x50, 0x61, 0x73, 0x73, 0x28, 0x29, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65,
0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x4e, 0x65, 0x77, 0x46, 0x6f,
0x6e, 0x74, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65,
0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x31,
0x34, 0x29, 0x29, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74,
0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x38, 0x39,
0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x37, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x32, 0x30,
0x2f, 0x32, 0x35, 0x35, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74,
0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x72, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x62,
0x75, 0x67, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x6c, 0x65,
0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67,
0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
0x29, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x72, 0x69,
0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a,
@@ -1148,16 +1154,10 @@ const unsigned char boot_lua[] =
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65,
0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x37,
0x30, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x6c,
0x65, 0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e,
0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
0x28, 0x29, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x66, 0x28, 0x70, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20,
0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x57,
0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x20, 0x2d, 0x20, 0x70, 0x6f, 0x73, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72,
0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28,
@@ -1194,7 +1194,12 @@ const unsigned char boot_lua[] =
0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65,
0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x28, 0x38, 0x39, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x31,
0x35, 0x37, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x64,
0x72, 0x61, 0x77, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72,
0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65,