Compare commits

...

234 Commits

Author SHA1 Message Date
Tom Rothamel 0b8bf87eaf doc: Fix changelog and update credits. 2019-09-26 20:12:56 -04:00
Tom Rothamel 857d776538 Remove information about mac signing.
It's best done with renpy/mac-notarization, rather than the
current system.
2019-09-26 18:33:29 -04:00
Tom Rothamel df51f5233f Finish notarization of Ren'Py.
This includes the notarization of the DMG that Ren'Py is
distributed with.
2019-09-24 20:50:50 -04:00
Tom Rothamel aed2625486 Merge pull request #2004 from uyjulian/avoid_spam_threading
Join previous autosave thread before spawning a new one
2019-09-24 01:58:56 -04:00
uyjulian 47271b7af9 Join previous autosave thread before spawning a new one 2019-09-24 00:53:54 -05:00
Tom Rothamel d8050e9f5d Use a tarball for notarization.
Turns out that a .zip can't really represent the extended
attributes used for signing some files properly, but the
tar format can. So we use that to ship things over, and
get a working .dmg.
2019-09-24 01:17:34 -04:00
Tom Rothamel b639171118 Scripts for notarizing Ren'Py. 2019-09-23 22:14:46 -04:00
Tom Rothamel 25e66d3150 More mac codesigning improvements.
* Do not sign the dmg.
* Fix giving the path to the entitlements.
2019-09-21 15:34:41 -04:00
Tom Rothamel dcaa97a70e Improve support for codesigning with entitlements. 2019-09-21 01:08:46 -04:00
Tom Rothamel e0fd8dc4d5 Update changelog and credits. 2019-09-20 00:08:04 -04:00
Tom Rothamel 2cc4bdd407 doc: Mention the Android version change in the changelog. 2019-09-19 23:41:09 -04:00
Tom Rothamel 8ccfb9abc7 Check in updated keywords. 2019-09-19 23:21:41 -04:00
Tom Rothamel 4734fb62f4 Merge pull request #2002 from Herpior/viewport-edgescroll-fix
Fixed bug with edgescroll in viewport
2019-09-19 22:08:25 -04:00
Tom Rothamel 8353120f55 doc: Changelog and sponsors. 2019-09-19 21:17:23 -04:00
Tom Rothamel 5a081b6dc7 doc: Web changelog, from Beuc. 2019-09-19 19:57:16 -04:00
Tom Rothamel 52cdcb699a doc: Changelog. 2019-09-19 02:25:05 -04:00
Tom Rothamel e190596f3d Add backwards compatibility for force_step. 2019-09-19 02:18:14 -04:00
Tom Rothamel 9352339f5b doc: Audio directory. 2019-09-18 01:41:58 -04:00
Tom Rothamel 5c315d02d1 doc: Fix. 2019-09-17 19:43:49 -04:00
Tom Rothamel 86a95abb60 Document AudioData. 2019-09-17 19:42:13 -04:00
Tom Rothamel cc03f54738 Allow audio data to be encapsulated in AudioData. 2019-09-16 23:56:26 -04:00
Tom Rothamel f40ff85436 Include the wave and sunau files with Ren'Py. 2019-09-16 22:25:53 -04:00
Tom Rothamel 3c6918c36f Fix crash caused by non-existent screen in save. 2019-09-15 10:40:35 -04:00
Tom Rothamel 754aec0507 Ensure that InputValue is called when an Input is shown for the first time. 2019-09-14 21:02:31 -04:00
Tom Rothamel 9f800275ad Remove spurious return. 2019-09-14 19:00:28 -04:00
Tom Rothamel cf05311158 Make the name of the screen used by renpy.input configurable. 2019-09-14 18:59:59 -04:00
Tom Rothamel 644d24dcc4 Avoid error if a a substitution doesn't exist during screen prep.
I believe that improvements to screen analysis make text more
predictable, which caused this problem.
2019-09-14 18:59:59 -04:00
Roope Herpiö 87f076e78c Mouse motion events on viewport no longer reset self.edge_last_st unless necessary 2019-09-13 21:11:35 +03:00
Tom Rothamel b0cc98cea2 Merge pull request #1998 from Andykl/missed-ignore
Minor fixes
2019-09-13 14:00:56 -04:00
Tom Rothamel 498ea82ec9 Document how arguments are passed to screens in modern Ren'Py. 2019-09-12 21:41:58 -04:00
Tom Rothamel e587790021 Move the start callbacks after the defaults.
Per #2001.
2019-09-12 01:05:57 -04:00
Tom Rothamel bbaf654ef9 Mark Ren'Py collection types as pure.
More specifically, __renpy__list__, __renpy__set__, and
__renpy__dict__.

Per #1935.
2019-09-11 22:57:29 -04:00
Tom Rothamel 44b5af95ff Merge pull request #2000 from Andykl/fix-drag-issue
Set correct style_prefix for drop object after restart interaction.
2019-09-10 19:44:40 -04:00
Andy_kl 5470f86c39 Set correct style prefix for drop object after restart interaction. 2019-09-11 02:38:44 +03:00
Tom Rothamel 8e04c766de Hide the notify screen on rollback.
Fixes #1951.
2019-09-10 01:17:32 -04:00
Tom Rothamel 82b396d424 The non-screens menu indicates that it shows the window.
Fixes #1981.
2019-09-09 23:27:27 -04:00
Tom Rothamel a0dad58dbf Ignore printable keycodes without unicode.
This should fix a race condition where that could cause unprefixed
key commands (like 'v' or 'a') to trigger if KEYDOWN is present
but the TEXTINPUT hasn't been delivered yet.
2019-09-09 22:57:33 -04:00
Tom Rothamel d28ecb39ff In a multipart statment like menu, call from should use the first part.
Fixes #1996.
2019-09-08 20:28:56 -04:00
Andy_kl 6597edcada Add vibrate to drag alternate 2019-09-09 02:03:19 +03:00
Andy_kl 5c44e738e6 Fix wrong event ignore 2019-09-09 01:49:44 +03:00
Tom Rothamel 7e295d67b5 Call renpy.shown_window from nvl_menu.
Fixes #1934.
2019-09-08 01:26:49 -04:00
Tom Rothamel 61eb81c79d Lock a button's style after _in_current_store.
Fixes #1973.
2019-09-08 01:15:05 -04:00
Tom Rothamel df32d31113 Throw a hard exception when gui.init is called outside of the init phase. 2019-09-08 00:59:07 -04:00
Tom Rothamel 59587015ba doc: Fix incomplete sentence.
Fixes #1962.
2019-09-08 00:55:27 -04:00
Tom Rothamel 312c49f194 Fix #1982. 2019-09-08 00:20:49 -04:00
Tom Rothamel 7c75a79c13 Deal with a problem when a FontGroup is replaced.
Fixes #1994.
2019-09-07 23:56:17 -04:00
Tom Rothamel e6546189eb ffmedia: Fix the mixing of mono files into stereo.
Fixes #1943.
2019-09-07 23:28:11 -04:00
Tom Rothamel 9500f658a3 Merge pull request #1889 from Andykl/change-get-layer
Receive a layer from default_layer in all exports funcs
2019-09-07 11:06:40 -04:00
Tom Rothamel 6d9bcff1f6 Merge pull request #1937 from Moshibit/master
patch - minor fixes
2019-09-07 11:05:57 -04:00
Tom Rothamel 3a352af1e5 Merge pull request #1965 from Beuc/gleshighp
gles: handle GPUs that do not support highp
2019-09-07 11:04:34 -04:00
Tom Rothamel 87b323a3d4 Tweak documentation. 2019-09-07 10:56:37 -04:00
Tom Rothamel 1bf97965b8 Merge pull request #1967 from Andykl/is_init_phase
Added is_init_phase in exports.
2019-09-07 10:55:29 -04:00
Tom Rothamel eafcbf572f Merge pull request #1997 from pionere/sound-fix
fixed sound-queue call_back
2019-09-07 10:54:45 -04:00
pionere 427a1542d8 fixed sound-queue call_back
call queue_empty_callback only if the queue is really empty
2019-09-07 16:37:50 +02:00
pionere ee53f232aa Revert: prevent crash when get_selected is called on a non-existent field 2019-09-07 16:32:17 +02:00
Tom Rothamel 7c03642cb5 Fix tyop that broke itch on mac. 2019-09-06 20:11:40 -04:00
Tom Rothamel 4347c89c15 gl2: Implement is_pixel_opaque. 2019-09-06 01:50:55 -04:00
Tom Rothamel 9783a0a733 gl2: Move the fbo into GLDraw.
This lets the same fbo be shared between the texture system and
things like screenshots and is_pixel_opaque.
2019-09-05 02:29:25 -04:00
Tom Rothamel e5252af42b Automatically voice character menu captions.
Fixes #1991.
2019-09-04 03:37:55 -04:00
Tom Rothamel 094009fa3a gl2: Replace the renderbuffer with a texture.
RGBA renderbuffers don't seem to be supported well on WebGL or
OpenGL ES, at least for the low-end platforms.
2019-09-03 00:17:10 -04:00
Tom Rothamel 6471986ea9 oldgl: Bind a texture to the framebuffer, not a renderbuffer.
Creating RGBA or RGBA8 renderbuffers doesn't seem to be supported
very well (at leeast in webgl), while texture attachments seem to work.
2019-09-02 22:28:29 -04:00
Tom Rothamel ab97732865 oldgl: Use renderbuffer copying to render textures.
This is a backport of a performance improvement from the gl2
branch, where profiling reported that rebinding the framebuffer
was very slow, and simply copying the pixels out is much
faster.
2019-09-01 22:04:49 -04:00
Tom Rothamel 4f90718eaa oldgl: Expose renderbuffer functions. 2019-09-01 22:00:57 -04:00
Tom Rothamel 2aa796d418 glold: Remove support for OpenGL ES 1
This wasn't used for anything, and it's obsolete, so out it goes.
2019-09-01 21:51:46 -04:00
Moshibit e62ee21922 translation correction 2019-08-31 22:47:17 -05:00
Moshibit 218cb677cd Delete indepth_minigame.rpyc 2019-08-31 22:46:44 -05:00
Moshibit fe0e129ad3 translation correction 2019-08-31 22:45:40 -05:00
Moshibit 630ed80eb5 translation correction 2019-08-31 22:33:55 -05:00
Moshibit 2b5d40053d translation correction 2019-08-31 21:48:12 -05:00
Moshibit 8728fd137a translation correction 2019-08-31 21:44:18 -05:00
Moshibit 7b34ec1ba3 translation correction 2019-08-31 21:04:06 -05:00
Moshibit 3f785ee366 Delete indepth_transitions.rpyc 2019-08-31 21:03:36 -05:00
Moshibit 1f9401a602 translation correction 2019-08-31 21:02:32 -05:00
Moshibit 91a0677f67 translation correction 2019-08-31 19:45:36 -05:00
Tom Rothamel 1392fb9239 gl2: Fix order of matrix operations. 2019-08-31 20:42:19 -04:00
Moshibit ac902ee733 translation correction 2019-08-31 19:06:40 -05:00
Tom Rothamel 6031917511 Fix matrix order. 2019-08-31 17:39:41 -04:00
Tom Rothamel 0e6ad371fe Merge pull request #1938 from Andykl/doc-afm_enabled
Doc afm enabled
2019-08-30 21:28:27 -04:00
Tom Rothamel ead5c63eac Merge pull request #1939 from psunbury/patch-1
Added that the default setting are in options.rpy
2019-08-30 21:27:55 -04:00
Tom Rothamel 18827a8ab3 Merge pull request #1952 from Andykl/atl-fix
Fix missed continue
2019-08-30 21:27:31 -04:00
Tom Rothamel 8c81811177 Merge pull request #1955 from jsfehler/pref_typo
Fix typo in common/00preferences.rpy
2019-08-30 21:27:16 -04:00
Tom Rothamel ce634662bd Merge pull request #1969 from renpy/Beuc-doc-python
doc: python: clarify other stores usage
2019-08-30 21:26:59 -04:00
Tom Rothamel 13c32894cf Document renpy.random.shuffle.
Per #1989.
2019-08-29 21:51:13 -04:00
Tom Rothamel 26e0f1617e gl2: Use the reverse matrix to multiple rendered textures. 2019-08-29 21:49:33 -04:00
Tom Rothamel 6754d50c12 Merge pull request #1984 from tdh8316/Korean-translation
Improve Korean translation
2019-08-26 21:39:46 -04:00
Donghyeok Tak ebe8264b1d Cleanup translations 2019-08-25 22:01:52 +09:00
Donghyeok Tak 5c9a1066e3 Improve Korean translation 2019-08-25 21:47:33 +09:00
Tom Rothamel 07a6f3dcbd Compate for audio directory stuff. 2019-08-25 08:27:18 -04:00
Tom Rothamel 3441e0e7f0 Remove pluralization.
Fixes #1983.
2019-08-25 08:14:34 -04:00
Moshibit 3bd9055c5f Add files via upload 2019-08-24 11:41:22 -05:00
Moshibit 2941031b2a Merge pull request #5 from renpy/master
update from renpy/master
2019-08-24 10:26:43 -05:00
Tom Rothamel 627b18a8e8 Merge pull request #1970 from renpy/Beuc-doc-emscripten
doc: reference renpy.emscripten + renpyweb enabling renpy.mobile
2019-08-23 06:45:10 -04:00
Tom Rothamel 681d3a5772 Merge pull request #1972 from Andykl/adjustment-force_step
Adjustment force_step
2019-08-22 05:18:13 -05:00
Tom Rothamel 39be517a0a gl2: Move shaders into the common section.
And introduce a function to register the shaders.
2019-08-20 23:16:28 -04:00
Tom Rothamel 8916575921 Automatically define audio files in the audio namespace. 2019-08-19 00:37:04 -05:00
Tom Rothamel 5288ff876d Use _audio_eval on voice filenames. 2019-08-18 18:36:50 -05:00
Tom Rothamel 067d98cdf0 Improve changes to SelectedIf and SensitiveIf.
This builds on the changes in #1977, so that all Action methods
can be proxied.
2019-08-17 08:58:17 -05:00
Tom Rothamel 65957b3236 Merge pull request #1977 from Andykl/improve-if-actions
Improve SelectedIf and SensitiveIf
2019-08-17 08:37:39 -05:00
Moshibit 726e535f1b corrections 2019-08-16 16:43:37 -05:00
Andy_kl 095a253bf8 Fix doc 2019-08-16 22:48:45 +03:00
Andy_kl d3c36b843e Improve SelectedIf and SensitiveIf 2019-08-16 22:42:17 +03:00
Tom Rothamel b5707d7091 Merge pull request #1953 from Beuc/variants-web
web: set variants according to what the browser runs on
2019-08-16 12:45:22 -05:00
Tom Rothamel 024b0aef81 Merge pull request #1961 from Beuc/fullscreen-web
web: detect when fullscreen status changed externally
2019-08-16 12:44:39 -05:00
Tom Rothamel 365913c267 gl2: Use a depth renderbuffer when rendering-to-texture with depth. 2019-08-15 08:10:13 -04:00
Tom Rothamel a2b8842206 gl2: Optimized path for triangle meshes. 2019-08-14 22:57:56 -04:00
Tom Rothamel dc51db06b1 gl2: If a mesh consists of only one kind of polygon, indicate that. 2019-08-14 22:56:38 -04:00
Tom Rothamel c590c4f956 gl2: Do not give OpenGL precision qualifiers. 2019-08-14 22:53:44 -04:00
Tom Rothamel 3871803088 gl2: Properly name config.depth_size. 2019-08-14 02:44:46 -04:00
Tom Rothamel 40ec6f87ef gl2: Depth buffer and depth testing. 2019-08-14 01:45:29 -04:00
Tom Rothamel 6339c0dfef gl2: Fix reload. 2019-08-10 16:38:24 -04:00
Tom Rothamel 510b7cb909 gl2: Support gles directly, rather than in the preprocessor. 2019-08-10 16:35:53 -04:00
Tom Rothamel 3a011ca2c6 gl2: Allow games to use config.gl2 to opt in. 2019-08-09 21:06:47 -04:00
Andy_kl d12fe12c2b Restore type after rounding 2019-08-09 22:04:23 +03:00
Tom Rothamel 37a128a470 gl2: Remove gl2ftl.pyx, now that a better version is in gl2texture. 2019-08-08 22:12:17 -04:00
Tom Rothamel 9e8b074a16 gl2: Disable gl2debug for now.
I haven't been using it, and it's causing build errors.
2019-08-07 18:31:55 -04:00
Tom Rothamel 3147e71c45 Fix windows build. 2019-08-07 11:22:24 -04:00
Tom Rothamel 60d413f96f gl2: Implement ImageDissolve. 2019-08-05 22:47:59 -04:00
Tom Rothamel 5029e6d078 gl2: Dissolve shader. 2019-08-05 21:59:14 -04:00
Tom Rothamel ef167433f4 gl2: Properly set up textures. 2019-08-05 21:54:00 -04:00
Andy_kl aac1f4c421 Compat this 2019-08-06 04:23:16 +03:00
Andy_kl 0ab0856e37 Doc for this 2019-08-06 04:17:31 +03:00
Andy_kl 760503b124 Added force_step keyword to all Action which uses step. 2019-08-06 04:08:26 +03:00
Andy_kl d16e3eea75 Added round_value process to Viewport 2019-08-06 03:48:44 +03:00
Andy_kl 09fda5505e Added round_value process to Bar 2019-08-06 03:47:03 +03:00
Andy_kl 5d4bb738ef Added force_step keyword and round value to Adjustment 2019-08-06 03:37:33 +03:00
Sylvain Beucler 204a48f577 web: more variants tuning 2019-08-05 14:03:51 +02:00
Beuc 8889ab487d doc: reference renpy.emscripten + renpyweb enabling renpy.mobile 2019-08-05 13:16:24 +02:00
Beuc 06684d692a doc: python: clarify other stores usage
Additional stores are available as local variables in the default 'store' under their name already, consequently 'import store.mystore as mystore' is a no-op
2019-08-05 13:08:57 +02:00
Sylvain Beucler 5f4e6d4ba1 web: set variants according to what the browser runs on 2019-08-05 12:51:52 +02:00
Tom Rothamel 200af0d584 gl2: Add rendering to a Texture. 2019-08-05 03:04:21 -04:00
Tom Rothamel e78828fa3b gl2: Add matrix function generation to Ren'Py. 2019-08-05 03:01:02 -04:00
Tom Rothamel 7e602c256e gl2: Add renpy.display.render.models.
A variable to determine if we are supporting a renderer that
support models (gl2) or quads (sw/gl).
2019-08-04 13:15:48 -04:00
Tom Rothamel 1a8154c78a gl2: Add fields for gl2 to renpy.render. 2019-08-04 12:32:11 -04:00
Tom Rothamel 2d7460bafd gl2: Fix a gc leak through Texture. 2019-08-04 12:21:40 -04:00
Tom Rothamel ae998912f5 gl2: Make renpy.geometry the 'default shader', so it doesn't have to be passed.
While we could have just added it to the tuple of shaders created in
GL2DrawingContext, that would lead to creating a tuple on every
render. So instead, it's added in the shader compiler.
2019-08-04 02:27:49 -04:00
Tom Rothamel e8cd034b7a gl2: Separate Texture and non-texture Models.
Which should make things simpler when determining if a RTT is
required, going forwards.
2019-08-03 16:46:21 -04:00
Andy_kl 900e291cdc Added is_init_phase in exports. 2019-08-03 15:19:18 +03:00
Tom Rothamel cb98d18aa9 gl2: Rename TexturedMesh -> Model. 2019-08-03 02:42:24 -04:00
Tom Rothamel ef4c5aff9e gl2: Do not copy a Polygon, Mesh, or TexturedMesh that didn't change. 2019-08-02 23:00:55 -04:00
Tom Rothamel 6cdcb5dfbc Create a flatten operation type.
This makes RTT entirely the renderer's concern.
2019-08-02 19:39:26 -04:00
Tom Rothamel 797ef0fc25 fix: Duplicate the children of DynamicDisplayable.
Fixes #1966, which causesd a Transform that was added to the
tree of surfaces twice, then rendered in a way that wasn't
cached, to cause a crash.
2019-08-01 22:01:35 -04:00
Tom Rothamel c132df21ca gl2: Dynamically select shader and implement alpha blending. 2019-08-01 02:16:07 -04:00
Tom Rothamel 6ab2df60f4 gl2: Implement texture preloading. 2019-07-31 22:47:05 -04:00
Tom Rothamel 101cb8c348 gl2: Support zero copy texture loading.
At least for the common cases, like loading text into the GPU.
2019-07-31 21:17:54 -04:00
Sylvain Beucler 2f53ddc03e gles: handle GPUs that do not support highp 2019-07-31 15:47:42 +02:00
Tom Rothamel 8a042fe187 gl2: Texture improvements.
* Ask GL for the maximum size.
* Remove the idea of generations - we swap out the loader when a
  generation is obsolete.
* Cache the fbo so changing it is needed less often.
2019-07-31 01:27:02 -04:00
Tom Rothamel f9a61f7567 gl2: Read the maximum texture size from the video card. 2019-07-30 01:11:14 -04:00
Tom Rothamel 79c965ba5d gl2: Deal with precision issues in the barycentric interpolator.
It's the same issue as before - arriving at the same point by
different paths might lead to being outside by a very small
amount, which we don't want.
2019-07-30 00:41:41 -04:00
Tom Rothamel 340db775c5 gl2: Fix method name typo. 2019-07-30 00:34:58 -04:00
Tom Rothamel d1224c0707 gl2: Load large surfaces as tiles. 2019-07-29 08:17:18 -04:00
Tom Rothamel afeb06c1d9 gl2: Change how rectangles work. 2019-07-28 23:32:44 -04:00
Tom Rothamel afad82b082 gl2: The Great Geometry Rewrite (tm)
This improves gl2 Polygons and Meshes by making it clear if a
new object is created or they're updated in place, by supporting
multiplication by matrices directly, and by including the repr
function everywhere.

Really, a quality of life improvement.
2019-07-28 18:33:27 -04:00
Tom Rothamel 2f683b2d69 gl2: Increase the numerical stability of gl2 by dealing with parallel lines. 2019-07-28 00:02:28 -04:00
Beuc d8ec4503a6 doc: fix formatting in sprites.html 2019-07-27 22:51:51 +02:00
Moshibit bb207a7fce Spanish translation correction
short and long must be in English
2019-07-27 11:44:52 -05:00
Sylvain Beucler 629ea6f13f web: detect when fullscreen status changed externally 2019-07-27 15:16:12 +02:00
Tom Rothamel 2a78e0141f gl2: Avoid leaking textures by changing generation too early. 2019-07-27 03:48:03 -04:00
Tom Rothamel 8da9eafd28 gl2: Remove debug print. 2019-07-27 03:28:50 -04:00
Tom Rothamel 1f8e1cacf6 gl2: Use a renderbuffer to load textures.
After extensive profiling my GTX 1060, it appears that using
glFrameBufferTexture2D is unacceptably slow. (300+us for just that
one call). glCopyTexImage seem to be much faster, with realistic
small texture loads (like button images) taking 60us or so.
2019-07-27 03:22:55 -04:00
Tom Rothamel 048f8fea8f gl2: Remove debugging from uguu.
Using KHR_debug gives us the same thing, faster.
2019-07-26 23:47:16 -04:00
Tom Rothamel 229bd5a82d gl2: Enable all debugging. 2019-07-26 23:23:19 -04:00
Tom Rothamel 12405a22d9 gl2: Support using KHR_debug to get error output. 2019-07-26 23:11:25 -04:00
Tom Rothamel 3529d40ba0 Add GetCharacterVolume. 2019-07-25 18:42:54 -04:00
Tom Rothamel 02be00bb78 Add compatibility for tile_ratio to Frame. 2019-07-25 12:36:57 -04:00
Tom Rothamel 189e7d8e4f Merge pull request #1956 from RenpyRemix/master
Add support for tile="integer" to frame. 

This is a version of tiling that always does an integer number of repeats, scaling as necessary to accomplish this.
2019-07-25 12:22:39 -04:00
Tom Rothamel 0956519365 gl2: Fix issues preventing a restart.
* Matrix can now be unpickled.
* deinit -> quit.
2019-07-24 01:32:07 -04:00
Tom Rothamel efbd897832 gl2: Avoid mipmapping, for now.
It changes the look of certainy parts of drawing, and so it needs
to be disabled for now.
2019-07-23 23:56:11 -04:00
Tom Rothamel c6d9b6e0c1 gl2: Fix uninitialized variable. 2019-07-23 23:42:42 -04:00
Tom Rothamel d0bbc81ebf gl2: Return the subsurface when taking a texture subsurface. 2019-07-23 22:52:24 -04:00
Tom Rothamel 662e8227ab gl2: The color used for solid textures should be alpha-premultiplied. 2019-07-23 22:39:47 -04:00
Tom Rothamel 33e3174692 Default to not code-signing for macOS.
Per #1958.
2019-07-23 18:05:40 -04:00
Moshibit dde68dd735 Merge pull request #4 from renpy/master
update from renpy/master
2019-07-23 15:34:15 -05:00
Tom Rothamel 5accb1c3e4 Add repr to texture. 2019-07-23 02:41:34 -04:00
Tom Rothamel 77d8b9b318 gl2: Properly reset the clipping rectangle. 2019-07-23 02:40:32 -04:00
Tom Rothamel 202fc47419 gl2: glEnableVertexAttribArray -> glDisableVertexAttribArray 2019-07-22 21:29:43 -04:00
RenpyRemix 49c0ec0504 Update imagelike.py
Support for tile="integer" in Frame
2019-07-22 23:51:26 +01:00
Tom Rothamel bc437b5e9b gl2: Implement clipping using polygons in the screen space. 2019-07-22 01:15:01 -04:00
Tom Rothamel b4dfcc44cf gl2: Determine if polygons are wound clockwise or counterclockwise.
This might actually change over the lifecycle of a polygon, as the
winding of the polygon changes when it goes from the pygame/Ren'Py
coordinate space to the OpenGL viewport space.

This change means it doesn't matter - polygon intersection can
always occur.
2019-07-22 01:00:00 -04:00
Tom Rothamel 1a98502bdc gl2: Set the number of actual points when restriding a polygon. 2019-07-22 00:52:54 -04:00
Joshua Fehler 977726c3ab Update 00preferences.rpy
Fix typo in common/00preferences.rpy
2019-07-21 23:48:51 -04:00
Tom Rothamel ed9cbfd21c gl2: Support cropping to a viewport-space polygon. 2019-07-21 15:07:46 -04:00
Tom Rothamel 261a0b807d gl2: Redo shaders to minimize allocation. 2019-07-21 01:14:09 -04:00
Tom Rothamel 261718967a gl2: Share the mesh used for ftl. 2019-07-20 23:13:49 -04:00
Moshibit 3e5e77b400 fix code
keyword "menu" cause an exception. Maybe you wanted to put "label" as it usually is after menu
2019-07-20 20:24:36 -05:00
Beuc 7fa4265f15 Remove mistaken file from Moshibit 2019-07-20 23:19:07 +02:00
Andy_kl 995823fbcf Fix missed continue 2019-07-20 23:00:47 +03:00
Tom Rothamel 663961581f Merge pull request #1949 from Andykl/nondevelop-dynamic
Do not raise error if dynamic variable already deleted in pop time.
2019-07-20 01:45:01 -04:00
Tom Rothamel d32946ce53 gl2: Texture loading (and eventually RTT) to its own pass. 2019-07-19 21:15:26 -04:00
Andy_kl 6bd9523780 Do not raise error if dynamic variable already deleted in pop time. 2019-07-20 02:36:48 +03:00
Tom Rothamel b0b49b353e gl2: Fix draw glitches.
These were caused by state that was left behind by the texture
loader.
2019-07-18 23:07:08 -04:00
Tom Rothamel b1b943b456 gl2: Render the Ren'Py surface tree.
This means that we're displaying a partially-correct screen,
rather than just test images.
2019-07-18 02:37:54 -04:00
Tom Rothamel dd5326c205 gl2: Load and display textures. 2019-07-17 22:40:21 -04:00
Tom Rothamel e62cc921c7 gl2: Able to load a surface as a TexturedMesh. 2019-07-16 23:14:41 -04:00
Tom Rothamel d343617616 gl2: Process texure loading through a TextureLoader object. 2019-07-16 02:11:15 -04:00
Tom Rothamel 0cf72637b2 Rename draw.free_memory to draw.kill_textures, unify with deinit.
The differentiation between .deinit() and .quit() was hard to understand.
This change makes it clear - .kill_textures frees all of the OpenGL textures,
while .quit() releases all GL resources.
2019-07-15 22:47:57 -04:00
Tom Rothamel e9312a3604 gl2: Display a solid. 2019-07-14 14:33:52 -04:00
psunbury 39420b940a Added that the default setting are in options.rpy
I spent a lot of time finding where this was set. It would be useful putting what file it is located.
2019-07-13 19:46:56 -07:00
Andy_kl d8655be2c5 Correct renpy.get_skipping returns 2019-07-14 05:18:09 +03:00
Andy_kl c276ac8ec8 Fix spurious skipping value 2019-07-14 05:16:17 +03:00
Andy_kl 37e7060baa Documented preferences.afm_enable 2019-07-14 05:15:44 +03:00
Tom Rothamel b155fa9411 gl2: Minor fixes, now can display a blank screen. 2019-07-13 12:02:58 -04:00
Tom Rothamel 4ef1e05dc8 gl2: Work on texture meshes. 2019-07-13 02:35:17 -04:00
Tom Rothamel da6e5e2c73 gl2: Add shader cache. 2019-07-12 23:29:48 -04:00
Moshibit 95c1cd5ad8 correct radio button 2019-07-12 21:50:40 -05:00
Moshibit f8747f6e38 Delete screens.rpy 2019-07-12 21:47:43 -05:00
Moshibit 450aeb55dc correct radio button 2019-07-12 21:41:14 -05:00
Moshibit 878e2284f8 fix typo 2019-07-12 21:35:09 -05:00
Moshibit a4291ccddb Merge pull request #3 from renpy/master
update from renpy/renpy master
2019-07-12 21:27:04 -05:00
Tom Rothamel 2091161241 Merge pull request #1933 from Queex/patch-1
Missing config prefix for gc_print_unreachable
2019-07-12 09:38:14 -04:00
Ben Wright c8ac8b511f Missing config prefix for gc_print_unreachable 2019-07-12 13:06:09 +01:00
Tom Rothamel ed3182c74c gl2: TexturedMesh fields. 2019-07-12 03:18:04 -04:00
Tom Rothamel 82b0d59f19 gl2: Cut out the old texture code. 2019-07-11 22:42:27 -04:00
Tom Rothamel 60b42fd79b Merge pull request #1929 from Andykl/sl2-fixes
Sl2 fixes
2019-07-11 00:56:24 -04:00
Tom Rothamel ba2c72eaac Allow config.load_failed_label to take a function.
Fixes #1928.
2019-07-10 21:31:46 -04:00
Andy_kl 7751d5603e Fix indention in doc 2019-07-11 02:35:49 +03:00
Andy_kl 6e8d622282 Moved minimum property to position properties 2019-07-11 02:19:51 +03:00
Andy_kl 30b3366168 Documented default_properties 2019-07-11 02:18:37 +03:00
Tom Rothamel 736d9487c5 gl2: Combine the geometry stuff into one file. 2019-07-09 23:15:58 -04:00
Tom Rothamel 7bf7af52b1 gl2: float -> double, so it works with emscripten. 2019-07-08 22:33:46 -04:00
Tom Rothamel be7daf39d5 gl2: Clean as much of we can of gl1 out of gl2draw.pyx. 2019-07-08 02:28:27 -04:00
Tom Rothamel 0faf7810f6 Include gl2 and matrix in the built distributions. 2019-07-07 20:16:53 -04:00
Tom Rothamel bd333803b2 gl2: Log shader cache errors. 2019-07-06 15:04:25 -04:00
Tom Rothamel f24e3f199e gl2: Add modules from glexperiment into Ren'Py. 2019-07-06 03:07:38 -04:00
Tom Rothamel 13a755ae93 IF a pure-python module can't be parsed as utf-8, try it as latin-1. 2019-07-05 22:05:46 -04:00
Tom Rothamel a907cfaa2e Remove gl2 blacklist.
It was not used.
2019-07-05 20:33:34 -04:00
Tom Rothamel b236453972 Convert from using a 2x2 matrix to a 4x4 matrix.
This will eventually let us represent z-rotations
and translations using matrices.
2019-07-05 13:10:47 -04:00
Tom Rothamel 95fe225a8c Merge pull request #1921 from Beuc/sigttin
rapt: prevent gradle from stopping Ren'Py when run in the background
2019-07-04 10:19:39 -04:00
Sylvain Beucler 621fbe9449 rapt: prevent gradle from stopping Ren'Py when run in the background 2019-07-04 15:57:02 +02:00
Tom Rothamel f6029db185 Avoid a with None before a say attribute transition with a layer.
As this would make the transition moot.
2019-07-03 22:23:09 -04:00
Tom Rothamel 38f8e1f16f Document config.keyword_after_python as an incompatible change. 2019-07-02 23:06:01 -04:00
Tom Rothamel ca95350b70 Bump version. 2019-07-02 19:42:21 -04:00
Sylvain Beucler 985b54d284 web: avoid encoding issues in non-UTF-8 environments
e.g. windows korean, cf. renpyweb issue 1
2019-07-02 22:39:30 +02:00
pionere 7d655595e0 prevent crash when get_selected is called on a non-existent field
A field (especially a global Variable) might not exists at the beginning of the interaction. This causes the application to crash e.g. if the variable is set on the 'action' of a button.
The solution is that the get_selected returns False if the given field does not exists at the moment.
2019-06-22 14:35:41 +02:00
Andy_kl 669a25d530 Receive a layer from default_layer in all exports funcs 2019-06-18 22:51:54 +03:00
128 changed files with 5405 additions and 6517 deletions
+2
View File
@@ -82,3 +82,5 @@ sphinx/source/inc
sphinx/source/thequestion.rst
tutorial/game/tutorial_director.rpy
launcher/theme
module/emscripten-static/
web/
+1 -1
View File
@@ -124,7 +124,7 @@ not edit the files in sphinx/source/inc directly, as they will be overwritten.
Docstrings may include tags on the first few lines:
\:doc: `section` `kind`
Indicates that this functions should be documented. `section` gives
Indicates that this function should be documented. `section` gives
the name of the include file the function will be documented in, while
`kind` indicates the kind of object to be documented (one of ``function``,
``method`` or ``class``. If omitted, `kind` will be auto-detected.
+24 -2
View File
@@ -54,6 +54,7 @@ def main():
ap.add_argument("--variant", action="store")
ap.add_argument("--sign", action="store_true", default=True)
ap.add_argument("--nosign", action="store_false", dest="sign")
ap.add_argument("--notarized", action="store_true", dest="notarized")
ap.add_argument("--vc-version-only", action="store_true")
args = ap.parse_args()
@@ -175,6 +176,12 @@ def main():
destination,
]
if args.notarized:
cmd.extend([
"--macapp",
"notarized/out",
])
print()
subprocess.check_call(cmd)
@@ -250,8 +257,23 @@ def main():
print()
if not (args.fast or args.sign):
print("For a final-ish release, remember to use --sign so we're signed on the mac.")
if args.sign and not args.notarized:
shutil.copy(sdk + ".tar.bz2", ROOT + "/notarized/in/renpy.tar.bz2")
print("Next steps:")
print()
print(" mac: ./scripts/notarize_app_1.sh")
print(" mac: ./scripts/notarize_app_2.sh")
print(" linux: ./distribute.py --notarized")
print()
elif args.sign and args.notarized:
print("Next steps:")
print()
print(" mac: ./scripts/notarize_dmg_1.sh", args.version)
print(" mac: ./scripts/notarize_dmg_2.sh", args.version)
print()
if __name__ == "__main__":
-6
View File
@@ -199,12 +199,6 @@ init python:
build.documentation('*.html')
build.documentation('*.txt')
## Set this to a string containing your Apple Developer ID Application
## to enable codesigning on the Mac. Be sure to change it to your own
## Apple-issued ID.
# define build.mac_identity = "Developer ID Application: Guy Shy (XHTE5H7Z42)"
## A Google Play license key is required to download expansion files and
## perform in-app purchases. It can be found on the "Services & APIs" page
+5 -3
View File
@@ -323,12 +323,14 @@ screen navigation():
textbutton _("About") action ShowMenu("about")
if renpy.variant("pc"):
if renpy.variant("pc") or (renpy.variant("web") and not renpy.variant("mobile")):
## Help isn't necessary or relevant to mobile devices.
textbutton _("Help") action ShowMenu("help")
## The quit button is banned on iOS and unnecessary on Android.
if renpy.variant("pc"):
## The quit button is banned on iOS and unnecessary on Android and Web.
textbutton _("Quit") action Quit(confirm=not main_menu)
@@ -725,7 +727,7 @@ screen preferences():
hbox:
box_wrap True
if renpy.variant("pc"):
if renpy.variant("pc") or renpy.variant("web"):
vbox:
style_prefix "radio"
+26 -12
View File
@@ -373,7 +373,7 @@ init python in distribute:
This manages the process of building distributions.
"""
def __init__(self, project, destination=None, reporter=None, packages=None, build_update=True, open_directory=False, noarchive=False, packagedest=None, report_success=True, scan=True):
def __init__(self, project, destination=None, reporter=None, packages=None, build_update=True, open_directory=False, noarchive=False, packagedest=None, report_success=True, scan=True, macapp=None):
"""
Distributes `project`.
@@ -404,6 +404,10 @@ init python in distribute:
`report_success`
If true, we report that the build succeeded.
`macapp`
If given, the path to a macapp that's used instead of
the macapp
"""
# A map from a package to a unique update version hash.
@@ -437,6 +441,9 @@ init python in distribute:
# Logfile.
self.log = open(self.temp_filename("distribute.txt"), "w")
# The path to the mac app.
self.macapp = macapp
# Start by scanning the project, to get the data and build
# dictionaries.
data = project.data
@@ -1059,6 +1066,9 @@ init python in distribute:
Signs the mac app contained in appzip.
"""
if self.macapp:
return self.rescan(fl, self.macapp)
identity = self.build.get('mac_identity', None)
if identity is None:
@@ -1094,6 +1104,7 @@ init python in distribute:
self.build["mac_codesign_command"],
identity=identity,
app=os.path.join(dn, self.app),
entitlements=os.path.join(config.gamedir, "entitlements.plist"),
)
# Rescan the signed app.
@@ -1109,7 +1120,7 @@ init python in distribute:
identity = self.build.get('mac_identity', None)
if identity is None:
raise Exception("Creating an unsigned DMG is not supported. Please set build.mac_identity.")
identity = ''
self.run(
_("Creating the Macintosh DMG..."),
@@ -1120,14 +1131,16 @@ init python in distribute:
dmg=dmg,
)
self.run(
_("Signing the Macintosh DMG..."),
self.build["mac_codesign_dmg_command"],
identity=identity,
volname=volname,
sourcedir=sourcedir,
dmg=dmg,
)
if self.build.get("mac_codesign_dmg_command", None):
self.run(
_("Signing the Macintosh DMG..."),
self.build["mac_codesign_dmg_command"],
identity=identity,
volname=volname,
sourcedir=sourcedir,
dmg=dmg,
)
def prepare_file_list(self, format, file_lists):
"""
@@ -1265,7 +1278,7 @@ init python in distribute:
file_hash, old_fl_hash = self.build_cache.get(full_filename, ("", ""))
if (not directory) and old_fl_hash == fl_hash:
if (not directory) and (old_fl_hash == fl_hash) and not(self.build['renpy'] and (variant == "sdk")):
if file_hash:
self.build_cache[full_filename] = (file_hash, fl_hash)
@@ -1476,6 +1489,7 @@ init python in distribute:
ap.add_argument("--no-update", default=True, action="store_false", dest="build_update", help="Prevents updates from being built.")
ap.add_argument("--package", action="append", help="If given, a package to build. Defaults to building all packages.")
ap.add_argument("--no-archive", action="store_true", help="If given, files will not be added to archives.")
ap.add_argument("--macapp", default=None, action="store", help="If given, the path to a signed and notarized mac app.")
ap.add_argument("project", help="The path to the project directory.")
args = ap.parse_args()
@@ -1487,7 +1501,7 @@ init python in distribute:
else:
packages = None
Distributor(p, destination=args.destination, reporter=TextReporter(), packages=packages, build_update=args.build_update, noarchive=args.no_archive, packagedest=args.packagedest)
Distributor(p, destination=args.destination, reporter=TextReporter(), packages=packages, build_update=args.build_update, noarchive=args.no_archive, packagedest=args.packagedest, macapp=args.macapp)
return False
+1 -1
View File
@@ -27,7 +27,7 @@ init python:
platform = "windows-amd64"
exe = "butler.exe"
elif renpy.macintosh:
plaform = "darwin-amd64"
platform = "darwin-amd64"
exe = "butler"
else:
platform = "linux-amd64"
+3 -1
View File
@@ -194,7 +194,9 @@ init -1 python:
kwargs["stdin"] = subprocess.PIPE
try:
self.process = subprocess.Popen(cmd, cwd=renpy.fsencode(RAPT_PATH), stdout=f, stderr=f, startupinfo=startupinfo, **kwargs)
self.process = subprocess.Popen(cmd, cwd=renpy.fsencode(RAPT_PATH), stdout=f, stderr=f, stdin=subprocess.PIPE, startupinfo=startupinfo, **kwargs)
# avoid SIGTTIN caused by e.g. gradle doing empty read on terminal stdin
self.process.stdin.close()
except:
import traceback
traceback.print_exc(file=f)
+3 -3
View File
@@ -102,7 +102,7 @@
# _developer/inspector.rpym:145
old " (no properties affect the displayable)"
new "(디스플레이어블에 적용되는 속성이 없습니다)"
new " (디스플레이어블에 적용되는 속성이 없습니다)"
# _developer/inspector.rpym:147
old " (default properties omitted)"
@@ -134,11 +134,11 @@
# 00console.rpy:413
old " <renpy script statement>: run the statement\n"
new "<렌파이 스크립트 명령문>: 명령문을 실행한다\n"
new " <렌파이 스크립트 명령문>: 명령문을 실행한다\n"
# 00console.rpy:415
old " <python expression or statement>: run the expression or statement"
new "<파이썬 표현식 또는 명령문>: 표현식이나 명령문을 실행한다"
new " <파이썬 표현식 또는 명령문>: 표현식이나 명령문을 실행한다"
# 00console.rpy:423
old "clear: clear the console history"
+5 -5
View File
@@ -18,11 +18,11 @@
# add_file.rpy:31
old "The filename must have the .rpy extension."
new "파일 이름에는 반드시 .rpy 확장자가 적혀있어야 합니다."
new "파일의 확장자는 반드시 .rpy 어야 합니다."
# add_file.rpy:39
old "The file already exists."
new "파일 이름이 이미 존재합니다."
new "파일이 이미 존재합니다."
# add_file.rpy:42
old "# Ren'Py automatically loads all script files ending with .rpy. To use this\n# file, define a label and jump to it from another file.\n"
@@ -214,7 +214,7 @@
# androidstrings.rpy:17
old "The build seems to have failed."
new "빌드 실패했습니다."
new "빌드 실패했습니다."
# androidstrings.rpy:18
old "Launching app."
@@ -594,7 +594,7 @@
# dmgcheck.rpy:50
old "Ren'Py is running from a read only folder. Some functionality will not work."
new "렌파이가 읽기 전용 폴더로부터 실행되고 있습니다. 일부 기능이 작동하지 않을 수 있습니다."
new "렌파이가 읽기 전용 폴더에서 실행되고 있습니다. 일부 기능이 작동하지 않을 수 있습니다."
# dmgcheck.rpy:50
old "This is probably because Ren'Py is running directly from a Macintosh drive image. To fix this, quit this launcher, copy the entire %s folder somewhere else on your computer, and run Ren'Py again."
@@ -1554,7 +1554,7 @@
# web.rpy:194
old "Before packaging web apps, you'll need to download RenPyWeb, Ren'Py's web support. Would you like to download RenPyWeb now?"
new "웹 앱을 패키징하려면 렌파이 렌파이웹(RenpyWeb)이 필요니다. 지금 내려받으시겠습니까?"
new "웹 앱을 패키징하려면 렌파이의 웹 지원인 렌파이웹(RenpyWeb)이 필요니다. 지금 내려받으시겠습니까?"
# updater.rpy:188
old "Fetching the list of update channels"
+2 -2
View File
@@ -190,9 +190,9 @@
# 00console.rpy:680
old "short: Shorten the representation of objects on the console (default)."
new "corto: Acorta la representación de los objetos en la consola (predeterminado)."
new "short: Acorta la representación de los objetos en la consola (predeterminado)."
# 00console.rpy:685
old "long: Print the full representation of objects on the console."
new "largo: imprime la representación completa de los objetos en la consola."
new "long: imprime la representación completa de los objetos en la consola."
+4 -3
View File
@@ -27,6 +27,7 @@ init python:
import shutil
import webserver
import io
WEB_PATH = None
@@ -91,12 +92,12 @@ init python:
shutil.copy(os.path.join(WEB_PATH, fn), os.path.join(destination, fn))
# Copy over index.html.
with open(os.path.join(WEB_PATH, "index.html")) as f:
with io.open(os.path.join(WEB_PATH, "index.html"), encoding='utf-8') as f:
html = f.read()
html = html.replace("%%TITLE%%", display_name.encode("utf-8"))
html = html.replace("%%TITLE%%", display_name)
with open(os.path.join(destination, "index.html"), "w") as f:
with io.open(os.path.join(destination, "index.html"), "w", encoding='utf-8') as f:
f.write(html)
webserver.start(destination)
+17 -6
View File
@@ -20,7 +20,7 @@
static int audio_equal_mono = 1;
/* The weight of stereo channels when audio_equal_mono is true. */
static float stereo_matrix[] = { 1.0, 1.0 };
static double stereo_matrix[] = { 1.0, 1.0 };
/* The output audio sample rate. */
static int audio_sample_rate = 44100;
@@ -571,19 +571,30 @@ static void decode_audio(MediaState *ms) {
break;
}
converted_frame = av_frame_alloc();
converted_frame->sample_rate = audio_sample_rate;
converted_frame->channel_layout = AV_CH_LAYOUT_STEREO;
converted_frame->format = AV_SAMPLE_FMT_S16;
if (!ms->audio_decode_frame->channel_layout) {
ms->audio_decode_frame->channel_layout = av_get_default_channel_layout(ms->audio_decode_frame->channels);
if (audio_equal_mono && (ms->audio_decode_frame->channels == 1)) {
swr_alloc_set_opts(
ms->swr,
converted_frame->channel_layout,
converted_frame->format,
converted_frame->sample_rate,
ms->audio_decode_frame->channel_layout,
ms->audio_decode_frame->format,
ms->audio_decode_frame->sample_rate,
0,
NULL);
swr_set_matrix(ms->swr, stereo_matrix, 1);
}
}
converted_frame = av_frame_alloc();
converted_frame->sample_rate = audio_sample_rate;
converted_frame->channel_layout = AV_CH_LAYOUT_STEREO;
converted_frame->format = AV_SAMPLE_FMT_S16;
if(swr_convert_frame(ms->swr, converted_frame, ms->audio_decode_frame)) {
av_frame_free(&converted_frame);
continue;
+45
View File
@@ -0,0 +1,45 @@
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_opengl_glext.h>
#include <stdio.h>
#include "gl2debug.h"
static void APIENTRY gl2_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
printf("GL %s: 0x%x, 0x%x: %s\n", (type == GL_DEBUG_TYPE_ERROR ? "ERROR" : " "), type, severity, message);
}
void gl2_enable_debug(void) {
void APIENTRY (*debugMessageCallback)(GLDEBUGPROC callback, const void *userParam);
void APIENTRY (*debugMessageControl)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled);
void APIENTRY (*glEnable)(GLenum);
debugMessageCallback = SDL_GL_GetProcAddress("glDebugMessageCallback");
if (!debugMessageCallback) {
debugMessageCallback = SDL_GL_GetProcAddress("glDebugMessageCallbackKHR");
}
if (debugMessageCallback == NULL) {
printf("GL: Could not get glDebugMessageCallback\n");
return;
}
debugMessageControl = SDL_GL_GetProcAddress("glDebugMessageControl");
if (!debugMessageControl) {
debugMessageControl = SDL_GL_GetProcAddress("glDebugMessageControlKHR");
}
if (debugMessageControl == NULL) {
printf("GL: Could not get glDebugMessageControl\n");
return;
}
glEnable = SDL_GL_GetProcAddress("glEnable");
glEnable(GL_DEBUG_OUTPUT);
debugMessageCallback(gl2_debug_callback, 0);
debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, 1);
}
+
+6
View File
@@ -0,0 +1,6 @@
#ifndef GL2DEBUG_H
#define GL2DEBUG_H
void gl2_enable_debug(void);
#endif
+7 -27
View File
@@ -44,33 +44,6 @@
#endif
#if defined RENPY_GLES_1
#include <GLES/gl.h>
#include <GLES/glext.h>
#define glOrtho glOrthof
#define GL_SOURCE0_ALPHA GL_SRC0_ALPHA
#define GL_SOURCE1_ALPHA GL_SRC1_ALPHA
#define GL_SOURCE2_ALPHA GL_SRC2_ALPHA
#define GL_SOURCE0_RGB GL_SRC0_RGB
#define GL_SOURCE1_RGB GL_SRC1_RGB
#define GL_SOURCE2_RGB GL_SRC2_RGB
#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES
#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES
#define glBindFramebufferEXT glBindFramebufferOES
#define glFramebufferTexture2DEXT glFramebufferTexture2DOES
#define glGenFramebuffersEXT glGenFramebuffersOES
#define glDeleteFramebuffersEXT glDeleteFramebuffersOES
#define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES
#define RENPY_THIRD_TEXTURE 0
#endif
#if defined RENPY_GLES_2
#ifdef ANGLE
@@ -88,6 +61,12 @@ typedef GLchar GLcharARB;
#define GL_MAX_TEXTURE_UNITS GL_MAX_TEXTURE_IMAGE_UNITS
#define GL_RENDERBUFFER_EXT GL_RENDERBUFFER
#define glGenRenderbuffersEXT glGenRenderbuffers
#define glBindRenderbufferEXT glBindRenderbuffer
#define glRenderbufferStorageEXT glRenderbufferStorage
#define glDeleteRenderbuffersEXT glDeleteRenderbuffers
#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER
#define GL_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING
#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0
@@ -96,6 +75,7 @@ typedef GLchar GLcharARB;
#define glGenFramebuffersEXT glGenFramebuffers
#define glDeleteFramebuffersEXT glDeleteFramebuffers
#define glCheckFramebufferStatusEXT glCheckFramebufferStatus
#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer
#define GL_OBJECT_INFO_LOG_LENGTH_ARB GL_INFO_LOG_LENGTH
#define GL_OBJECT_COMPILE_STATUS_ARB GL_COMPILE_STATUS
+3
View File
@@ -2755,6 +2755,9 @@ def find_gl_names(dirname, gl1):
rv = list(names)
rv.sort()
for i in rv:
print(i)
return rv
+3 -3
View File
@@ -181,6 +181,7 @@ for p in generate_styles.prefixes:
cython("renpy.styledata.style_{}functions".format(p), pyx=setuplib.gen + "/style_{}functions.pyx".format(p))
# renpy.display
cython("renpy.display.matrix")
cython("renpy.display.render", libs=[ 'z', 'm' ])
cython("renpy.display.accelerator", libs=sdl + [ 'z', 'm' ])
@@ -218,11 +219,10 @@ cython("renpy.gl.glrtt_fbo", libs=glew_libs)
cython("renpy.gl2.uguugl", libs=sdl)
cython("renpy.gl2.uguu", libs=sdl)
cython("renpy.gl2.gl2geometry")
cython("renpy.gl2.gl2draw", libs=sdl)
cython("renpy.gl2.gl2texture", libs=sdl)
cython("renpy.gl2.gl2environ_shader", libs=sdl)
cython("renpy.gl2.gl2rtt_fbo", libs=sdl)
cython("renpy.gl2.gl2shader", libs=sdl)
if not (android or ios or emscripten):
# renpy.angle
+279
View File
@@ -0,0 +1,279 @@
from __future__ import print_function
from sympy import symbols, Matrix, pi, cos, sin, simplify
matrix_names = [
"xdx",
"xdy",
"xdz",
"xdw",
"ydx",
"ydy",
"ydz",
"ydw",
"zdx",
"zdy",
"zdz",
"zdw",
"wdx",
"wdy",
"wdz",
"wdw",
]
def prefixed_matrix(prefix):
"""
Returns a matrix where each entry is of the for prefix___name.
"""
return Matrix(4, 4, [ symbols(prefix + "___" + i) for i in matrix_names ])
###############################################################################
import cStringIO
generators = [ ]
class Generator(object):
def __init__(self, name, docs):
self.f = cStringIO.StringIO()
self.name = name
self.docs = docs
generators.append(self)
self.first_let = True
def parameters(self, params):
print(file=self.f)
print(file=self.f)
print("def {}({}):".format(
self.name,
", ".join("float " + i for i in params.split())), file=self.f)
if self.docs:
print(' """' + self.docs + '"""', file=self.f)
if params.split():
return symbols(params)
def let(self, name, value):
if self.first_let:
print(file=self.f)
self.first_let = False
value = simplify(value, rational=True)
print(" cdef float {} = {}".format(name, str(value)), file=self.f)
return symbols(name)
def matrix(self, m):
print(file=self.f)
print(" cdef Matrix rv = Matrix(None)", file=self.f)
print(file=self.f)
for name, value in zip(matrix_names, m):
if value == 0.0:
continue
print(" rv.{} =".format(name), simplify(value, rational=True), file=self.f)
print(file=self.f)
print(" return rv", file=self.f)
def generate(func):
g = Generator(func.__name__, func.__doc__)
func(g)
return func
def write(fn):
with open(fn, "w") as f:
for i in generators:
f.write(i.f.getvalue())
@generate
def identity(g):
"""
Returns an identity matrix.
"""
g.parameters("")
g.matrix(Matrix(4, 4, [
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
]))
@generate
def offset(g):
"""
Returns a matrix that offsets the vertex by a fixed amount.
"""
x, y, z = g.parameters("x y z")
g.matrix(Matrix(4, 4, [
1.0, 0.0, 0.0, x,
0.0, 1.0, 0.0, y,
0.0, 0.0, 1.0, z,
0.0, 0.0, 0.0, 1.0,
]))
@generate
def rotate(g):
"""
Returns a matrix that rotates the displayable around the
origin.
`x`, `y`, `x`
The amount to rotate around the origin, in degrees.
"""
x, y, z = g.parameters("x y z")
sinx = g.let("sinx", sin(x * pi / 180.0))
cosx = g.let("cosx", cos(x * pi / 180.0))
siny = g.let("siny", sin(y * pi / 180.0))
cosy = g.let("cosy", cos(y * pi / 180.0))
sinz = g.let("sinz", sin(z * pi / 180.0))
cosz = g.let("cosz", cos(z * pi / 180.0))
rx = Matrix(4, 4, [
1, 0, 0, 0,
0, cosx, -sinx, 0,
0, sinx, cosx, 0,
0, 0, 0, 1 ])
ry = Matrix(4, 4, [
cosy, 0, siny, 0,
0, 1, 0, 0,
-siny, 0, cosy, 0,
0, 0, 0, 1])
rz = Matrix(4, 4, [
cosz, -sinz, 0, 0,
sinz, cosz, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1, ])
g.matrix(rz * ry * rx)
@generate
def perspective(g):
"""
Returns the Ren'Py projection matrix. This is a view into a 3d space
where (0, 0) is the top left corner (`w`/2, `h`/2) is the center, and
(`w`,`h`) is the bottom right, when the z coordinate is 0.
`w`, `h`
The width and height of the input plane, in pixels.
`n`
The distance of the near plane from the camera.
`p`
The distance of the 1:1 plane from the camera. This is where 1 pixel
is one coordinate unit.
`f`
The distance of the far plane from the camera.
"""
w, h, n, p, f = g.parameters('w h n p f')
offset = Matrix(4, 4, [
1.0, 0.0, 0.0, -w / 2.0,
0.0, 1.0, 0.0, -h / 2.0,
0.0, 0.0, 1.0, -p,
0.0, 0.0, 0.0, 1.0,
])
projection = Matrix(4, 4, [
2.0 * p / w, 0.0, 0.0, 0.0,
0.0, 2.0 * p / h, 0.0, 0.0,
0.0, 0.0, -(f+n)/(f-n), -2 * f * n / (f - n),
0.0, 0.0, -1.0, 0.0,
])
reverse_offset = Matrix(4, 4, [
w / 2.0, 0.0, 0.0, w / 2.0,
0.0, h / 2.0, 0.0, h / 2.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
])
g.matrix(reverse_offset * projection * offset)
@generate
def screen_projection(g):
"""
This generates a matrix that projects the Ren'Py space, where (0, 0) is the
top left and (`w`, `h`) is the bottom right, into the OpenGL viewport, where
(-1.0, 1.0) is the top left and (1.0, -1.0) is the bottom.
Generates the matrix that projects the Ren'Py screen to the OpenGL screen.
"""
w, h = g.parameters("w h")
m = Matrix(4, 4, [
2.0 / w, 0.0, 0.0, -1.0,
0.0, -2.0 / h, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
])
g.matrix(m)
@generate
def texture_projection(g):
"""
This generates a matrix that project the Ren'Py space, where (0, 0) is the
top left and (`w`, `h`) is the bottom right, into the OpenGL render-to-texture
space, where (-1.0, -1.0) is the top left and (1.0, 1.0) is the bottom.
Generates the matrix that projects the Ren'Py screen to the OpenGL screen.
"""
w, h = g.parameters("w h")
m = Matrix(4, 4, [
2.0 / w, 0.0, 0.0, -1.0,
0.0, 2.0 / h, 0.0, -1.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
])
g.matrix(m)
if __name__ == "__main__":
import os
RENPY = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
write(os.path.join(RENPY, "renpy", "display", "matrix_functions.pxi"))
+1 -1
View File
@@ -6,7 +6,7 @@ from libc.stdlib cimport calloc, free
from cpython.buffer cimport PyObject_GetBuffer, PyBuffer_Release, PyBUF_CONTIG, PyBUF_CONTIG_RO
cimport uguugl
from uguugl import reset_error, get_error, enable_check_error, load
from uguugl import load
cdef object proxy_return_string(const GLubyte *s):
"""
-38
View File
@@ -12,41 +12,3 @@ cdef void *find_gl_command(names):
raise Exception("{} not found.".format(names[0]))
cdef const char *error_function
cdef GLenum error_code
def reset_error():
"""
Resets the
"""
global error_function
error_function = NULL
global error_code
error_code = GL_NO_ERROR
reset_error()
def get_error():
if error_function != NULL:
return error_function.decode("utf-8"), error_code
else:
return None, GL_NO_ERROR
cdef void check_error(const char *function) nogil:
global error_function
global error_code
cdef GLenum error
error = real_glGetError()
if (error_function == NULL) and (error != GL_NO_ERROR):
error_function = function
error_code = error
+1 -27
View File
@@ -304,28 +304,10 @@ class XMLToPYX:
c = self.commands[i]
w("")
w(f"cdef {i}_type real_{i}")
w(f"cdef {i}_type {i}")
w("")
param_list = c.format_param_list()
proxy_call = c.format_proxy_call()
if c.return_type != "void":
w(f'cdef {c.return_type} check_{i}{param_list} nogil:')
w(f' cdef {c.return_type} rv')
w(f' rv = real_{i}{proxy_call}')
w(f' check_error("{i}")')
w(f' return rv')
else:
w(f'cdef {c.return_type} check_{i}{param_list} nogil:')
w(f' real_{i}{proxy_call}')
w(f' check_error("{i}")')
w("")
w("def load():")
@@ -339,16 +321,8 @@ class XMLToPYX:
names = [ i.encode("utf-8") for i in names ]
w(f"")
w(f" global real_{i}, {i}")
w(f" real_{i} = <{i}_type> find_gl_command({names!r})")
w(f" {i} = real_{i}")
w("")
w("def enable_check_error():")
for i in sorted(self.features.commands):
w(f" global {i}")
w(f" {i} = check_{i}")
w(f" {i} = <{i}_type> find_gl_command({names!r})")
def generate_uguu_pyx(self, f):
+1
View File
@@ -48,3 +48,4 @@ BLIT = int
DISSOLVE = int
IMAGEDISSOLVE = int
PIXELLATE = int
FLATTEN = int
+13 -2
View File
@@ -40,7 +40,7 @@ except ImportError:
vc_version = 0
# The tuple giving the version number.
version_tuple = (7, 3, 2, vc_version)
version_tuple = (7, 3, 3, vc_version)
# The name of this version.
version_name = "The world (wide web) is not enough."
@@ -171,6 +171,7 @@ backup_blacklist = {
"renpy.test.testkey",
"renpy.test.testmouse",
"renpy.test.testparser",
"renpy.gl2",
"renpycoverage",
}
@@ -586,7 +587,7 @@ def reload_all():
renpy.display.render.mark_sweep()
# Get rid of the draw module and interface.
renpy.display.draw.deinit()
renpy.display.draw.quit()
renpy.display.draw = None
renpy.display.interface = None
@@ -654,6 +655,8 @@ def import_cython():
import renpy.arguments
import renpy.display.accelerator
import renpy.display.matrix
import renpy.display.render
import renpy.gl.gl
@@ -673,6 +676,14 @@ def import_cython():
import renpy.angle.glrtt_fbo
import renpy.angle.gltexture
import renpy.gl2.gl2draw
import renpy.gl2.gl2ftl
import renpy.gl2.gl2geometry
import renpy.gl2.gl2shader
import renpy.gl2.gl2texture
import renpy.gl2.uguu
import renpy.gl2.uguugl
if False:
import renpy.defaultstore as store
+6
View File
@@ -1526,16 +1526,19 @@ cdef extern from "glcompat.h" nogil:
cdef int glActiveTextureARB(GLenum) except? 0
cdef int glAttachObjectARB(GLhandleARB, GLhandleARB) except? 0
cdef int glBindFramebufferEXT(GLenum, GLuint) except? 0
cdef int glBindRenderbufferEXT(GLenum, GLuint) except? 0
cdef int glBindTexture(GLenum, GLuint) except? 0
cdef int glBlendFunc(GLenum, GLenum) except? 0
cdef int glClear(GLbitfield) except? 0
cdef int glClearColor(GLclampf, GLclampf, GLclampf, GLclampf) except? 0
cdef int glCompileShaderARB(GLhandleARB) except? 0
cdef int glCopyTexImage2D(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint) except? 0
cdef int glCopyTexSubImage2D(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei) except? 0
cdef GLhandleARB glCreateProgramObjectARB() except? 0
cdef GLhandleARB glCreateShaderObjectARB(GLenum) except? 0
cdef int glDeleteFramebuffersEXT(GLsizei, GLuint *) except? 0
cdef int glDeleteProgram(GLuint) except? 0
cdef int glDeleteRenderbuffersEXT(GLsizei, GLuint *) except? 0
cdef int glDeleteShader(GLuint) except? 0
cdef int glDeleteTextures(GLsizei, GLuint *) except? 0
cdef int glDisable(GLenum) except? 0
@@ -1544,8 +1547,10 @@ cdef int glDrawArrays(GLenum, GLint, GLsizei) except? 0
cdef int glEnable(GLenum) except? 0
cdef int glEnableVertexAttribArrayARB(GLuint) except? 0
cdef int glFinish() except? 0
cdef int glFramebufferRenderbufferEXT(GLenum, GLenum, GLenum, GLuint) except? 0
cdef int glFramebufferTexture2DEXT(GLenum, GLenum, GLenum, GLuint, GLint) except? 0
cdef int glGenFramebuffersEXT(GLsizei, GLuint *) except? 0
cdef int glGenRenderbuffersEXT(GLsizei, GLuint *) except? 0
cdef int glGenTextures(GLsizei, GLuint *) except? 0
cdef GLint glGetAttribLocationARB(GLhandleARB, GLchar *) except? 0
cdef int glGetIntegerv(GLenum, GLint *) except? 0
@@ -1557,6 +1562,7 @@ cdef GLchar * glGetString(GLenum) except? NULL
cdef GLint glGetUniformLocationARB(GLhandleARB, GLchar *) except? 0
cdef int glLinkProgramARB(GLhandleARB) except? 0
cdef int glReadPixels(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLubyte *) except? 0
cdef int glRenderbufferStorageEXT(GLenum, GLenum, GLsizei, GLsizei) except? 0
cdef int glScissor(GLint, GLint, GLsizei, GLsizei) except? 0
cdef int glShaderSourceARB(GLhandleARB, GLsizei, GLchar * *, GLint *) except? 0
cdef int glTexImage2D(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, GLubyte *) except? 0
+90 -2
View File
@@ -1,5 +1,3 @@
from __future__ import print_function
import os
import renpy
@@ -52,6 +50,21 @@ cdef int glBindFramebufferEXT(GLenum a0, GLuint a1) except? 0:
raise Exception(message)
return 1
cdef int glBindRenderbufferEXT(GLenum a0, GLuint a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glBindRenderbufferEXT')
cdef GLenum error
realGlBindRenderbufferEXT(a0, a1)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glBindRenderbufferEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glBindTexture(GLenum a0, GLuint a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glBindTexture')
@@ -128,6 +141,21 @@ cdef int glCompileShaderARB(GLhandleARB a0) except? 0:
raise Exception(message)
return 1
cdef int glCopyTexImage2D(GLenum a0, GLint a1, GLenum a2, GLint a3, GLint a4, GLsizei a5, GLsizei a6, GLint a7) except? 0:
if check_errors & 4:
renpy.display.log.write('glCopyTexImage2D')
cdef GLenum error
realGlCopyTexImage2D(a0, a1, a2, a3, a4, a5, a6, a7)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glCopyTexImage2D' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glCopyTexSubImage2D(GLenum a0, GLint a1, GLint a2, GLint a3, GLint a4, GLint a5, GLsizei a6, GLsizei a7) except? 0:
if check_errors & 4:
renpy.display.log.write('glCopyTexSubImage2D')
@@ -203,6 +231,21 @@ cdef int glDeleteProgram(GLuint a0) except? 0:
raise Exception(message)
return 1
cdef int glDeleteRenderbuffersEXT(GLsizei a0, GLuint * a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glDeleteRenderbuffersEXT')
cdef GLenum error
realGlDeleteRenderbuffersEXT(a0, a1)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glDeleteRenderbuffersEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glDeleteShader(GLuint a0) except? 0:
if check_errors & 4:
renpy.display.log.write('glDeleteShader')
@@ -323,6 +366,21 @@ cdef int glFinish() except? 0:
raise Exception(message)
return 1
cdef int glFramebufferRenderbufferEXT(GLenum a0, GLenum a1, GLenum a2, GLuint a3) except? 0:
if check_errors & 4:
renpy.display.log.write('glFramebufferRenderbufferEXT')
cdef GLenum error
realGlFramebufferRenderbufferEXT(a0, a1, a2, a3)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glFramebufferRenderbufferEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glFramebufferTexture2DEXT(GLenum a0, GLenum a1, GLenum a2, GLuint a3, GLint a4) except? 0:
if check_errors & 4:
renpy.display.log.write('glFramebufferTexture2DEXT')
@@ -353,6 +411,21 @@ cdef int glGenFramebuffersEXT(GLsizei a0, GLuint * a1) except? 0:
raise Exception(message)
return 1
cdef int glGenRenderbuffersEXT(GLsizei a0, GLuint * a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glGenRenderbuffersEXT')
cdef GLenum error
realGlGenRenderbuffersEXT(a0, a1)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glGenRenderbuffersEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glGenTextures(GLsizei a0, GLuint * a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glGenTextures')
@@ -518,6 +591,21 @@ cdef int glReadPixels(GLint a0, GLint a1, GLsizei a2, GLsizei a3, GLenum a4, GLe
raise Exception(message)
return 1
cdef int glRenderbufferStorageEXT(GLenum a0, GLenum a1, GLsizei a2, GLsizei a3) except? 0:
if check_errors & 4:
renpy.display.log.write('glRenderbufferStorageEXT')
cdef GLenum error
realGlRenderbufferStorageEXT(a0, a1, a2, a3)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glRenderbufferStorageEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glScissor(GLint a0, GLint a1, GLsizei a2, GLsizei a3) except? 0:
if check_errors & 4:
renpy.display.log.write('glScissor')
+3 -1
View File
@@ -356,9 +356,10 @@ class Node(object):
A node in the abstract syntax tree of the program.
@ivar name: The name of this node.
@ivar filename: The filename where this node comes from.
@ivar linenumber: The line number of the line on which this node is defined.
@ivar next: The statement that will execute after this one.
@ivar statement_start: If present, the first node that makes up the statement that includes this node.
"""
__slots__ = [
@@ -366,6 +367,7 @@ class Node(object):
'filename',
'linenumber',
'next',
'statement_start',
]
# True if this node is translatable, false otherwise. (This can be set on
+1
View File
@@ -1828,6 +1828,7 @@ def parse_atl(l):
if l.keyword('circles'):
expr = l.require(l.simple_expression)
rm.add_circles(expr)
continue
# Try to parse a property.
cp = l.checkpoint()
+54 -17
View File
@@ -36,6 +36,7 @@ import os
import re
import threading
import sys
import io
# Import the appropriate modules, or set them to None if we cannot.
@@ -73,6 +74,42 @@ def load(fn):
return rv
class AudioData(unicode):
"""
:doc: audio
This class wraps a bytes object containing audio data, so it can be
passed to the audio playback system. The audio data should be contained
in some format Ren'Py supports. (For examples RIFF WAV format headers,
not unadorned samples.)
`data`
A bytes object containing the audio file data.
`filename`
A synthetic filename associated with this data. It can be used to
suggest the format `data` is in, and is reported as part of
error messages.
Once created, this can be used wherever an audio filename is allowed. For
example::
define audio.easteregg = AudioData(b'...', 'sample.wav')
play sound easteregg
"""
def __new__(cls, data, filename):
rv = unicode.__new__(cls, filename)
rv.data = data
return rv
def __init__(self, data, filename):
pass
def __reduce__(self):
return(AudioData, (self.data, unicode(self)))
class QueueEntry(object):
"""
A queue entry object.
@@ -359,9 +396,6 @@ class Channel(object):
self.queue = [ ]
return
# Should we do the callback?
do_callback = False
topq = None
# This has been modified so we only queue a single sound file
@@ -417,7 +451,10 @@ class Channel(object):
if (end >= 0) and ((end - start) <= 0) and self.queue:
continue
topf = load(self.file_prefix + filename + self.file_suffix)
if isinstance(topq.filename, AudioData):
topf = io.BytesIO(topq.filename.data)
else:
topf = load(self.file_prefix + filename + self.file_suffix)
renpysound.set_video(self.number, self.movie)
@@ -442,20 +479,20 @@ class Channel(object):
break
if self.loop and not self.queue:
for i in self.loop:
if topq is not None:
newq = QueueEntry(i, 0, topq.tight, True)
else:
newq = QueueEntry(i, 0, False, True)
# Empty queue?
if not self.queue:
# Re-loop:
if self.loop:
for i in self.loop:
if topq is not None:
newq = QueueEntry(i, 0, topq.tight, True)
else:
newq = QueueEntry(i, 0, False, True)
self.queue.append(newq)
else:
do_callback = True
# Queue empty callback.
if do_callback and self.callback:
self.callback() # E1102
self.queue.append(newq)
# Try callback:
elif self.callback:
self.callback() # E1102
want_pause = self.context.pause or global_pause
+2
View File
@@ -87,6 +87,8 @@ def extra_imports():
import pstats; pstats
import _ssl; _ssl
import SimpleHTTPServer; SimpleHTTPServer
import wave; wave
import sunau; sunau
# Used by requests.
import cgi; cgi
+2 -2
View File
@@ -975,12 +975,10 @@ class ADVCharacter(object):
trans, layer = renpy.config.say_attribute_transition_callback(
self.image_tag, mode, *delta)
if trans is not None:
if layer is None:
renpy.exports.with_statement(trans)
else:
renpy.exports.with_statement(None)
renpy.exports.transition(trans, layer=layer)
def restore_say_attributes(self, predicting, state, interact):
@@ -1094,6 +1092,8 @@ class ADVCharacter(object):
if interact:
mode = _mode or self.mode
renpy.exports.mode(mode)
else:
renpy.game.context().deferred_translate_identifier = renpy.game.context().translate_identifier
# Figure out the arguments to display.
display_args = self.display_args.copy()
+30 -10
View File
@@ -74,19 +74,29 @@ init -1500 python:
"""
:doc: other_action
This allows an expression to control if a button should be marked
as selected. It should be used as part of a list with one or more
actions. For example::
This indicates that one action in a list of actions should be used
to determine if a button is selected. This only makes sense
when the button has a list of actions. For example::
# The button is selected if mars_flag is True
# The button is selected only if mars_flag is True
textbutton "Marsopolis":
action [ Jump("mars"), SelectedIf(mars_flag) ]
action [ SelectedIf(SetVariable("mars_flag", True)), SetVariable("on_mars", True) ]
The action inside SelectedIf is run normally when the button is clicked.
"""
# Note: This had been documented to take a boolean.
def __init__(self, expression):
self.expression = expression
if isinstance(expression, Action):
for i in [ "get_selected", "get_sensitive", "get_tooltip", "periodic", "unhovered", "unhovered" ]:
setattr(self, i, getattr(expression, i, None))
def __call__(self):
if isinstance(self.expression, Action):
return self.expression()
return None
def get_selected(self):
@@ -97,19 +107,29 @@ init -1500 python:
"""
:doc: other_action
This allows an expression to control if a button should be marked
as sensitive. It should be used as part of a list with one or more
actions. For example::
This indicates that one action in a list of actions should be used
to determine if a button is sensitive. This only makes sense
when the button has a list of actions. For example::
# The button is sensitive if mars_flag is True
# The button is sensitive only if mars_flag is True
textbutton "Marsopolis":
action [ Jump("mars"), SensitiveIf(mars_flag) ]
action [ SensitiveIf(SetVariable("mars_flag", True)), SetVariable("on_mars", True) ]
The action inside SensitiveIf is run normally when the button is clicked.
"""
# Note: This had been documented to take a boolean.
def __init__(self, expression):
self.expression = expression
if isinstance(expression, Action):
for i in [ "get_selected", "get_sensitive", "get_tooltip", "periodic", "unhovered", "unhovered" ]:
setattr(self, i, getattr(expression, i, None))
def __call__(self):
if isinstance(self.expression, Action):
return self.expression()
return None
def get_sensitive(self):
@@ -1,4 +1,4 @@
# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -19,30 +19,30 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
init -1900 python:
config.audio_directory = 'audio'
# The blacklist of OpenGL cards. Fields are:
# - A substring of the Renderer.
# - A substring of the Version.
# - True to allow shader rendering.
# - True to allow fixed-function rendering.
def _scan_audio_directory():
# If both of the last two entries are false, GL refuses to
# start.
import os
from __future__ import print_function
if not config.audio_directory:
return
BLACKLIST = [
prefix = config.audio_directory.rstrip('/') + '/'
# Crashes for Mugenjohncel.
("S3 Graphics DeltaChrome", "1.4 20.00", False, False),
for fn in renpy.list_files():
if not fn.startswith(prefix):
continue
# A bug in Mesa 7.9 and 7.10 (before 7.10.3) causes the system to
# fail to initialize the GLSL compiler.
# https://bugs.freedesktop.org/show_bug.cgi?id=35603
("Mesa", "Mesa 7.9", False, True),
("Mesa", "Mesa 7.10.3", True, True),
("Mesa", "Mesa 7.10", False, True),
basename = os.path.basename(fn)
base, ext = os.path.splitext(basename)
# Default to allowing everything.
("", "", True, True),
]
if not ext.lower() in [ ".wav", ".mp2", ".mp3", ".ogg", ".opus" ]:
continue
base = base.lower()
audio.__dict__.setdefault(base, fn)
init python:
_scan_audio_directory()
+23 -11
View File
@@ -145,17 +145,19 @@ init -1500 python:
offset = 0
action = None
force_step = False
identity_fields = [ 'dict' ]
equality_fields = [ 'key', 'range', 'max_is_zero', 'style', 'offset', 'step', 'action']
equality_fields = [ 'key', 'range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
def __init__(self, dict, key, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
def __init__(self, dict, key, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
self.dict = dict
self.key = key
self.range = range
self.max_is_zero = max_is_zero
self.style = style
self.offset = offset
self.force_step = force_step
if step is None:
if isinstance(range, float):
@@ -197,7 +199,9 @@ init -1500 python:
range=self.range,
value=value,
changed=self.changed,
step=self.step)
step=self.step,
force_step=self.force_step,
)
def get_style(self):
return self.style, "v" + self.style
@@ -236,17 +240,19 @@ init -1500 python:
offset = 0
action = None
force_step = False
identity_fields = [ 'object', ]
equality_fields = [ 'range', 'max_is_zero', 'style', 'offset', 'step', 'action']
equality_fields = [ 'range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
def __init__(self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
def __init__(self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
self.object = object
self.field = field
self.range = range
self.max_is_zero = max_is_zero
self.style = style
self.offset = offset
self.force_step = force_step
if step is None:
if isinstance(range, float):
@@ -288,13 +294,15 @@ init -1500 python:
range=self.range,
value=value,
changed=self.changed,
step=self.step)
step=self.step,
force_step=self.force_step,
)
def get_style(self):
return self.style, "v" + self.style
@renpy.pure
def VariableValue(variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
def VariableValue(variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
"""
:doc: value
@@ -323,7 +331,7 @@ init -1500 python:
If not None, an action to call when the field has changed.
"""
return FieldValue(store, variable, range, max_is_zero=max_is_zero, style=style, offset=offset, step=step, action=action)
return FieldValue(store, variable, range, max_is_zero=max_is_zero, style=style, offset=offset, step=step, action=action, force_step=force_step)
@renpy.pure
class ScreenVariableValue(BarValue, FieldEquality):
@@ -356,16 +364,18 @@ init -1500 python:
action = None
offset = 0
force_step = False
identity_fields = [ ]
equality_fields = [ 'variable', 'max_is_zero', 'style', 'offset', 'step', 'action']
equality_fields = [ 'variable', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
def __init__(self, variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
def __init__(self, variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
self.variable = variable
self.range = range
self.max_is_zero = max_is_zero
self.style = style
self.offset = offset
self.force_step = force_step
if step is None:
if isinstance(range, float):
@@ -414,7 +424,9 @@ init -1500 python:
range=self.range,
value=value,
changed=self.changed,
step=self.step)
step=self.step,
force_step=self.force_step,
)
def get_style(self):
return self.style, "v" + self.style
+2 -2
View File
@@ -389,13 +389,13 @@ init -1500 python in build:
mac_identity = None
# The command used for mac codesigning.
mac_codesign_command = [ "/usr/bin/codesign", "-s", "{identity}", "-f", "--deep", "--no-strict", "{app}" ]
mac_codesign_command = [ "/usr/bin/codesign", "--entitlements={entitlements}", "--options=runtime", "--timestamp", "-s", "{identity}", "-f", "--deep", "--no-strict", "{app}" ]
# The command used to build a dmg.
mac_create_dmg_command = [ "/usr/bin/hdiutil", "create", "-format", "UDBZ", "-volname", "{volname}", "-srcfolder", "{sourcedir}", "-ov", "{dmg}" ]
# The command used to sign a dmg.
mac_codesign_dmg_command = [ "/usr/bin/codesign", "-s", "{identity}", "-f", "{dmg}" ]
mac_codesign_dmg_command = [ "/usr/bin/codesign", "--timestamp", "-s", "{identity}", "-f", "{dmg}" ]
# Do we want to add the script_version file?
script_version = True
+4
View File
@@ -190,6 +190,10 @@ init -1900 python:
if version <= (7, 3, 0):
config.force_sound = False
if version <= (7, 3, 2):
config.audio_directory = None
config.early_start_store = True
# The version of Ren'Py this script is intended for, or
# None if it's intended for the current version.
config.script_version = None
+4 -1
View File
@@ -388,8 +388,11 @@ init -1500 python:
renderer_info = renpy.get_renderer_info()
if config.gl2 and not renderer_info.get("models", False):
problem = "fixed"
# Software renderer check.
if config.renderer != "sw" and renderer_info["renderer"] == "sw":
elif config.renderer != "sw" and renderer_info["renderer"] == "sw":
problem = "sw"
# Speed check.
+3
View File
@@ -39,6 +39,9 @@ init -1100 python in gui:
The height of the default window.
"""
if (not renpy.is_init_phase()) and config.developer:
raise Exception("gui.init may only be called during the init phase.")
config.screen_width = width
config.screen_height = height
+1
View File
@@ -456,6 +456,7 @@ init -1500 python:
renpy.mode('nvl_menu')
renpy.shown_window()
if nvl_list is None:
store.nvl_list = [ ]
+1 -1
View File
@@ -200,7 +200,7 @@ init -1500 python:
* Preference("gl tearing", False) - Skips frames rather than tearing.
* Preference("font transform", "opendyslexic") - Sets the accessibility font transform to opendyslexic.
* Preference("font transform", "dejavusans") - Sets the accessibility font trandform to deja vu sans.
* Preference("font transform", "dejavusans") - Sets the accessibility font transform to deja vu sans.
* Preference("font transform", None) - Disables the accessibility font transform.
* Preference("font size", 1.0) - Sets the accessibility font size scaling factor.
+84
View File
@@ -0,0 +1,84 @@
init python:
renpy.register_shader("renpy.geometry", variables="""
uniform mat4 uTransform;
attribute vec4 aPosition;
""", vertex_100="""
gl_Position = uTransform * aPosition;
""")
renpy.register_shader("renpy.texture", variables="""
uniform sampler2D uTex0;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
""", vertex_110="""
vTexCoord = aTexCoord;
""", fragment_110="""
gl_FragColor = texture2D(uTex0, vTexCoord.xy);
""")
renpy.register_shader("renpy.solid", variables="""
uniform vec4 uSolidColor;
""", fragment_110="""
gl_FragColor = uSolidColor;
""")
renpy.register_shader("renpy.dissolve", variables="""
uniform sampler2D uTex0;
uniform sampler2D uTex1;
uniform float uDissolve;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
""", vertex_110="""
vTexCoord = aTexCoord;
""", fragment_110="""
vec4 color0 = texture2D(uTex0, vTexCoord.st);
vec4 color1 = texture2D(uTex1, vTexCoord.st);
gl_FragColor = mix(color0, color1, uDissolve);
""")
renpy.register_shader("renpy.imagedissolve", variables="""
uniform sampler2D uTex0;
uniform sampler2D uTex1;
uniform sampler2D uTex2;
uniform float uDissolveOffset;
uniform float uDissolveMultiplier;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
""", vertex_110="""
vTexCoord = aTexCoord;
""", fragment_110="""
vec4 color0 = texture2D(uTex0, vTexCoord.st);
vec4 color1 = texture2D(uTex1, vTexCoord.st);
vec4 color2 = texture2D(uTex2, vTexCoord.st);
float a = clamp((color0.a + uDissolveOffset) * uDissolveMultiplier, 0.0, 1.0);
gl_FragColor = mix(color1, color2, a);
""")
renpy.register_shader("renpy.colormatrix", variables="""
uniform mat4 uColorMatrix;
""", fragment_120="""
gl_FragColor = gl_FragColor * uColorMatrix;
""")
renpy.register_shader("renpy.alpha", variables="""
uniform float uAlpha;
uniform float uOver;
""", fragment_130="""
gl_FragColor = gl_FragColor * vec4(uAlpha, uAlpha, uAlpha, uAlpha * uOver);
""")
renpy.register_shader("renpy.ftl", variables="""
attribute vec4 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
uniform sampler2D uTex0;
""", vertex_100="""
vTexCoord = aTexCoord;
gl_Position = aPosition;
""", fragment_100="""
gl_FragColor = texture2D(uTex0, vTexCoord.xy);
""")
+10 -2
View File
@@ -59,6 +59,9 @@ init -1600 python hide:
# Should we profile reloading?
config.profile_reload = False
# When should start_store happen?
config.early_start_store = False
init -1600 python:
def _init_language():
@@ -180,11 +183,17 @@ label _splashscreen:
# tell anyone.
label _start:
call _start_store
if config.early_start_store:
call _start_store
python:
renpy.execute_default_statement(True)
if not config.early_start_store:
call _start_store
python:
# Predict the main menu. When a load occurs, the loaded data will
# overwrite the prediction requests.
if renpy.has_screen("main_menu"):
@@ -192,7 +201,6 @@ label _start:
renpy.block_rollback()
scene black
if not _restart:
+18 -4
View File
@@ -170,8 +170,8 @@ init -1500 python:
:doc: voice_action
This allows the volume of each characters to be adjusted.
If `volume` is None, this returns the value of volume of `voice_tag`.
Otherwise, this set it to `volume`.
If `volume` is None, this returns a BarValue that
controls the value of `voice_tag`. Otherwise, this set it to `volume`.
`volume` is a number between 0.0 and 1.0, and is interpreted as a
fraction of the mixer volume for `voice` channel.
@@ -185,6 +185,17 @@ init -1500 python:
else:
return SetDict(persistent._character_volume, voice_tag, volume)
def GetCharacterVolume(voice_tag):
"""
:doc: preference_functions
This returns the volume associated with voice tag, a number
between 0.0 and 1.0, which is interpreted as a fraction of the
mixer volume for the `voice` channel.
"""
return persistent._character_volume.get(voice_tag, 1.0)
@renpy.pure
class PlayCharacterVoice(Action, FieldEquality):
"""
@@ -309,6 +320,7 @@ init -1500 python:
for tlid in [
renpy.game.context().translate_identifier,
renpy.game.context().alternate_translate_identifier,
renpy.game.context().deferred_translate_identifier,
]:
if tlid is None:
@@ -330,7 +342,7 @@ init -1500 python:
break
self.tlid = renpy.game.context().translate_identifier
self.tlid = renpy.game.context().translate_identifier or renpy.game.context().deferred_translate_identifier
if self.filename:
self.sustain = False
@@ -432,6 +444,8 @@ init -1500 python hide:
else:
_voice.play = None
renpy.game.context().deferred_translate_identifier = None
_voice.auto_file = vi.auto_filename
_voice.sustain = vi.sustain
_voice.tlid = vi.tlid
@@ -517,7 +531,7 @@ python early hide:
return fn
def execute_voice(fn):
fn = eval(fn)
fn = _audio_eval(fn)
voice(fn)
def lint_voice(fn):
+13
View File
@@ -954,6 +954,9 @@ ftfont_scale = { }
# This is used to scale the ascent and descent of a font.
ftfont_vertical_extent_scale = { }
# The default shader.
default_shader = "renpy.geometry"
def say_attribute_transition_callback(*args):
"""
@@ -993,6 +996,16 @@ disable_input = False
# also determine the order of their render.
keep_side_render_order = True
# Should this game enable and require gl2?
gl2 = False
# Does this game use the depth buffer? If so, how many bits of depth should
# it use?
depth_size = None
# A list of screens to remove when the context is copied.
context_copy_remove_screens = [ "notify" ]
del os
del collections
+4
View File
@@ -26,6 +26,7 @@ from renpy.minstore import *
# But please note that this will not be available in the body
# of user code, unless we re-import it.
import renpy.display
import renpy.audio
import renpy.text
import renpy.display.im as im
@@ -208,6 +209,7 @@ MultipleTransition = renpy.curry.curry(renpy.display.transition.MultipleTransiti
ComposeTransition = renpy.curry.curry(renpy.display.transition.ComposeTransition)
Pause = renpy.curry.curry(renpy.display.transition.NoTransition)
SubTransition = renpy.curry.curry(renpy.display.transition.SubTransition)
# Misc.
ADVSpeaker = ADVCharacter = renpy.character.ADVCharacter
Speaker = Character = renpy.character.Character
@@ -217,6 +219,8 @@ MultiPersistent = renpy.persistent.MultiPersistent
Action = renpy.ui.Action
BarValue = renpy.ui.BarValue
AudioData = renpy.audio.audio.AudioData
# NOTE: When exporting something from here, decide if we need to add it to
# renpy.pyanalysis.pure_functions.
+109 -43
View File
@@ -667,7 +667,7 @@ class SayBehavior(renpy.display.layout.Null):
elif renpy.game.context().seen_current(True):
return True
else:
renpy.config.skipping = False
renpy.config.skipping = None
renpy.exports.restart_interaction()
else:
@@ -702,6 +702,9 @@ class Button(renpy.display.layout.Window):
keysym = None
alternate_keysym = None
# This locks the displayable against further change.
locked = False
def __init__(self, child=None, style='button', clicked=None,
hovered=None, unhovered=None, action=None, role=None,
time_policy=None, keymap={}, alternate=None,
@@ -745,6 +748,14 @@ class Button(renpy.display.layout.Window):
return get_tooltip(self.action)
def _in_current_store(self):
rv = self._copy()
rv.style = self.style.copy()
rv.set_style_prefix(self.style.prefix, True)
rv.focusable = False
rv.locked = True
return rv
def predict_one_action(self):
predict_action(self.clicked)
predict_action(self.hovered)
@@ -840,41 +851,46 @@ class Button(renpy.display.layout.Window):
def per_interact(self):
if self.action is not None:
if self.is_selected():
role = 'selected_'
if not self.locked:
if self.action is not None:
if self.is_selected():
role = 'selected_'
else:
role = ''
if self.is_sensitive():
clicked = self.action
else:
clicked = None
role = ''
else:
role = ''
clicked = self.clicked
if self.is_sensitive():
clicked = self.action
if self.role_parameter is not None:
role = self.role_parameter
if (role != self.role) or (clicked is not self.clicked):
renpy.display.render.invalidate(self)
self.role = role
self.clicked = clicked
if self.clicked is not None:
self.set_style_prefix(self.role + "idle_", True)
self.focusable = True
else:
clicked = None
role = ''
else:
role = ''
clicked = self.clicked
if self.role_parameter is not None:
role = self.role_parameter
if (role != self.role) or (clicked is not self.clicked):
renpy.display.render.invalidate(self)
self.role = role
self.clicked = clicked
if self.clicked is not None:
self.set_style_prefix(self.role + "idle_", True)
self.focusable = True
else:
self.set_style_prefix(self.role + "insensitive_", True)
self.focusable = False
self.set_style_prefix(self.role + "insensitive_", True)
self.focusable = False
super(Button, self).per_interact()
def event(self, ev, x, y, st):
if self.locked:
return None
def handle_click(action):
renpy.exports.play(self.style.activate_sound)
@@ -938,7 +954,7 @@ class Button(renpy.display.layout.Window):
if (self.clicked is not None) and map_event(ev, "button_ignore"):
raise renpy.display.core.IgnoreEvent()
if (self.clicked is not None) and map_event(ev, "button_alternate_ignore"):
if (self.alternate is not None) and map_event(ev, "button_alternate_ignore"):
raise renpy.display.core.IgnoreEvent()
# If clicked,
@@ -1111,6 +1127,7 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
default = u""
edit_text = u""
value = None
shown = False
def __init__(self,
default="",
@@ -1171,15 +1188,10 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
self.content = replaces.content
self.editable = replaces.editable
self.caret_pos = replaces.caret_pos
self.shown = replaces.shown
self.update_text(self.content, self.editable)
def _show(self):
if self.default != self.content:
self.content = self.default
self.caret_pos = len(self.content)
self.update_text(self.content, self.editable)
def update_text(self, new_content, editable, check_size=False):
edit = renpy.display.interface.text_editing
@@ -1272,6 +1284,18 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
if self.value.default and (default_input_value is None):
default_input_value = self.value
if not self.shown:
if self.value is not None:
default = self.value.get_text()
self.default = unicode(default)
self.content = self.default
self.caret_pos = len(self.content)
self.update_text(self.content, self.editable)
self.shown = True
def event(self, ev, x, y, st):
self.old_caret_pos = self.caret_pos
@@ -1370,7 +1394,10 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
if ev.unicode and ord(ev.unicode[0]) >= 32:
raw_text = ev.unicode
elif renpy.display.interface.text_event_in_queue():
raw_text = ''
raise renpy.display.core.IgnoreEvent()
elif (32 <= ev.key < 127) and not (ev.mod & (pygame.KMOD_ALT | pygame.KMOD_META)):
# Ignore printable keycodes without unicode.
raise renpy.display.core.IgnoreEvent()
if raw_text is not None:
@@ -1428,7 +1455,9 @@ class Adjustment(renpy.object.Object):
"""
def __init__(self, range=1, value=0, step=None, page=None, changed=None, adjustable=None, ranged=None): # @ReservedAssignment
force_step = False
def __init__(self, range=1, value=0, step=None, page=None, changed=None, adjustable=None, ranged=None, force_step=False): # @ReservedAssignment
"""
The following parameters correspond to fields or properties on
the adjustment object:
@@ -1456,12 +1485,12 @@ class Adjustment(renpy.object.Object):
The following parameters control the behavior of the adjustment.
`adjustable`
If True, this adjustment can be changed by a bar. If False,
it can't.
If True, this adjustment can be changed by a bar. If False,
it can't.
It defaults to being adjustable if a `changed` function
is given or if the adjustment is associated with a viewport,
and not adjustable otherwise.
It defaults to being adjustable if a `changed` function
is given or if the adjustment is associated with a viewport,
and not adjustable otherwise.
`changed`
This function is called with the new value when the value of
@@ -1471,6 +1500,16 @@ class Adjustment(renpy.object.Object):
This function is called with the adjustment object when
the range of the adjustment is set by a viewport.
`force_step`
If True and this adjustment changes by dragging associated
viewport or a bar, value will be changed only if the drag
reached next step.
If "release" and this adjustment changes by dragging associated
viewport or a bar, after the release, value will be
rounded to the nearest step.
If False, this adjustment will changes by dragging, ignoring
the step value.
.. method:: change(value)
Changes the value of the adjustment to `value`, updating
@@ -1490,9 +1529,27 @@ class Adjustment(renpy.object.Object):
self.changed = changed
self.adjustable = adjustable
self.ranged = ranged
self.force_step = force_step
def round_value(self, value, release):
# Prevent deadlock border points
if value <= 0:
return 0
elif value >= self._range:
return self._range
if self.force_step is False:
return value
if (not release) and self.force_step == "release":
return value
return type(self.value)(self.step * round(float(value) / self.step))
def get_value(self):
if self._value > self._range:
if self._value <= 0:
return 0
if self._value >= self._range:
return self._range
return self._value
@@ -1907,9 +1964,18 @@ class Bar(renpy.display.core.Displayable):
renpy.display.tts.speak(renpy.minstore.__("deactivate"))
self.set_style_prefix("hover_", True)
renpy.display.focus.set_grab(None)
ignore_event = True
# Invoke rounding adjustment on bar release
value = self.adjustment.round_value(value, release=True)
if value != old_value:
rv = self.adjustment.change(value)
if rv is not None:
return rv
raise renpy.display.core.IgnoreEvent()
if value != old_value:
value = self.adjustment.round_value(value, release=False)
rv = self.adjustment.change(value)
if rv is not None:
return rv
+18 -10
View File
@@ -1921,6 +1921,10 @@ class Interface(object):
renderers = [ "gl", "angle", "sw" ]
else:
renderers = [ "gl", "sw" ]
if renpy.config.gl2:
renderers = [ "gl2", "egl2" ] + renderers
else:
renderers = [ renderer, "sw" ]
@@ -1968,6 +1972,9 @@ class Interface(object):
return rv
def kill_textures(self):
if renpy.display.draw is not None:
renpy.display.draw.kill_textures()
renpy.display.render.free_memory()
renpy.text.text.layout_cache_clear()
@@ -1977,7 +1984,6 @@ class Interface(object):
"""
self.kill_textures()
renpy.display.im.cache.clear()
renpy.display.module.bo_cache = None
@@ -1990,14 +1996,16 @@ class Interface(object):
if renpy.display.draw and renpy.display.draw.info["renderer"] == "sw":
renpy.display.video.movie_stop(clear=False)
renpy.display.render.free_memory()
renpy.text.text.layout_cache_clear()
renpy.display.module.bo_cache = None
if self.display_reset:
pygame.key.stop_text_input() # @UndefinedVariable
pygame.key.set_text_input_rect(None) # @UndefinedVariable
self.text_rect = None
renpy.display.draw.deinit()
if renpy.display.draw.info["renderer"] == "angle":
renpy.display.draw.quit()
@@ -2005,13 +2013,7 @@ class Interface(object):
# minimized state on windows.
pygame.display.quit()
renpy.display.render.free_memory()
renpy.display.im.cache.clear()
renpy.text.text.layout_cache_clear()
renpy.display.module.bo_cache = None
self.kill_textures_and_surfaces()
self.kill_textures_and_surfaces()
self.old_text_rect = None
self.display_reset = False
@@ -2042,6 +2044,7 @@ class Interface(object):
for draw in draws:
if draw.set_mode(virtual_size, physical_size, fullscreen):
renpy.display.draw = draw
renpy.display.render.models = draw.info.get("models", False)
break
else:
# pygame.display.quit()
@@ -3419,6 +3422,11 @@ class Interface(object):
size = (ev.w // self.dpi_scale, ev.h // self.dpi_scale)
# Refresh fullscreen status (e.g. user pressed Esc. in browser)
main_window = pygame.display.get_window()
self.fullscreen = main_window is not None and bool(main_window.get_window_flags() & (pygame.WINDOW_FULLSCREEN_DESKTOP|pygame.WINDOW_FULLSCREEN))
renpy.game.preferences.fullscreen = self.fullscreen
if pygame.display.get_surface().get_size() != ev.size:
self.set_mode(size)
+11 -6
View File
@@ -88,7 +88,8 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
When a Drag is first rendered, if it's position cannot be determined
from the DragGroup it is in, the position of its upper-left corner
is computed using the standard layout algorithm. Once that position
has been computed, the layout properties are ignored in favor of the
position stored inside the Drag.
`d`
If present, the child of this Drag. Drags use the child style
@@ -601,10 +602,12 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
raise renpy.display.core.IgnoreEvent()
if ((self.alternate is not None) and
if (
(self.alternate is not None) and
renpy.display.touch and
(self.click_time is not None) and
((st - self.click_time) > renpy.config.longpress_duration)):
((st - self.click_time) > renpy.config.longpress_duration)
):
self.click_time = None
@@ -612,6 +615,8 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
if rv is not None:
return rv
renpy.exports.vibrate(renpy.config.longpress_vibrate)
# Handle clicking on droppables.
if not grabbed:
if self.clicked is not None and map_event(ev, "drag_deactivate"):
@@ -684,11 +689,11 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
if self.last_drop is not None:
self.last_drop.set_style_prefix("idle_", True)
if drop is not None:
drop.set_style_prefix("selected_idle_", True)
self.last_drop = drop
if drop is not None:
drop.set_style_prefix("selected_idle_", True)
if map_event(ev, 'drag_deactivate'):
self.click_time = None
-1
View File
@@ -532,7 +532,6 @@ def free_memory():
Frees some memory.
"""
renpy.display.draw.free_memory()
cache.clear()
+72 -18
View File
@@ -152,8 +152,8 @@ class Frame(renpy.display.core.Displayable):
An image manipulator that will be resized by this frame.
`left`
The size of the border on the left side. This can also be an
:func:`Borders` object, in which case that object is use in place
The size of the border on the left side. This can also be a
:func:`Borders` object, in which case that object is used in place
of the other parameters.
`top`
@@ -167,8 +167,10 @@ class Frame(renpy.display.core.Displayable):
The side of the border on the bottom. If None, defaults to `top`.
`tile`
If true, tiling is used to resize sections of the image,
rather than scaling.
If set to True, tiling is used to resize sections of the image,
rather than scaling. If set to the string "integer", the nearest
integer number of tiles will be used in each direction. That set of
full tiles will then be scaled up or down to fit the required area.
::
@@ -180,6 +182,7 @@ class Frame(renpy.display.core.Displayable):
__version__ = 1
properties = { }
tile_ratio = 0.5
def after_upgrade(self, version):
if version < 2:
@@ -188,7 +191,9 @@ class Frame(renpy.display.core.Displayable):
self.top = self.yborder
self.bottom = self.yborder
def __init__(self, image, left=None, top=None, right=None, bottom=None, xborder=0, yborder=0, bilinear=True, tile=False, **properties):
def __init__(self, image, left=None, top=None, right=None, bottom=None,
xborder=0, yborder=0, bilinear=True, tile=False,
tile_ratio=0.5, **properties):
super(Frame, self).__init__(**properties)
self.image = renpy.easy.displayable(image)
@@ -203,6 +208,9 @@ class Frame(renpy.display.core.Displayable):
bottom = insets.bottom
self.tile = tile
# When tile="integer" the proportion of an edge tile that determines
# whether to use less tiles and stretch or more tiles and shrink
self.tile_ratio = float(tile_ratio)
# Compat for old argument names.
if left is None:
@@ -227,7 +235,9 @@ class Frame(renpy.display.core.Displayable):
self.top,
self.right,
self.bottom,
" tile" if self.tile else "")
(" tile ({})".format(self.tile_ratio) if self.tile == "integer"
else " tile" if self.tile
else ""))
def __eq__(self, o):
if not self._equals(o):
@@ -248,6 +258,9 @@ class Frame(renpy.display.core.Displayable):
if self.tile != o.tile:
return False
if self.tile_ratio != o.tile_ratio:
return False
return True
def render(self, width, height, st, at):
@@ -345,18 +358,36 @@ class Frame(renpy.display.core.Displayable):
if csw != cdw or csh != cdh:
if self.tile:
newcr = Render(cdw, cdh)
ctw, cth = cdw, cdh
xtiles = max(1, cdw // csw + (1 if cdw % csw else 0))
ytiles = max(1, cdh // csh + (1 if cdh % csh else 0))
if cdw % csw or cdh % csh:
# Area is not an exact integer number of tiles
if self.tile == "integer":
if cdw % csw / float(csw) < self.tile_ratio:
xtiles = max(1, xtiles-1)
if cdh % csh / float(csh) < self.tile_ratio:
ytiles = max(1, ytiles-1)
# Set size of the used tiles (ready to scale)
ctw, cth = csw * xtiles, csh * ytiles
newcr = Render(ctw, cth)
newcr.xclipping = True
newcr.yclipping = True
for x in xrange(0, cdw, csw):
for y in xrange(0, cdh, csh):
newcr.blit(cr, (x, y))
for x in xrange(0, xtiles):
for y in xrange(0, ytiles):
newcr.blit(cr, (x * csw, y * csh))
csw, csh = ctw, cth
cr = newcr
else:
if csw != cdw or csh != cdh:
# Subsurface needs scaling
newcr = Render(cdw, cdh)
newcr.forward = Matrix2D(1.0 * csw / cdw, 0, 0, 1.0 * csh / cdh)
newcr.reverse = Matrix2D(1.0 * cdw / csw, 0, 0, 1.0 * cdh / csh)
@@ -465,19 +496,42 @@ class Frame(renpy.display.core.Displayable):
# Scale or tile if we have to.
if dstsize != srcsize:
if self.tile:
tilew, tileh = srcsize
dstw, dsth = dstsize
surf2 = renpy.display.pgrender.surface_unscaled(dstsize, surf)
xtiles = max(
1, dstw // tilew + (1 if dstw % tilew else 0))
ytiles = max(
1, dsth // tileh + (1 if dsth % tileh else 0))
for y in range(0, dsth, tileh):
for x in range(0, dstw, tilew):
surf2.blit(surf, (x, y))
if dstw % tilew or dsth % tileh:
# Area is not an exact integer number of tiles
surf = surf2
if self.tile == "integer":
if dstw % tilew / float(tilew) < self.tile_ratio:
xtiles = max(1, xtiles-1)
if dsth % tileh / float(tileh) < self.tile_ratio:
ytiles = max(1, ytiles-1)
else:
# Tile at least one tile in each direction
surf2 = renpy.display.pgrender.surface_unscaled(
(tilew * xtiles, tileh * ytiles), surf)
for y in range(0, ytiles):
for x in range(0, xtiles):
surf2.blit(surf, (x*tilew, y*tileh))
if self.tile is True:
# Trim the tiled surface to required size
surf = surf2.subsurface((0, 0, dstw, dsth))
else:
# Using integer full 'tiles' per side
srcsize = (tilew * xtiles, tileh * ytiles)
surf = surf2
if dstsize != srcsize:
surf2 = renpy.display.scale.real_transform_scale(surf, dstsize)
surf = surf2
+21 -6
View File
@@ -1230,6 +1230,8 @@ class DynamicDisplayable(renpy.display.core.Displayable):
nosave = [ 'child' ]
_duplicatable = True
def after_setstate(self):
self.child = None
@@ -1248,12 +1250,25 @@ class DynamicDisplayable(renpy.display.core.Displayable):
self.args = args
self.kwargs = kwargs
def _duplicate(self, args):
rv = self._copy(args)
if rv.child is not None and rv.child._duplicateable:
rv.child = rv.child._duplicate(args)
return rv
def visit(self):
return [ ]
def update(self, st, at):
child, redraw = self.function(st, at, *self.args, **self.kwargs)
child = renpy.easy.displayable(child)
if child._duplicatable:
child = child._duplicate(self._args)
child._unique()
child.visit_all(lambda c : c.per_interact())
self.child = child
@@ -1856,14 +1871,13 @@ class Flatten(Container):
cr = renpy.display.render.render(self.child, width, height, st, at)
cw, ch = cr.get_size()
tex = cr.render_to_texture(True)
rv = renpy.display.render.Render(cw, ch)
rv.blit(tex, (0, 0))
rv.depends_on(cr, focus=True)
rv.blit(cr, (0, 0))
rv.reverse = renpy.display.draw.draw_to_virt
rv.forward = renpy.display.render.IDENTITY
rv.operation = renpy.display.render.FLATTEN
rv.mesh = True
rv.shaders = ( "renpy.texture", )
self.offsets = [ (0, 0) ]
@@ -1872,6 +1886,7 @@ class Flatten(Container):
def get_placement(self):
return self.child.get_placement()
class AlphaMask(Container):
"""
:doc: disp_imagelike
+44
View File
@@ -0,0 +1,44 @@
cdef class Matrix:
cdef float m[0]
cdef public float xdx
cdef public float xdy
cdef public float xdz
cdef public float xdw
cdef public float ydx
cdef public float ydy
cdef public float ydz
cdef public float ydw
cdef public float zdx
cdef public float zdy
cdef public float zdz
cdef public float zdw
cdef public float wdx
cdef public float wdy
cdef public float wdz
cdef public float wdw
cdef inline void transform4(Matrix self, float *ox, float *oy, float *oz, float *ow, float x, float y, float z, float w):
ox[0] = x * self.xdx + y * self.xdy + z * self.xdz + w * self.xdw
oy[0] = x * self.ydx + y * self.ydy + z * self.ydz + w * self.ydw
oz[0] = x * self.zdx + y * self.zdy + z * self.zdz + w * self.zdw
ow[0] = x * self.wdx + y * self.wdy + z * self.wdz + w * self.wdw
cdef inline void transform3(Matrix self, float *ox, float *oy, float *oz, float x, float y, float z, float w):
ox[0] = x * self.xdx + y * self.xdy + z * self.xdz + w * self.xdw
oy[0] = x * self.ydx + y * self.ydy + z * self.ydz + w * self.ydw
oz[0] = x * self.zdx + y * self.zdy + z * self.zdz + w * self.zdw
cdef inline void transform2(Matrix self, float *ox, float *oy, float x, float y, float z, float w):
ox[0] = x * self.xdx + y * self.xdy + z * self.xdz + w * self.xdw
oy[0] = x * self.ydx + y * self.ydy + z * self.ydz + w * self.ydw
cpdef bint is_unit_aligned(Matrix self)
cdef class Matrix2D(Matrix):
pass
+191
View File
@@ -0,0 +1,191 @@
from __future__ import print_function
from libc.string cimport memset
from libc.math cimport sin, cos, M_PI as pi
cdef float *aligned_1 = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
cdef float *aligned_2 = [ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
fields = [
"xdx", "xdy", "xdz", "xdw",
"ydx", "ydy", "ydz", "ydw",
"zdx", "zdy", "zdz", "zdw",
"wdx", "wdy", "wdz", "wdw",
]
cdef class Matrix:
"""
Represents a 4x4 matrix.
"""
def __init__(Matrix self, l):
memset(self.m, 0, sizeof(float) * 16)
if l is None:
return
cdef int lenl = len(l)
if lenl == 4:
(self.xdx, self.xdy,
self.ydx, self.ydy) = l
self.ydy = 1.0
self.wdw = 1.0
elif lenl == 9:
(self.xdx, self.xdy, self.xdz,
self.ydx, self.ydy, self.ydz,
self.zdx, self.zdy, self.zdz) = l
self.wdw = 1.0
elif lenl == 16:
(self.xdx, self.xdy, self.xdz, self.xdw,
self.ydx, self.ydy, self.ydz, self.ydw,
self.zdx, self.zdy, self.zdz, self.zdw,
self.wdx, self.wdy, self.wdz, self.wdw) = l
else:
raise Exception("Unsupported matrix length {} (must be 4, 9, or 16).".format(len(l)))
def __getstate__(self):
rv = { }
for i in range(16):
rv[fields[i]] = self.m[i]
return rv
def __setstate__(self, state):
memset(self.m, 0, sizeof(float) * 16)
self.zdz = 1.0
self.wdw = 1.0
for i in range(16):
if fields[i] in state:
self.m[i] = state[fields[i]]
def __mul__(Matrix self, Matrix other):
cdef Matrix rv = Matrix(None)
rv.xdx = other.wdx*self.xdw + other.xdx*self.xdx + other.ydx*self.xdy + other.zdx*self.xdz
rv.xdy = other.wdy*self.xdw + other.xdy*self.xdx + other.ydy*self.xdy + other.zdy*self.xdz
rv.xdz = other.wdz*self.xdw + other.xdz*self.xdx + other.ydz*self.xdy + other.zdz*self.xdz
rv.xdw = other.wdw*self.xdw + other.xdw*self.xdx + other.ydw*self.xdy + other.zdw*self.xdz
rv.ydx = other.wdx*self.ydw + other.xdx*self.ydx + other.ydx*self.ydy + other.zdx*self.ydz
rv.ydy = other.wdy*self.ydw + other.xdy*self.ydx + other.ydy*self.ydy + other.zdy*self.ydz
rv.ydz = other.wdz*self.ydw + other.xdz*self.ydx + other.ydz*self.ydy + other.zdz*self.ydz
rv.ydw = other.wdw*self.ydw + other.xdw*self.ydx + other.ydw*self.ydy + other.zdw*self.ydz
rv.zdx = other.wdx*self.zdw + other.xdx*self.zdx + other.ydx*self.zdy + other.zdx*self.zdz
rv.zdy = other.wdy*self.zdw + other.xdy*self.zdx + other.ydy*self.zdy + other.zdy*self.zdz
rv.zdz = other.wdz*self.zdw + other.xdz*self.zdx + other.ydz*self.zdy + other.zdz*self.zdz
rv.zdw = other.wdw*self.zdw + other.xdw*self.zdx + other.ydw*self.zdy + other.zdw*self.zdz
rv.wdx = other.wdx*self.wdw + other.xdx*self.wdx + other.ydx*self.wdy + other.zdx*self.wdz
rv.wdy = other.wdy*self.wdw + other.xdy*self.wdx + other.ydy*self.wdy + other.zdy*self.wdz
rv.wdz = other.wdz*self.wdw + other.xdz*self.wdx + other.ydz*self.wdy + other.zdz*self.wdz
rv.wdw = other.wdw*self.wdw + other.xdw*self.wdx + other.ydw*self.wdy + other.zdw*self.wdz
return rv
def __getitem__(Matrix self, int index):
if 0 <= index < 16:
return self.m[index]
raise IndexError("Matrix index out of range.")
def __setitem__(Matrix self, int index, float value):
if 0 <= index < 16:
self.m[index] = value
return
raise IndexError("Matrix index out of range.")
def __repr__(Matrix self):
cdef int x, y
rv = "Matrix(["
for 0 <= y < 4:
if y:
rv += "\n "
for 0 <= x < 4:
rv += "{:8.5f}, ".format(self.m[x + y * 4])
return rv + "])"
def transform(Matrix self, float x, float y, float z=0.0, float w=1.0, int components=2):
cdef float ox, oy, oz, ow
self.transform4(&ox, &oy, &oz, &ow, x, y, z, w)
if components == 2:
return (ox, oy)
elif components == 3:
return (ox, oy, oz)
elif components == 4:
return (ox, oy, oz, ow)
def __richcmp__(Matrix self, Matrix other, op):
if op != 2:
return NotImplemented
if self is other:
return True
cdef int i
cdef double total
total = 0
for 0 < i < 16:
total += abs(self.m[i] - other.m[i])
return total < .0001
cpdef bint is_unit_aligned(Matrix self):
"""
Returns true if exactly one of abs(xdx) or abs(xdy) is 1.0, and
the same for xdy and ydy. This is intended to report if a matrix
is aligned to the axes.
"""
cdef int i
cdef float v
cdef float total_1
cdef float total_2
total_1 = 0
total_2 = 0
for 0 < i < 16:
v = abs(self.m[i])
total_1 += abs(i - aligned_1[i])
total_2 += abs(i - aligned_2[i])
return (total_1 < .0001) or (total_2 < .0001)
cdef class Matrix2D(Matrix):
def __init__(Matrix2D self, double xdx, double xdy, double ydx, double ydy):
memset(self.m, 0, sizeof(float) * 16)
self.xdx = xdx
self.xdy = xdy
self.ydx = ydx
self.ydy = ydy
self.zdz = 1.0
self.wdw = 1.0
include "matrix_functions.pxi"
+141
View File
@@ -0,0 +1,141 @@
def identity():
"""
Returns an identity matrix.
"""
cdef Matrix rv = Matrix(None)
rv.xdx = 1.00000000000000
rv.ydy = 1.00000000000000
rv.zdz = 1.00000000000000
rv.wdw = 1.00000000000000
return rv
def offset(float x, float y, float z):
"""
Returns a matrix that offsets the vertex by a fixed amount.
"""
cdef Matrix rv = Matrix(None)
rv.xdx = 1.00000000000000
rv.xdw = x
rv.ydy = 1.00000000000000
rv.ydw = y
rv.zdz = 1.00000000000000
rv.zdw = z
rv.wdw = 1.00000000000000
return rv
def rotate(float x, float y, float z):
"""
Returns a matrix that rotates the displayable around the
origin.
`x`, `y`, `x`
The amount to rotate around the origin, in degrees.
"""
cdef float sinx = sin(pi*x/180)
cdef float cosx = cos(pi*x/180)
cdef float siny = sin(pi*y/180)
cdef float cosy = cos(pi*y/180)
cdef float sinz = sin(pi*z/180)
cdef float cosz = cos(pi*z/180)
cdef Matrix rv = Matrix(None)
rv.xdx = cosy*cosz
rv.xdy = -cosx*sinz + cosz*sinx*siny
rv.xdz = cosx*cosz*siny + sinx*sinz
rv.ydx = cosy*sinz
rv.ydy = cosx*cosz + sinx*siny*sinz
rv.ydz = cosx*siny*sinz - cosz*sinx
rv.zdx = -siny
rv.zdy = cosy*sinx
rv.zdz = cosx*cosy
rv.wdw = 1
return rv
def perspective(float w, float h, float n, float p, float f):
"""
Returns the Ren'Py projection matrix. This is a view into a 3d space
where (0, 0) is the top left corner (`w`/2, `h`/2) is the center, and
(`w`,`h`) is the bottom right, when the z coordinate is 0.
`w`, `h`
The width and height of the input plane, in pixels.
`n`
The distance of the near plane from the camera.
`p`
The distance of the 1:1 plane from the camera. This is where 1 pixel
is one coordinate unit.
`f`
The distance of the far plane from the camera.
"""
cdef Matrix rv = Matrix(None)
rv.xdx = p
rv.xdz = -w/2
rv.ydy = p
rv.ydz = -h/2
rv.zdz = -(f + n)/(f - n)
rv.zdw = (-2*f*n + p*(f + n))/(f - n)
rv.wdz = -1.00000000000000
rv.wdw = p
return rv
def screen_projection(float w, float h):
"""
This generates a matrix that projects the Ren'Py space, where (0, 0) is the
top left and (`w`, `h`) is the bottom right, into the OpenGL viewport, where
(-1.0, 1.0) is the top left and (1.0, -1.0) is the bottom.
Generates the matrix that projects the Ren'Py screen to the OpenGL screen.
"""
cdef Matrix rv = Matrix(None)
rv.xdx = 2/w
rv.xdw = -1.00000000000000
rv.ydy = -2/h
rv.ydw = 1.00000000000000
rv.zdz = 1.00000000000000
rv.wdw = 1.00000000000000
return rv
def texture_projection(float w, float h):
"""
This generates a matrix that project the Ren'Py space, where (0, 0) is the
top left and (`w`, `h`) is the bottom right, into the OpenGL render-to-texture
space, where (-1.0, -1.0) is the top left and (1.0, 1.0) is the bottom.
Generates the matrix that projects the Ren'Py screen to the OpenGL screen.
"""
cdef Matrix rv = Matrix(None)
rv.xdx = 2/w
rv.xdw = -1.00000000000000
rv.ydy = 2/h
rv.ydw = -1.00000000000000
rv.zdz = 1.00000000000000
rv.wdw = 1.00000000000000
return rv
+1 -2
View File
@@ -171,8 +171,7 @@ class SpriteManager(renpy.display.core.Displayable):
SpriteManagers have the following fields:
`width`, `height`
The width and height of this SpriteManager, in pixels.
The width and height of this SpriteManager, in pixels.
SpriteManagers have the following methods:
+45 -9
View File
@@ -19,14 +19,8 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cdef class Matrix2D:
cdef public double xdx
cdef public double xdy
cdef public double ydx
cdef public double ydy
cpdef tuple transform(Matrix2D self, double x, double y)
cpdef bint is_unit_aligned(Matrix2D self)
from renpy.display.matrix cimport Matrix, Matrix2D
cdef class Render:
@@ -39,14 +33,27 @@ cdef class Render:
cdef public set parents
cdef public list depends_on_list
# gl, sw: The drawing operation and its parameters.
cdef public int operation
cdef public double operation_complete
cdef public bint operation_alpha
cdef public object operation_parameter
cdef public Matrix2D forward, reverse
# The transform toward texture space
cdef public Matrix forward
# The transform toward screen space.
cdef public Matrix reverse
# Alpha multiplication - this is multipled with r, g, b, and a, to reduce
# the alpha of this Render and its children.
cdef public double alpha
# Over multiplication - this is multiplied with a. This is really (1-additive),
# such that if this is 0.0, additive blending occurs.
cdef public double over
# True if child textures should be rendered in nearest-neighbor mode.
cdef public object nearest
cdef public list focuses
@@ -62,12 +69,41 @@ cdef class Render:
cdef public bint xclipping
cdef public bint yclipping
# gl, sw: Caching of render-to-texture.
cdef public object surface, alpha_surface, half_cache
cdef public bint modal
cdef public bint text_input
# gl2 ######################################################################
# The mesh. If this is not None, the children are all rendered to Textures,
# and used to form a model. If this is True, the Mesh is taken from the first
# child's Texture, otherwise this must be a Mesh.
cdef public object mesh
# A tuple of shaders that will be used when rendering, or None.
cdef public tuple shaders
# A dictionary containing uniforms that will be used when rendering, or
# None.
cdef public dict uniforms
# Properties that control rendering.
cdef public dict properties
# Used to cache the result of rendering this Render to a texture.
cdef public object cached_texture
# Used to cache the model.
cdef public object cached_model
# True if the texture has been loaded.
cdef public bint loaded
# operations ###############################################################
cpdef int blit(Render self, source, tuple pos, object focus=*, object main=*, object index=*)
cpdef int subpixel_blit(Render self, source, tuple pos, object focus=*, object main=*, object index=*)
cpdef int absolute_blit(Render self, source, tuple pos, object focus=*, object main=*, object index=*)
+65 -93
View File
@@ -22,6 +22,14 @@
from __future__ import print_function
from renpy.display.matrix import Matrix, Matrix2D
from renpy.display.matrix cimport Matrix, Matrix2D
# This is required to get these to re-export despite having been defined
# in cython.
globals()["Matrix"] = Matrix
globals()["Matrix2D"] = Matrix2D
import collections
import pygame_sdl2 as pygame
import threading
@@ -59,6 +67,10 @@ per_frame = False
# Are we rendering for the purpose of sizing something.
sizing = False
# This is true if we're using a renderer that supports models,
# false otherwise.
models = False
def adjust_render_cache_times(old_time, new_time):
"""
This adjusts the render cache such that if a render starts at
@@ -410,74 +422,6 @@ def redraw(d, when):
redraw_queue.append((when + renpy.game.interface.frame_time, d))
cdef class Matrix2D:
"""
This represents a 2d matrix that can be used to transform
points and things like that.
"""
def __getstate__(self):
return dict(
xdx = self.xdx,
xdy = self.xdy,
ydx = self.ydx,
ydy = self.ydy)
def __setstate__(self, state):
self.xdx = state['xdx']
self.xdy = state['xdy']
self.ydx = state['ydx']
self.ydy = state['ydy']
def __init__(Matrix2D self, double xdx, double xdy, double ydx, double ydy):
self.xdx = xdx
self.xdy = xdy
self.ydx = ydx
self.ydy = ydy
cpdef tuple transform(Matrix2D self, double x, double y):
return (x * self.xdx + y * self.xdy), (x * self.ydx + y * self.ydy)
def __mul__(Matrix2D self, Matrix2D other):
return Matrix2D(
other.xdx * self.xdx + other.xdy * self.ydx,
other.xdx * self.xdy + other.xdy * self.ydy,
other.ydx * self.xdx + other.ydy * self.ydx,
other.ydx * self.xdy + other.ydy * self.ydy)
def __repr__(self):
return "Matrix2D(xdx=%f, xdy=%f, ydx=%f, ydy=%f)" % (self.xdx, self.xdy, self.ydx, self.ydy)
def __richcmp__(self, other, op):
if op != 2:
return NotImplemented
if self is other:
return True
return (
abs(self.xdx - other.xdx) +
abs(self.xdy - other.xdy) +
abs(self.ydx - other.ydx) +
abs(self.ydy - other.ydy)) < .00001
cpdef bint is_unit_aligned(Matrix2D self):
"""
Returns true if exactly one of abs(xdx) or abs(xdy) is 1.0, and
the same for xdy and ydy. This is intended to report if a matrix
is aligned to the axes.
(It will also return true for something like xdx=1, xdy=0, ydx=1, xdy=1,
but that should never happen.)
"""
cdef bint unit_xdx = .99999 < abs(self.xdx) < 1.00001
cdef bint unit_xdy = .99999 < abs(self.xdy) < 1.00001
cdef bint unit_ydx = .99999 < abs(self.ydx) < 1.00001
cdef bint unit_ydy = .99999 < abs(self.ydy) < 1.00001
return (unit_xdx ^ unit_xdy) and (unit_ydx ^ unit_ydy)
IDENTITY = Matrix2D(1, 0, 0, 1)
@@ -630,6 +574,7 @@ BLIT = 0
DISSOLVE = 1
IMAGEDISSOLVE = 2
PIXELLATE = 3
FLATTEN = 4
cdef class Render:
@@ -649,7 +594,6 @@ cdef class Render:
# Is has this render been removed from the cache?
self.cache_killed = False
self.width = width
self.height = height
@@ -659,25 +603,6 @@ cdef class Render:
# back to front.
self.children = [ ]
# The set of renders that either have us as children, or depend on
# us.
self.parents = set()
# The renders we depend on, including our children.
self.depends_on_list = [ ]
# The operation we're performing. (BLIT, DISSOLVE, OR IMAGE_DISSOLVE)
self.operation = BLIT
# The fraction of the operation that is complete.
self.operation_complete = 0.0
# Should the dissolve operations preserve alpha?
self.operation_alpha = False
# The parameter to the operation.
self.operation_parameter = 0
# Forward is used to transform from screen coordinates to child
# coordinates.
# Reverse is used to transform from child coordinates to screen
@@ -730,6 +655,33 @@ cdef class Render:
self.xclipping = False
self.yclipping = False
# Are we modal?
self.modal = False
# Are we a text input?
self.text_input = False
# gl, sw
# The set of renders that either have us as children, or depend on
# us.
self.parents = set()
# The renders we depend on, including our children.
self.depends_on_list = [ ]
# The operation we're performing. (BLIT, DISSOLVE, OR IMAGE_DISSOLVE)
self.operation = BLIT
# The fraction of the operation that is complete.
self.operation_complete = 0.0
# Should the dissolve operations preserve alpha?
self.operation_alpha = False
# The parameter to the operation.
self.operation_parameter = 0
# Caches of the texture created by rendering this surface.
self.surface = None
self.alpha_surface = None
@@ -738,11 +690,31 @@ cdef class Render:
# (This is set in gldraw.)
self.half_cache = None
# Are we modal?
self.modal = False
# gl2
# Are we a text input?
self.text_input = False
# The mesh. If this is not None, the children are all rendered to Textures,
# and used to form a model. If this is True, the Mesh is taken from the first
# child's Texture, otherwise this must be a Mesh.
self.mesh = None
# A tuple of shaders that will be used when rendering, or None.
self.shaders = None
# A dictionary containing uniforms that will be used when rendering, or
# None.
self.uniforms = None
# Properties that are used for rendering.
self.properties = None
# Used to cache the result of rendering this Render to a texture.
self.cached_texture = None
# Used to cache the model.
self.cached_model = None
# Have the textures been loaded?
self.loaded = False
live_renders.append(self)
@@ -1410,7 +1382,7 @@ cdef class Render:
return Canvas(surf)
def screen_rect(self, double sx, double sy, Matrix2D transform):
def screen_rect(self, double sx, double sy, Matrix transform):
"""
Returns the rectangle, in screen-space coordinates, that will be covered
by this render when it's drawn to the screen at sx, sy, with the transform
+26 -11
View File
@@ -680,7 +680,11 @@ class ScreenDisplayable(renpy.display.layout.Container):
rv.focus_screen = self
hiding = (self.phase == OLD) or (self.phase == HIDE)
sensitive = renpy.python.py_eval(self.screen.sensitive, locals=self.scope)
if self.screen is None:
sensitive = False
else:
sensitive = renpy.python.py_eval(self.screen.sensitive, locals=self.scope)
rv.blit(child, (0, 0), focus=sensitive and not hiding, main=not hiding)
rv.modal = self.modal and not hiding
@@ -698,6 +702,9 @@ class ScreenDisplayable(renpy.display.layout.Container):
if (self.phase == OLD) or (self.phase == HIDE):
return
if not self.screen:
return None
if not renpy.python.py_eval(self.screen.sensitive, locals=self.scope):
ev = renpy.display.interface.time_event
@@ -908,17 +915,25 @@ def prepare_screens():
predict_cache.clear()
if not analyzed:
analyze_screens()
old_predicting = renpy.display.predict.predicting
renpy.display.predict.predicting = True
for s in sorted_variants():
if s.ast is None:
continue
try:
s.ast.unprepare_screen()
s.ast.prepare_screen()
if not analyzed:
analyze_screens()
prepared = True
for s in sorted_variants():
if s.ast is None:
continue
s.ast.unprepare_screen()
s.ast.prepare_screen()
prepared = True
finally:
renpy.display.predict.predicting = old_predicting
if renpy.config.developer and use_cycle:
raise Exception("The following screens use each other in a loop: " + ", ".join(use_cycle) +". This is not allowed.")
@@ -1071,8 +1086,8 @@ def show_screen(_screen_name, *_args, **kwargs):
If true, the screen will be automatically hidden at the end of
the current interaction.
Keyword arguments not beginning with underscore (_) are used to
initialize the screen's scope.
Non-keyword arguments, and keyword arguments that do not begin with
an underscore, are passed to the screen.
"""
_layer = kwargs.pop("_layer", None)
+9 -14
View File
@@ -28,7 +28,7 @@ import weakref
import time
import os
from renpy.display.render import blit_lock, IDENTITY, BLIT, DISSOLVE, IMAGEDISSOLVE, PIXELLATE
from renpy.display.render import blit_lock, IDENTITY, BLIT, DISSOLVE, IMAGEDISSOLVE, PIXELLATE, FLATTEN
# A map from cached surface to rle version of cached surface.
rle_cache = weakref.WeakKeyDictionary()
@@ -338,6 +338,10 @@ def draw_special(what, dest, x, y):
dest.subsurface((0, 0, w, h)),
px, px, px, px)
elif what.operation == FLATTEN:
surf = what.children[0][0].render_to_texture(dest.get_masks()[3])
dest.subsurface((0, 0, w, h)).blit(surf, (0, 0))
else:
raise Exception("Unknown operation: %d" % what.operation)
@@ -635,8 +639,8 @@ def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
cxo, cyo = reverse.transform(cxo, cyo)
if what.forward:
child_forward = forward * what.forward
child_reverse = what.reverse * reverse
child_forward = what.forward * forward
child_reverse = reverse * what.reverse
else:
child_forward = forward
child_reverse = reverse
@@ -1108,22 +1112,13 @@ class SWDraw(object):
self.mutated_surface(surf)
return surf
def free_memory(self):
def kill_textures(self):
"""
Frees up memory.
Kills all textures and caches of textures.
"""
rle_cache.clear()
def deinit(self):
"""
Called when we're restarted.
"""
renpy.display.render.free_memory()
return
def quit(self): # @ReservedAssignment
"""
Shuts down the drawing system.
+39
View File
@@ -369,6 +369,19 @@ class Dissolve(Transition):
rv.operation_alpha = self.alpha or renpy.config.dissolve_force_alpha
rv.operation_complete = complete
if renpy.display.render.models:
target = rv.get_size()
if top.get_size() != target:
top = top.subsurface((0, 0, width, height))
if bottom.get_size() != target:
bottom = bottom.subsurface((0, 0, width, height))
rv.mesh = True
rv.shaders = ( "renpy.dissolve", )
rv.uniforms = { "uDissolve" : complete }
rv.blit(bottom, (0, 0), focus=False, main=False)
rv.blit(top, (0, 0), focus=True, main=True)
@@ -509,6 +522,32 @@ class ImageDissolve(Transition):
rv.operation_complete = complete
rv.operation_parameter = self.ramplen
if renpy.display.render.models:
target = rv.get_size()
if image.get_size() != target:
image = image.subsurface((0, 0, width, height))
if top.get_size() != target:
top = top.subsurface((0, 0, width, height))
if bottom.get_size() != target:
bottom = bottom.subsurface((0, 0, width, height))
ramp = self.ramplen
# Prevent a DBZ if the user gives us a 0 ramp.
if ramp < 1:
ramp = 1
# Compute the offset to apply to the alpha.
start = -1.0
end = ramp / 256.0
offset = start + ( end - start) * complete
rv.mesh = True
rv.shaders = ( "renpy.imagedissolve", )
rv.uniforms = { "uDissolveOffset" : offset, "uDissolveMultiplier" : 256.0 / ramp }
rv.blit(image, (0, 0), focus=False, main=False)
rv.blit(bottom, (0, 0), focus=False, main=False)
rv.blit(top, (0, 0), focus=True, main=True)
+30 -9
View File
@@ -278,7 +278,7 @@ class Viewport(renpy.display.layout.Container):
return rv
def check_edge_redraw(self, st):
def check_edge_redraw(self, st, reset_st=True):
redraw = False
if (self.edge_xspeed > 0) and (self.xadjustment.value < self.xadjustment.range):
@@ -293,7 +293,8 @@ class Viewport(renpy.display.layout.Container):
if redraw:
renpy.display.render.redraw(self, 0)
self.edge_last_st = st
if reset_st or self.edge_last_st is None:
self.edge_last_st = st
else:
self.edge_last_st = None
@@ -309,18 +310,38 @@ class Viewport(renpy.display.layout.Container):
if self.draggable and renpy.display.focus.get_grab() == self:
old_xvalue = self.xadjustment.value
old_yvalue = self.yadjustment.value
if renpy.display.behavior.map_event(ev, 'viewport_drag_end'):
renpy.display.focus.set_grab(None)
# Invoke rounding adjustment on viewport release
xvalue = self.xadjustment.round_value(old_xvalue, release=True)
self.xadjustment.change(xvalue)
yvalue = self.yadjustment.round_value(old_yvalue, release=True)
self.yadjustment.change(yvalue)
raise renpy.display.core.IgnoreEvent()
oldx, oldy = self.drag_position
dx = x - oldx
dy = y - oldy
self.xadjustment.change(self.xadjustment.value - dx)
self.yadjustment.change(self.yadjustment.value - dy)
new_xvalue = self.xadjustment.round_value(old_xvalue - dx, release=False)
if old_xvalue == new_xvalue:
newx = oldx
else:
self.xadjustment.change(new_xvalue)
newx = x
self.drag_position = (x, y) # W0201
new_yvalue = self.yadjustment.round_value(old_yvalue - dy, release=False)
if old_yvalue == new_yvalue:
newy = oldy
else:
self.yadjustment.change(new_yvalue)
newy = y
if renpy.display.behavior.map_event(ev, 'viewport_drag_end'):
renpy.display.focus.set_grab(None)
raise renpy.display.core.IgnoreEvent()
self.drag_position = (newx, newy) # W0201
if not ((0 <= x < self.width) and (0 <= y <= self.height)):
self.edge_xspeed = 0
@@ -474,7 +495,7 @@ class Viewport(renpy.display.layout.Container):
self.edge_yspeed = self.edge_speed * self.edge_function(yspeed)
if xspeed or yspeed:
self.check_edge_redraw(st)
self.check_edge_redraw(st, reset_st=False)
else:
self.edge_last_st = None
+12 -3
View File
@@ -147,6 +147,8 @@ class Context(renpy.object.Object):
temporary_attributes = None
deferred_translate_identifier = None
def __repr__(self):
if not self.current:
@@ -277,7 +279,7 @@ class Context(renpy.object.Object):
# executed?
self.force_checkpoint = False
# A mapt from a channel to the Movie playing on that channel.
# A map from a channel to the Movie playing on that channel.
self.movie = { }
if context:
@@ -299,6 +301,9 @@ class Context(renpy.object.Object):
self.scene_lists = renpy.display.core.SceneLists(oldsl, self.images)
for i in renpy.config.context_copy_remove_screens:
self.scene_lists.remove("screens", i, None)
self.make_dynamic([ "_return", "_args", "_kwargs", "mouse_visible", "suppress_overlay", "_side_image_attributes" ])
self.dynamic_stack.append({ })
@@ -319,6 +324,10 @@ class Context(renpy.object.Object):
# The alternate identifier of the current translate block.
self.alternate_translate_identifier = None
# The translate identifier of the last say statement with
# interact = False.
self.deferred_translate_identifier = None
def replace_node(self, old, new):
def replace_one(name):
@@ -369,7 +378,7 @@ class Context(renpy.object.Object):
for k, v in dynamic.iteritems():
if isinstance(v, Delete):
del store[k]
store.pop(k, None)
else:
store[k] = v
@@ -592,7 +601,7 @@ class Context(renpy.object.Object):
except renpy.game.CallException as e:
if e.from_current:
return_site = node.name
return_site = getattr(node, "statement_start", node).name
else:
if self.next_node is None:
raise Exception("renpy.call can't be used when the next node is undefined.")
+30 -11
View File
@@ -91,7 +91,6 @@ from renpy.parser import unelide_filename, get_parse_errors
from renpy.translation import change_language, known_languages
from renpy.translation.generation import generic_filter as transform_text
from renpy.persistent import register_persistent
from renpy.character import show_display_say, predict_show_display_say, display_say
@@ -116,6 +115,8 @@ from renpy.loader import add_python_directory
from renpy.lint import try_compile, try_eval
from renpy.gl2.gl2shadercache import register_shader
renpy_pure("ParameterizedText")
renpy_pure("Keymap")
renpy_pure("has_screen")
@@ -469,7 +470,7 @@ def can_show(name, layer=None, tag=None):
return None
def showing(name, layer='master'):
def showing(name, layer=None):
"""
:doc: image_func
@@ -481,7 +482,6 @@ def showing(name, layer='master'):
component of the image name. It may also be a string giving
only the image tag.
`layer`
The layer to check. If None, uses the default layer for `tag`.
"""
@@ -780,7 +780,7 @@ def scene(layer='master'):
renpy.config.missing_scene(layer)
def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None, pixel_width=None): # @ReservedAssignment
def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None, pixel_width=None, screen="input"): # @ReservedAssignment
"""
:doc: input
@@ -809,6 +809,10 @@ def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=N
If not None, the input is limited to being this many pixels wide,
in the font used by the input to display text.
`screen`
The name of the screen that takes input. If not given, the ``input``
screen is used.
If :var:`config.disable_input` is True, this function only returns
`default`.
"""
@@ -828,14 +832,17 @@ def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=N
fixed = in_fixed_rollback()
if has_screen("input"):
if has_screen(screen):
widget_properties = { }
widget_properties["input"] = dict(default=default, length=length, allow=allow, exclude=exclude, editable=not fixed, pixel_width=pixel_width)
show_screen("input", _transient=True, _widget_properties=widget_properties, prompt=prompt)
show_screen(screen, _transient=True, _widget_properties=widget_properties, prompt=prompt)
else:
if screen != "input":
raise Exception("The '{}' screen does not exist.".format(screen))
renpy.ui.window(style='input_window')
renpy.ui.vbox()
@@ -1188,6 +1195,8 @@ def display_menu(items,
**scope)
else:
renpy.exports.shown_window()
renpy.ui.window(style=window_style, focus="menu")
renpy.ui.menu(items,
location=renpy.game.context().current,
@@ -2670,7 +2679,7 @@ def get_placement(d):
return placement(p)
def get_image_bounds(tag, width=None, height=None, layer='master'):
def get_image_bounds(tag, width=None, height=None, layer=None):
"""
:doc: image_func
@@ -2868,8 +2877,8 @@ def call_screen(_screen_name, *args, **kwargs):
to occur. The screen is hidden at the end of the interaction, and
the result of the interaction is returned.
Keyword arguments not beginning with _ are passed to the scope of
the screen.
Positional arguments, and keyword arguments that do not begin with
_ are passed to the screen.
If the keyword argument `_with_none` is false, "with None" is not
run at the end of end of the interaction.
@@ -3715,8 +3724,8 @@ def get_skipping():
"""
:doc: other
Returns true if the Ren'Py is skipping, "fast" if Ren'Py is fast skipping,
and false if it is not skipping.
Returns "slow" if the Ren'Py is skipping, "fast" if Ren'Py is fast skipping,
and None if it is not skipping.
"""
return renpy.config.skipping
@@ -3783,3 +3792,13 @@ def is_skipping():
"""
return not not renpy.config.skipping
def is_init_phase():
"""
:doc: other
Returns True if Ren'Py is currently executing init code, or False otherwise.
"""
return renpy.game.context().init_phase
+6
View File
@@ -1526,16 +1526,19 @@ cdef extern from "glcompat.h" nogil:
cdef int glActiveTextureARB(GLenum) except? 0
cdef int glAttachObjectARB(GLhandleARB, GLhandleARB) except? 0
cdef int glBindFramebufferEXT(GLenum, GLuint) except? 0
cdef int glBindRenderbufferEXT(GLenum, GLuint) except? 0
cdef int glBindTexture(GLenum, GLuint) except? 0
cdef int glBlendFunc(GLenum, GLenum) except? 0
cdef int glClear(GLbitfield) except? 0
cdef int glClearColor(GLclampf, GLclampf, GLclampf, GLclampf) except? 0
cdef int glCompileShaderARB(GLhandleARB) except? 0
cdef int glCopyTexImage2D(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint) except? 0
cdef int glCopyTexSubImage2D(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei) except? 0
cdef GLhandleARB glCreateProgramObjectARB() except? 0
cdef GLhandleARB glCreateShaderObjectARB(GLenum) except? 0
cdef int glDeleteFramebuffersEXT(GLsizei, GLuint *) except? 0
cdef int glDeleteProgram(GLuint) except? 0
cdef int glDeleteRenderbuffersEXT(GLsizei, GLuint *) except? 0
cdef int glDeleteShader(GLuint) except? 0
cdef int glDeleteTextures(GLsizei, GLuint *) except? 0
cdef int glDisable(GLenum) except? 0
@@ -1544,8 +1547,10 @@ cdef int glDrawArrays(GLenum, GLint, GLsizei) except? 0
cdef int glEnable(GLenum) except? 0
cdef int glEnableVertexAttribArrayARB(GLuint) except? 0
cdef int glFinish() except? 0
cdef int glFramebufferRenderbufferEXT(GLenum, GLenum, GLenum, GLuint) except? 0
cdef int glFramebufferTexture2DEXT(GLenum, GLenum, GLenum, GLuint, GLint) except? 0
cdef int glGenFramebuffersEXT(GLsizei, GLuint *) except? 0
cdef int glGenRenderbuffersEXT(GLsizei, GLuint *) except? 0
cdef int glGenTextures(GLsizei, GLuint *) except? 0
cdef GLint glGetAttribLocationARB(GLhandleARB, GLchar *) except? 0
cdef int glGetIntegerv(GLenum, GLint *) except? 0
@@ -1557,6 +1562,7 @@ cdef GLchar * glGetString(GLenum) except? NULL
cdef GLint glGetUniformLocationARB(GLhandleARB, GLchar *) except? 0
cdef int glLinkProgramARB(GLhandleARB) except? 0
cdef int glReadPixels(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLubyte *) except? 0
cdef int glRenderbufferStorageEXT(GLenum, GLenum, GLsizei, GLsizei) except? 0
cdef int glScissor(GLint, GLint, GLsizei, GLsizei) except? 0
cdef int glShaderSourceARB(GLhandleARB, GLsizei, GLchar * *, GLint *) except? 0
cdef int glTexImage2D(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, GLubyte *) except? 0
+90 -2
View File
@@ -1,5 +1,3 @@
from __future__ import print_function
import os
import renpy
@@ -52,6 +50,21 @@ cdef int glBindFramebufferEXT(GLenum a0, GLuint a1) except? 0:
raise Exception(message)
return 1
cdef int glBindRenderbufferEXT(GLenum a0, GLuint a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glBindRenderbufferEXT')
cdef GLenum error
realGlBindRenderbufferEXT(a0, a1)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glBindRenderbufferEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glBindTexture(GLenum a0, GLuint a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glBindTexture')
@@ -128,6 +141,21 @@ cdef int glCompileShaderARB(GLhandleARB a0) except? 0:
raise Exception(message)
return 1
cdef int glCopyTexImage2D(GLenum a0, GLint a1, GLenum a2, GLint a3, GLint a4, GLsizei a5, GLsizei a6, GLint a7) except? 0:
if check_errors & 4:
renpy.display.log.write('glCopyTexImage2D')
cdef GLenum error
realGlCopyTexImage2D(a0, a1, a2, a3, a4, a5, a6, a7)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glCopyTexImage2D' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glCopyTexSubImage2D(GLenum a0, GLint a1, GLint a2, GLint a3, GLint a4, GLint a5, GLsizei a6, GLsizei a7) except? 0:
if check_errors & 4:
renpy.display.log.write('glCopyTexSubImage2D')
@@ -203,6 +231,21 @@ cdef int glDeleteProgram(GLuint a0) except? 0:
raise Exception(message)
return 1
cdef int glDeleteRenderbuffersEXT(GLsizei a0, GLuint * a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glDeleteRenderbuffersEXT')
cdef GLenum error
realGlDeleteRenderbuffersEXT(a0, a1)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glDeleteRenderbuffersEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glDeleteShader(GLuint a0) except? 0:
if check_errors & 4:
renpy.display.log.write('glDeleteShader')
@@ -323,6 +366,21 @@ cdef int glFinish() except? 0:
raise Exception(message)
return 1
cdef int glFramebufferRenderbufferEXT(GLenum a0, GLenum a1, GLenum a2, GLuint a3) except? 0:
if check_errors & 4:
renpy.display.log.write('glFramebufferRenderbufferEXT')
cdef GLenum error
realGlFramebufferRenderbufferEXT(a0, a1, a2, a3)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glFramebufferRenderbufferEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glFramebufferTexture2DEXT(GLenum a0, GLenum a1, GLenum a2, GLuint a3, GLint a4) except? 0:
if check_errors & 4:
renpy.display.log.write('glFramebufferTexture2DEXT')
@@ -353,6 +411,21 @@ cdef int glGenFramebuffersEXT(GLsizei a0, GLuint * a1) except? 0:
raise Exception(message)
return 1
cdef int glGenRenderbuffersEXT(GLsizei a0, GLuint * a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glGenRenderbuffersEXT')
cdef GLenum error
realGlGenRenderbuffersEXT(a0, a1)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glGenRenderbuffersEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glGenTextures(GLsizei a0, GLuint * a1) except? 0:
if check_errors & 4:
renpy.display.log.write('glGenTextures')
@@ -518,6 +591,21 @@ cdef int glReadPixels(GLint a0, GLint a1, GLsizei a2, GLsizei a3, GLenum a4, GLe
raise Exception(message)
return 1
cdef int glRenderbufferStorageEXT(GLenum a0, GLenum a1, GLsizei a2, GLsizei a3) except? 0:
if check_errors & 4:
renpy.display.log.write('glRenderbufferStorageEXT')
cdef GLenum error
realGlRenderbufferStorageEXT(a0, a1, a2, a3)
if check_errors:
error = realGlGetError()
if error:
message = 'GL error %x in glRenderbufferStorageEXT' % error
if check_errors & 1:
renpy.display.log.write('%s', message)
if check_errors & 2:
raise Exception(message)
return 1
cdef int glScissor(GLint a0, GLint a1, GLsizei a2, GLsizei a3) except? 0:
if check_errors & 4:
renpy.display.log.write('glScissor')
+4 -3
View File
@@ -19,6 +19,7 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from renpy.display.matrix cimport Matrix, Matrix2D
cimport renpy.display.render as render
cdef class Environ
@@ -58,8 +59,8 @@ cdef class GLDraw:
cdef public object draw_per_virt
# Matrices that transform drawable to virtual, and vice versa.
cdef public render.Matrix2D virt_to_draw
cdef public render.Matrix2D draw_to_virt
cdef public Matrix virt_to_draw
cdef public Matrix draw_to_virt
cdef public int fast_redraw_frames
@@ -78,7 +79,7 @@ cdef class GLDraw:
double yo,
double alpha,
double over,
render.Matrix2D reverse,
Matrix reverse,
bint nearest,
bint subpixel) except 1
+34 -21
View File
@@ -65,10 +65,11 @@ cdef bint EGL
EGL = egl_available()
# Cache various externals, so we can use them more efficiently.
cdef int DISSOLVE, IMAGEDISSOLVE, PIXELLATE
cdef int DISSOLVE, IMAGEDISSOLVE, PIXELLATE, FLATTEN
DISSOLVE = renpy.display.render.DISSOLVE
IMAGEDISSOLVE = renpy.display.render.IMAGEDISSOLVE
PIXELLATE = renpy.display.render.PIXELLATE
FLATTEN = renpy.display.render.FLATTEN
cdef object IDENTITY
IDENTITY = renpy.display.render.IDENTITY
@@ -181,11 +182,15 @@ cdef class GLDraw:
return False
if self.did_init:
self.deinit()
self.kill_textures()
if renpy.android:
fullscreen = False
# Refresh fullscreen status (e.g. user pressed Esc. in browser)
main_window = pygame.display.get_window()
self.old_fullscreen = main_window is not None and bool(main_window.get_window_flags() & (pygame.WINDOW_FULLSCREEN_DESKTOP|pygame.WINDOW_FULLSCREEN))
if fullscreen != self.old_fullscreen:
self.did_init = False
@@ -414,8 +419,8 @@ cdef class GLDraw:
else:
self.draw_per_virt = 1.0
self.virt_to_draw = render.Matrix2D(self.draw_per_virt, 0, 0, self.draw_per_virt)
self.draw_to_virt = render.Matrix2D(1.0 / self.draw_per_virt, 0, 0, 1.0 / self.draw_per_virt)
self.virt_to_draw = Matrix2D(self.draw_per_virt, 0, 0, self.draw_per_virt)
self.draw_to_virt = Matrix2D(1.0 / self.draw_per_virt, 0, 0, 1.0 / self.draw_per_virt)
if not self.did_init:
if not self.init():
@@ -446,17 +451,12 @@ cdef class GLDraw:
return True
def deinit(self):
def quit(self):
"""
De-initializes the system in preparation for a restart, or
quit. Flushes out all the textures while it's at it.
This shuts down the module and all use of the GL context.
"""
renpy.display.interface.kill_textures()
self.texture_cache.clear()
gltexture.dealloc_textures()
self.kill_textures()
if self.rtt:
self.rtt.deinit()
@@ -464,8 +464,6 @@ cdef class GLDraw:
if self.environ:
self.environ.deinit()
def quit(self):
if not self.old_fullscreen:
renpy.display.gl_size = self.physical_size
@@ -943,13 +941,13 @@ cdef class GLDraw:
double yo,
double alpha,
double over,
render.Matrix2D reverse,
Matrix reverse,
bint nearest,
bint subpixel) except 1:
cdef render.Render rend
cdef double cxo, cyo, tcxo, tcyo
cdef render.Matrix2D child_reverse
cdef Matrix child_reverse
if not isinstance(what, render.Render):
@@ -1039,7 +1037,7 @@ cdef class GLDraw:
return 0
if rend.operation == PIXELLATE:
elif rend.operation == PIXELLATE:
self.set_clip(clip)
p = rend.operation_parameter
@@ -1049,7 +1047,7 @@ cdef class GLDraw:
p /= 2
pc = self.get_half(pc)
reverse *= renpy.display.render.Matrix2D(1.0 * what.width / pc.width, 0, 0, 1.0 * what.height / pc.height)
reverse *= Matrix2D(1.0 * what.width / pc.width, 0, 0, 1.0 * what.height / pc.height)
gltexture.blit(
pc,
@@ -1063,6 +1061,21 @@ cdef class GLDraw:
return 0
elif rend.operation == FLATTEN:
self.set_clip(clip)
gltexture.blit(
rend.children[0][0].render_to_texture(True),
xo,
yo,
reverse * self.draw_to_virt,
alpha,
over,
self.environ,
nearest)
return 0
# Compute clipping.
if rend.xclipping or rend.yclipping:
@@ -1100,7 +1113,7 @@ cdef class GLDraw:
return 0
if rend.reverse is not None and rend.reverse is not IDENTITY:
child_reverse = rend.reverse * reverse
child_reverse = reverse * rend.reverse
else:
child_reverse = reverse
@@ -1209,7 +1222,7 @@ cdef class GLDraw:
if what.half_cache:
return what.half_cache
reverse = renpy.display.render.Matrix2D(0.5, 0, 0, .5)
reverse = Matrix2D(0.5, 0, 0, .5)
width = max(what.width / 2, 1)
height = max(what.height / 2, 1)
@@ -1399,7 +1412,7 @@ cdef class GLDraw:
return rv
def free_memory(self):
def kill_textures(self):
self.texture_cache.clear()
gltexture.dealloc_textures()
+16 -42
View File
@@ -31,11 +31,17 @@ cdef int round(double d):
return <int> (d + .5)
VERTEX_SHADER1 = """\
GLES_PORTABILITY = """\
#ifdef GL_ES
precision highp float;
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
# else
precision mediump float;
# endif
#endif
"""
VERTEX_SHADER1 = GLES_PORTABILITY + """\
uniform mat4 Projection;
attribute vec4 Vertex;
@@ -53,11 +59,7 @@ void main() {
}
"""
VERTEX_SHADER2 = """\
#ifdef GL_ES
precision highp float;
#endif
VERTEX_SHADER2 = GLES_PORTABILITY + """\
uniform mat4 Projection;
attribute vec4 Vertex;
@@ -78,11 +80,7 @@ void main() {
}
"""
VERTEX_SHADER3 = """\
#ifdef GL_ES
precision highp float;
#endif
VERTEX_SHADER3 = GLES_PORTABILITY + """\
uniform mat4 Projection;
attribute vec4 Vertex;
@@ -109,11 +107,7 @@ void main() {
BLIT_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
BLIT_SHADER = GLES_PORTABILITY + """\
uniform vec4 Color;
uniform sampler2D tex0;
@@ -130,11 +124,7 @@ void main()
}
"""
BLIT_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
BLIT_CLIP_SHADER = GLES_PORTABILITY + """\
uniform vec4 Color;
uniform sampler2D tex0;
@@ -155,11 +145,7 @@ void main()
}
"""
BLEND_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
BLEND_SHADER = GLES_PORTABILITY + """\
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
@@ -182,11 +168,7 @@ void main()
"""
BLEND_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
BLEND_CLIP_SHADER = GLES_PORTABILITY + """\
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
@@ -212,11 +194,7 @@ void main()
}
"""
IMAGEBLEND_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
IMAGEBLEND_SHADER = GLES_PORTABILITY + """\
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
@@ -244,11 +222,7 @@ void main()
}
"""
IMAGEBLEND_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
IMAGEBLEND_CLIP_SHADER = GLES_PORTABILITY + """\
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
+27 -11
View File
@@ -35,22 +35,41 @@ cdef GLuint fbo
# The root framebuffer.
cdef GLint root_fbo
# The renderbuffer object we use.
cdef GLuint texture
class FboRtt(Rtt):
"""
This class uses texture copying to implement Render-to-texture.
"""
def init(self):
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &root_fbo);
renpy.display.log.write("Root FBO is: %d", root_fbo)
glGenFramebuffersEXT(1, &fbo)
glGenTextures(1, &texture)
cdef int i
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &i)
self.size_limit = i
renpy.display.log.write("FBO Maximum Texture Size: %d", i)
self.size_limit = min(i, 2048)
renpy.display.log.write("FBO Maximum Texture Size: %d", self.size_limit)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.size_limit, self.size_limit, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D,
texture,
0)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, root_fbo)
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &root_fbo);
renpy.display.log.write("Root FBO is: %d", root_fbo)
def deinit(self):
"""
@@ -59,6 +78,7 @@ class FboRtt(Rtt):
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, root_fbo)
glDeleteFramebuffersEXT(1, &fbo)
glDeleteTextures(1, &texture)
def begin(self):
"""
@@ -77,18 +97,14 @@ class FboRtt(Rtt):
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D,
texture,
0)
environ.viewport(0, 0, w, h)
environ.ortho(x, x + w, y, y + h, -1, 1)
draw_func(x, y, w, h)
glBindTexture(GL_TEXTURE_2D, texture)
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, w, h, 0)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, root_fbo)
+4 -3
View File
@@ -19,6 +19,7 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from renpy.display.matrix cimport Matrix
cimport renpy.display.render as render
from gldraw cimport Environ
@@ -70,7 +71,7 @@ cpdef blit(
TextureGrid tg,
double sx,
double sy,
render.Matrix2D transform,
Matrix transform,
double alpha,
double over,
Environ environ,
@@ -81,7 +82,7 @@ cpdef blend(
TextureGrid tg1,
double sx,
double sy,
render.Matrix2D transform,
Matrix transform,
double alpha,
double over,
double fraction,
@@ -94,7 +95,7 @@ cpdef imageblend(
TextureGrid tg2,
double sx,
double sy,
render.Matrix2D transform,
Matrix transform,
double alpha,
double over,
double fraction,
+4 -17
View File
@@ -269,25 +269,12 @@ cdef class TextureCore:
self.generation = 0
self.number = 0
# The format of this texture in the GPU (or 0 if not known).
self.format = 0
# These are used to map an index into texture coordinates.
self.xmul = 0
self.xadd = 0
self.ymul = 0
self.yadd = 0
# These contained the premultiplied (but not GPU-loaded)
# surface. They allow us to defer loading until the surface is
# needed.
self.premult = None
self.premult_size = None
self.premult_left = 0
self.premult_right = 0
self.premult_top = 0
self.premult_bottom = 0
# True if we're in NEAREST mode. False if we're in LINEAR mode.
self.nearest = False
@@ -992,7 +979,7 @@ def align_axes(*args):
return rv
cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, double alpha, double over, Environ environ, bint nearest):
cpdef blit(TextureGrid tg, double sx, double sy, Matrix transform, double alpha, double over, Environ environ, bint nearest):
"""
This draws texgrid `tg` to the screen. `sx` and `sy` are offsets from
the upper-left corner of the screen.
@@ -1037,7 +1024,7 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
y += texh
cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matrix2D transform, double alpha, double over, double fraction, Environ environ, bint nearest):
cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, Matrix transform, double alpha, double over, double fraction, Environ environ, bint nearest):
"""
Blends two textures to the screen.
@@ -1094,7 +1081,7 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
y += t0h
cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, double sy, render.Matrix2D transform, double alpha, double over, double fraction, int ramp, Environ environ, bint nearest):
cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, double sy, Matrix transform, double alpha, double over, double fraction, int ramp, Environ environ, bint nearest):
"""
This uses texture 0 to control the blending of tetures 1 and 2 to
the screen.
@@ -1384,7 +1371,7 @@ cdef void draw_rectangle(
double y,
double w,
double h,
render.Matrix2D transform,
Matrix transform,
TextureCore tex0, float tex0x, float tex0y,
TextureCore tex1, float tex1x, float tex1y,
TextureCore tex2, float tex2x, float tex2y,
+24 -41
View File
@@ -19,16 +19,14 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from renpy.display.matrix cimport Matrix, Matrix2D
cimport renpy.display.render as render
cdef class Environ
from renpy.gl2.gl2texture cimport TextureLoader
from uguugl cimport *
cdef class GL2Draw:
cdef bint did_init
cdef bint did_texture_test
cdef Environ environ
cdef public object rtt
cdef object window
cdef tuple virtual_size
cdef public tuple physical_size
@@ -45,53 +43,38 @@ cdef class GL2Draw:
cdef public object fullscreen_surface
cdef object display_info
cdef tuple clip_cache
cdef bint fast_dissolve
cdef tuple default_clip
cdef bint did_render_to_texture
cdef float dpi_scale
cdef object ready_texture_queue
cdef object shader_cache
cdef public tuple clip_rtt_box
# The number of drawable pixels per virtual pixel.
cdef public object draw_per_virt
cdef public float draw_per_phys
cdef public tuple drawable_viewport
# Matrices that transform drawable to virtual, and vice versa.
cdef public render.Matrix2D virt_to_draw
cdef public render.Matrix2D draw_to_virt
cdef public object draw_per_virt
cdef public Matrix virt_to_draw
cdef public Matrix draw_to_virt
cdef public int fast_redraw_frames
cdef public bint gles
cpdef set_clip(GL2Draw self, tuple clip)
# The color texture object used for offscreen rendering.
cdef GLuint color_texture
cpdef int draw_render_textures(
GL2Draw self,
object what,
bint non_aligned) except 1
# The depth renderbuffer object used for offscreen rendering.
cdef GLuint depth_renderbuffer
cpdef int draw_transformed(
GL2Draw self,
object what,
tuple clip,
double xo,
double yo,
double alpha,
double over,
render.Matrix2D reverse,
bint nearest,
bint subpixel) except 1
# The framebuffer object used for offscreen rendering.
cdef GLuint fbo
cdef class Environ:
cdef void blit(self)
cdef void blend(self, double fraction)
cdef void imageblend(self, double fraction, int ramp)
cdef void set_vertex(self, float *vertices)
cdef void set_texture(self, int unit, float *coords)
cdef void set_color(self, double r, double g, double b, double a)
cdef void set_clip(self, tuple clip_box, GL2Draw draw)
cdef void unset_clip(self, GL2Draw draw)
cdef void ortho(self, double left, double right, double bottom, double top, double near, double far)
cdef void viewport(self, int x, int y, int width, int height)
# The texture_loader singleton.
cdef public TextureLoader texture_loader
# The default FBO.
cdef public GLuint default_fbo
# The current FBO.
cdef public GLuint current_fbo
cdef void change_fbo(self, GLuint fbo)
+556 -700
View File
File diff suppressed because it is too large Load Diff
-696
View File
@@ -1,696 +0,0 @@
#cython: profile=False
# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function
DEF ANGLE = False
from uguugl cimport *
from gl2draw cimport *
cdef int round(double d):
return <int> (d + .5)
VERTEX_SHADER1 = """\
#ifdef GL_ES
precision highp float;
#endif
uniform mat4 Projection;
attribute vec4 Vertex;
attribute vec2 VertexTexCoord0;
varying vec2 TexCoord0;
varying vec2 pos;
void main() {
TexCoord0 = VertexTexCoord0;
pos = Vertex.xy;
gl_Position = Projection * Vertex;
}
"""
VERTEX_SHADER2 = """\
#ifdef GL_ES
precision highp float;
#endif
uniform mat4 Projection;
attribute vec4 Vertex;
attribute vec2 VertexTexCoord0;
attribute vec2 VertexTexCoord1;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 pos;
void main() {
TexCoord0 = VertexTexCoord0;
TexCoord1 = VertexTexCoord1;
pos = Vertex.xy;
gl_Position = Projection * Vertex;
}
"""
VERTEX_SHADER3 = """\
#ifdef GL_ES
precision highp float;
#endif
uniform mat4 Projection;
attribute vec4 Vertex;
attribute vec2 VertexTexCoord0;
attribute vec2 VertexTexCoord1;
attribute vec2 VertexTexCoord2;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 TexCoord2;
varying vec2 pos;
void main() {
TexCoord0 = VertexTexCoord0;
TexCoord1 = VertexTexCoord1;
TexCoord2 = VertexTexCoord2;
pos = Vertex.xy;
gl_Position = Projection * Vertex;
}
"""
BLIT_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
varying vec2 TexCoord0;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
vec4 color0 = texture2D(tex0, TexCoord0.st);
gl_FragColor = color0 * Color;
}
"""
BLIT_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
varying vec2 TexCoord0;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
discard;
}
vec4 color0 = texture2D(tex0, TexCoord0.st);
gl_FragColor = color0 * Color;
}
"""
BLEND_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform float done;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
vec4 color0 = texture2D(tex0, TexCoord0.st);
vec4 color1 = texture2D(tex1, TexCoord1.st);
gl_FragColor = mix(color0, color1, done) * Color;
}
"""
BLEND_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform float done;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
discard;
}
vec4 color0 = texture2D(tex0, TexCoord0.st);
vec4 color1 = texture2D(tex1, TexCoord1.st);
gl_FragColor = mix(color0, color1, done) * Color;
}
"""
IMAGEBLEND_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float offset;
uniform float multiplier;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 TexCoord2;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
vec4 color0 = texture2D(tex0, TexCoord0.st);
vec4 color1 = texture2D(tex1, TexCoord1.st);
vec4 color2 = texture2D(tex2, TexCoord2.st);
float a = clamp((color0.a + offset) * multiplier, 0.0, 1.0);
gl_FragColor = mix(color1, color2, a) * Color;
}
"""
IMAGEBLEND_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float offset;
uniform float multiplier;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 TexCoord2;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
discard;
}
vec4 color0 = texture2D(tex0, TexCoord0.st);
vec4 color1 = texture2D(tex1, TexCoord1.st);
vec4 color2 = texture2D(tex2, TexCoord2.st);
float a = clamp((color0.a + offset) * multiplier, 0.0, 1.0);
gl_FragColor = mix(color1, color2, a) * Color;
}
"""
def check_status(shader, handle, type):
"""
Checks the status of a shader or program. If it fails, then an
exception is raised.
"""
cdef GLint status = 0
cdef GLint log_length = 0
if shader:
glGetShaderiv(handle, type, &status)
else:
glGetProgramiv(handle, type, &status)
if status == 1: # 0 for problems.
return
if shader:
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length)
else:
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length)
log = ' ' * log_length
if shader:
glGetShaderInfoLog(handle, log_length, &log_length, <char *> log)
else:
glGetProgramInfoLog(handle, log_length, &log_length, <char *> log)
raise Exception("Shader error: %s" % log)
def compile_shader(kind, source):
"""
Allocates and compiles a shader.
"""
cdef const char *sourceptr = <const char *> source
cdef int lensource = len(source)
handle = glCreateShader(kind)
glShaderSource(handle, 1, <const GLchar * const*> &sourceptr, &lensource)
glCompileShader(handle)
check_status(True, handle, GL_COMPILE_STATUS)
return handle
def compile_program(vertex, fragment):
"""
Compiles a pair of shaders into a program.
"""
vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex)
fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment)
program = glCreateProgram()
glAttachShader(program, vertex_shader)
glAttachShader(program, fragment_shader)
glLinkProgram(program)
check_status(False, program, GL_LINK_STATUS)
glUseProgram(program)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
return program
cdef class Program(object):
"""
Encapsulates a program.
"""
cdef GLuint program
# Attributes.
cdef GLint Vertex
cdef GLint VertexTexCoord0
cdef GLint VertexTexCoord1
cdef GLint VertexTexCoord2
# Uniforms.
cdef GLint Projection
cdef GLint Color
cdef GLint tex0
cdef GLint tex1
cdef GLint tex2
cdef GLint clip0
cdef GLint clip1
cdef GLint offset
cdef GLint multiplier
cdef GLint done
def __init__(self, vertex, fragment):
self.program = compile_program(vertex, fragment)
self.Vertex = glGetAttribLocation(self.program, "Vertex")
self.VertexTexCoord0 = glGetAttribLocation(self.program, "VertexTexCoord0")
self.VertexTexCoord1 = glGetAttribLocation(self.program, "VertexTexCoord1")
self.VertexTexCoord2 = glGetAttribLocation(self.program, "VertexTexCoord2")
self.Projection = glGetUniformLocation(self.program, "Projection")
self.tex0 = glGetUniformLocation(self.program, "tex0")
self.tex1 = glGetUniformLocation(self.program, "tex1")
self.tex2 = glGetUniformLocation(self.program, "tex2")
self.offset = glGetUniformLocation(self.program, "offset")
self.multiplier = glGetUniformLocation(self.program, "multiplier")
self.done = glGetUniformLocation(self.program, "done")
self.Color = glGetUniformLocation(self.program, "Color")
self.clip0 = glGetUniformLocation(self.program, "clip0")
self.clip1 = glGetUniformLocation(self.program, "clip1")
def disable_attribs(self):
# Disable the vertex attributes used by this program.
if self.Vertex != -1:
glDisableVertexAttribArray(self.Vertex)
if self.VertexTexCoord0 != -1:
glDisableVertexAttribArray(self.VertexTexCoord0)
if self.VertexTexCoord1 != -1:
glDisableVertexAttribArray(self.VertexTexCoord1)
if self.VertexTexCoord2 != -1:
glDisableVertexAttribArray(self.VertexTexCoord2)
def delete(self):
glDeleteProgram(self.program)
cdef class ShaderEnviron(Environ):
"""
This is an environment that uses shaders.
"""
cdef Program program
cdef float projection[16]
cdef Program blit_program
cdef Program blend_program
cdef Program imageblend_program
cdef Program blit_clip_program
cdef Program blend_clip_program
cdef Program imageblend_clip_program
cdef bint clipping
cdef double clip_x0
cdef double clip_y0
cdef double clip_x1
cdef double clip_y1
cdef int viewport_x
cdef int viewport_y
cdef int viewport_w
cdef int viewport_h
def init(self):
self.blit_program = Program(VERTEX_SHADER1, BLIT_SHADER)
self.blit_clip_program = Program(VERTEX_SHADER1, BLIT_CLIP_SHADER)
self.blend_program = Program(VERTEX_SHADER2, BLEND_SHADER)
self.blend_clip_program = Program(VERTEX_SHADER2, BLEND_CLIP_SHADER)
self.imageblend_program = Program(VERTEX_SHADER3, IMAGEBLEND_SHADER)
self.imageblend_clip_program = Program(VERTEX_SHADER3, IMAGEBLEND_CLIP_SHADER)
# The current program.
self.program = None
def deinit(self):
"""
Called before changing the GL context.
"""
if self.program is not None:
self.program.disable_attribs()
self.program = None
self.blit_program.delete()
self.blend_program.delete()
self.imageblend_program.delete()
def activate(self, Program program):
if self.program is not None:
self.program.disable_attribs()
self.program = program
glUseProgram(program.program)
glUniformMatrix4fv(program.Projection, 1, GL_FALSE, self.projection)
if self.clipping:
glUniform2f(program.clip0, self.clip_x0 - .01, self.clip_y0 - .01)
glUniform2f(program.clip1, self.clip_x1 + .01, self.clip_y1 + .01)
cdef void blit(self):
if self.clipping:
program = self.blit_clip_program
else:
program = self.blit_program
if self.program is not program:
self.activate(program)
glUniform1i(program.tex0, 0)
cdef void blend(self, double fraction):
if self.clipping:
program = self.blend_clip_program
else:
program = self.blend_program
if self.program is not program:
self.activate(program)
glUniform1i(program.tex0, 0)
glUniform1i(program.tex1, 1)
glUniform1f(program.done, fraction)
cdef void imageblend(self, double fraction, int ramp):
if self.clipping:
program = self.imageblend_clip_program
else:
program = self.imageblend_program
if self.program is not program:
self.activate(program)
glUniform1i(program.tex0, 0)
glUniform1i(program.tex1, 1)
glUniform1i(program.tex2, 2)
# Prevent a DBZ if the user gives us a 0 ramp.
if ramp < 1:
ramp = 1
# Compute the offset to apply to the alpha.
start = -1.0
end = ramp / 256.0
offset = start + ( end - start) * fraction
# Setup the multiplier and the offset.
glUniform1f(program.multiplier, 256.0 / ramp)
glUniform1f(program.offset, offset)
cdef void set_vertex(self, float *vertices):
glEnableVertexAttribArray(self.program.Vertex)
glVertexAttribPointer(self.program.Vertex, 2, GL_FLOAT, GL_FALSE, 0, <GLubyte *> vertices)
cdef void set_texture(self, int unit, float *coords):
cdef tex
if unit == 0:
tex = self.program.VertexTexCoord0
elif unit == 1:
tex = self.program.VertexTexCoord1
elif unit == 2:
tex = self.program.VertexTexCoord2
else:
return
if tex < 0:
return
if coords != NULL:
glVertexAttribPointer(tex, 2, GL_FLOAT, GL_FALSE, 0, <GLubyte *> coords)
glEnableVertexAttribArray(tex)
else:
glDisableVertexAttribArray(tex)
cdef void set_color(self, double r, double g, double b, double a):
glUniform4f(self.program.Color, r, g, b, a)
cdef void ortho(self, double left, double right, double bottom, double top, double near, double far):
self.projection[ 0] = 2 / (right - left)
self.projection[ 4] = 0
self.projection[ 8] = 0
self.projection[12] = -(right + left) / (right - left)
self.projection[ 1] = 0
self.projection[ 5] = 2 / (top - bottom)
self.projection[ 9] = 0
self.projection[13] = -(top + bottom) / (top - bottom)
self.projection[ 2] = 0
self.projection[ 6] = 0
self.projection[10] = -2 / (far - near)
self.projection[14] = -(far + near) / (far - near)
self.projection[ 3] = 0
self.projection[ 7] = 0
self.projection[11] = 0
self.projection[15] = 1
if self.program is not None:
self.program.disable_attribs()
self.program = None
cdef void project(self, x, y, z, double *rv_x, double *rv_y, double *rv_z):
"""
Given a point, projects it using the projection.
"""
rv_x[0] = x * self.projection[0] + y * self.projection[4] + z * self.projection[8] + self.projection[12]
rv_y[0] = x * self.projection[1] + y * self.projection[5] + z * self.projection[9] + self.projection[13]
rv_z[0] = x * self.projection[2] + y * self.projection[6] + z * self.projection[10] + self.projection[14]
cdef void set_clip(self, tuple clip_box, GL2Draw draw):
cdef double minx, miny, maxx, maxy, z
cdef double vwidth, vheight
cdef double px, py, pw, ph
cdef int cx, cy, cw, ch
cdef int psw, psh
if clip_box == draw.default_clip:
self.unset_clip(draw)
return
minx, miny, maxx, maxy = clip_box
psw, psh = draw.physical_size
# The clipping box.
self.clipping = True
self.clip_x0 = minx
self.clip_y0 = miny
self.clip_x1 = maxx
self.clip_y1 = maxy
# Set the scissor rectangle to be slightly larger than the
# clipping box. This ensures everything that needs to be drawn
# is drawn, and we don't spend a lot of time shading clipped
# fragments.
if draw.clip_rtt_box is None:
z = 0
# Project to normalized coordinates.
self.project(minx, miny, z, &minx, &miny, &z)
self.project(maxx, maxy, z, &maxx, &maxy, &z)
# Convert to window coordinates.
minx = (minx + 1) * self.viewport_w / 2 + self.viewport_x
maxx = (maxx + 1) * self.viewport_w / 2 + self.viewport_x
miny = (miny + 1) * self.viewport_h / 2 + self.viewport_y
maxy = (maxy + 1) * self.viewport_h / 2 + self.viewport_y
# Increase the bounding box, to ensure every relevant pixel is
# in it. The shader will take care of enforcing the actual box.
minx -= 1
maxx += 1
miny += 1
maxy -= 1
if minx < 0:
minx = 0
if miny < 0:
miny = 0
glEnable(GL_SCISSOR_TEST)
glScissor(<GLint> round(minx), <GLint> round(maxy), <GLint> round(maxx - minx), <GLsizei> round(miny - maxy))
else:
cx, cy, cw, ch = draw.clip_rtt_box
glEnable(GL_SCISSOR_TEST)
glScissor(<GLint> round(minx - cx), <GLint> round(miny - cy), <GLint> round(maxx - minx), <GLint> round(maxy - miny))
if self.program is not None:
self.program.disable_attribs()
self.program = None
cdef void unset_clip(self, GL2Draw draw):
glDisable(GL_SCISSOR_TEST)
self.clipping = False
self.clip_x0 = 0
self.clip_y0 = 0
self.clip_x1 = 65535
self.clip_y1 = 65535
if self.program is not None:
self.program.disable_attribs()
self.program = None
cdef void viewport(self, int x, int y, int width, int height):
glViewport(x, y, width, height)
self.viewport_x = x
self.viewport_y = y
self.viewport_w = width
self.viewport_h = height
+73
View File
@@ -0,0 +1,73 @@
from renpy.display.matrix cimport Matrix
cdef class Polygon:
# The number of points in the polygon.
cdef int points
# The stride of the polygon - the number of floats comprising a data
# point.
cdef int stride
# The data in a polygon.
cdef float *data
cpdef Polygon copy(Polygon self)
cpdef void offset_inplace(Polygon self, double x, double y, double z)
cpdef Polygon offset(Polygon self, double x, double y, double z)
cpdef void multiply_matrix_inplace(Polygon self, Matrix matrix)
cpdef Polygon multiply_matrix(Polygon self, Matrix matrix)
cpdef void perspective_divide_inplace(Polygon self)
cpdef Polygon perspective_divide(Polygon self)
cpdef Polygon intersect(Polygon self, Polygon other)
# Constructors for particularly useful polygons.
cpdef Polygon rectangle(double l, double t, double r, double b)
cpdef Polygon texture_rectangle(double pl, double pt, double pr, double pb, double tl, double tt, double tr, double tb)
cdef class Mesh:
# The total number of points.
cdef public int points
# The stride - the amount of data per point, in floats.
cdef public int stride
# A map from an attribute to the offset of that attribute.
cdef public dict attributes
# A list of polygons that comprise the mesh.
cdef public list polygons
# An array that contains all the data from the points. This is NULL
# until it's created.
cdef float *data
cdef float *get_data(self, int offset)
# The count of polygons that comprise the mesh. This is only valid after
# get_data is called.
cdef public int polygon_count
# If all the polygons in the mesh have the same number of points, this is
# that number of points. Else, it's 0.
cdef public int polygon_points
cpdef Mesh copy(Mesh self)
cpdef void offset_inplace(Mesh self, double x, double y, double z)
cpdef Mesh offset(Mesh self, double x, double y, double z)
cpdef void multiply_matrix_inplace(Mesh self, Matrix matrix)
cpdef Mesh multiply_matrix(Mesh self, Matrix matrix)
cpdef void perspective_divide_inplace(Mesh self)
cpdef Mesh perspective_divide(Mesh self)
cpdef Mesh intersect(Mesh self, Mesh other)
cpdef Mesh crop(Mesh self, Polygon other)
+835
View File
@@ -0,0 +1,835 @@
from __future__ import print_function
from renpy.display.matrix cimport Matrix
from libc.stdlib cimport malloc, free
from libc.string cimport memcpy
from libc.math cimport copysign
DEF MAX_POINTS = 128
DEF X = 0
DEF Y = 1
DEF Z = 2
DEF W = 3
DEF TX = 4
DEF TY = 5
cdef class Polygon:
def __init__(Polygon self, int stride, int points, data):
"""
Allocates a new Polygon.
`stride`
The number of floats per vertex. This should be at least 3, for the
default aPosition vec3.
`points`
The number of vertices in the polygon that space is allocated for.
If `data` is given, this is also the number of points in the polygon.
`data`
If not None, an iterable of length stride * points, that gives the
vertex data for each of the points.
"""
cdef int i
self.stride = stride
self.data = <float *> malloc(sizeof(float) * points * stride)
if data is None:
self.points = 0
else:
self.points = points
for 0 <= i < stride * points:
self.data[i] = data[i]
def __dealloc__(Polygon self):
free(self.data)
cpdef Polygon copy(Polygon self):
"""
Returns a copy of this polygon.
"""
cdef Polygon rv = Polygon(self.stride, self.points, None)
rv.points = self.points
memcpy(rv.data, self.data, sizeof(float) * self.stride * self.points)
return rv
cpdef void offset_inplace(Polygon self, double x, double y, double z):
cdef float *p = self.data
cdef int i
for 0 <= i < self.points:
p[X] += x
p[Y] += y
p[Z] += z
p += self.stride
cpdef Polygon offset(Polygon self, double x, double y, double z):
cdef Polygon rv = self.copy()
rv.offset_inplace(x, y, z)
return rv
cpdef void perspective_divide_inplace(Polygon self):
"""
Performs a perspective divide on each point in this polygon, in place.
"""
cdef float *p = self.data
cdef int i
for 0 <= i < self.points:
if p[W]:
p[X] /= p[W]
p[Y] /= p[W]
p[Z] /= p[W]
p += self.stride
cpdef Polygon perspective_divide(Polygon self):
"""
Returns a perspective divided copy of this polygon.
"""
cdef Polygon rv = self.copy()
rv.perspective_divide_inplace()
return rv
cpdef void multiply_matrix_inplace(Polygon self, Matrix m):
"""
Multiplies each point's position with the matrix `m`, in place.
"""
cdef int i
cdef float *p = self.data
for 0 <= i < self.points:
m.transform4(p, p+1, p+2, p+3, p[0], p[1], p[2], p[3])
p += self.stride
cpdef Polygon multiply_matrix(Polygon self, Matrix m):
"""
Multiplies each point's position with the matrix `m`, in place,
and returns a copy.
"""
cdef Polygon rv = self.copy()
rv.multiply_matrix_inplace(m)
return rv
def __mul__(self, other):
if isinstance(other, Matrix):
return self.multiply_matrix(other)
return NotImplemented
def __rmul__(self, other):
if isinstance(other, Matrix):
return self.multiply_matrix(other)
return NotImplemented
cpdef Polygon intersect(Polygon self, Polygon other):
"""
Returns a new polygon that is the intersection of the current polygon
and the other polygon.
"""
rv = intersect(other, self, self.stride + other.stride - 4, True)
if rv is None:
return None
# We only need to interpolate OP if it has something other than
# position data.
if other.stride > 4:
barycentric(other, rv, self.stride - 4)
barycentric(self, rv, 0)
return rv
def __repr__(self):
rv = "<Polygon"
for 0 <= p < self.points:
rv += "\n ["
for 0 <= i < self.stride:
rv += "{:g}, ".format(self.data[p * self.stride + i])
rv += "]"
rv += ">"
return rv
cpdef Polygon rectangle(double l, double t, double r, double b):
"""
Generates a rectangle with one corner at (l, t, 0, 1) and the other at (r, b, 0, 1).
"""
cdef Polygon rv = Polygon(4, 4, None)
rv.points = 4
cdef float *p = rv.data
p[X] = l
p[Y] = t
p[Z] = 0
p[W] = 1
p += 4
p[X] = r
p[Y] = t
p[Z] = 0
p[W] = 1
p += 4
p[X] = r
p[Y] = b
p[Z] = 0
p[W] = 1
p += 4
p[X] = l
p[Y] = b
p[Z] = 0
p[W] = 1
return rv
cpdef Polygon texture_rectangle(double pl, double pt, double pr, double pb, double tl, double tt, double tr, double tb):
"""
This generates a rectangle with teture coordinates. One corner is at (pl, pt, 0, 1) with
texture coordinates (tl, tt), while the other is at (pr, pb, 0, 1) / (tr, tb).
"""
cdef Polygon rv = Polygon(6, 4, None)
rv.points = 4
cdef float *p = rv.data
p[X] = pl
p[Y] = pt
p[Z] = 0
p[W] = 1
p[TX] = tl
p[TY] = tt
p += 6
p[X] = pr
p[Y] = pt
p[Z] = 0
p[W] = 1
p[TX] = tr
p[TY] = tt
p += 6
p[X] = pr
p[Y] = pb
p[Z] = 0
p[W] = 1
p[TX] = tr
p[TY] = tb
p += 6
p[X] = pl
p[Y] = pb
p[Z] = 0
p[W] = 1
p[TX] = tl
p[TY] = tb
return rv
# Accssor functions for the contents of polygons.
cdef inline float get(Polygon p, int index, int offset):
return p.data[index * p.stride + offset]
cdef inline float *ref(Polygon p, int index, int offset):
return &p.data[index * p.stride + offset]
cdef inline float set(Polygon p, int index, int offset, float value):
p.data[index * p.stride + offset] = value
return value
cdef void intersectLines(
float x1, float y1,
float x2, float y2,
float x3, float y3,
float x4, float y4,
float *px, float *py,
):
"""
Given a line that goes through (x1, y1) to (x2, y2), and a second line
that goes through (x3, y3) and (x4, y4), find the point where the two
lines intersect.
"""
cdef float denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
px[0] = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom
py[0] = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
cdef Polygon intersectOnce(float winding, float a0x, float a0y, float a1x, float a1y, Polygon p, int rvstride):
# The vector from a0 to a1.
cdef float vecax = a1x - a0x
cdef float vecay = a1y - a0y
# The vector from a0 to each point.
cdef float vecpx
cdef float vexpx
# For each point, are we inside or outside?
cdef bint inside[MAX_POINTS]
cdef int i
cdef int j
cdef bint allin = True
# Figure out which points are 'inside' the wound line.
for 0 <= i < p.points:
vecpx = get(p, i, X) - a0x
vecpy = get(p, i, Y) - a0y
inside[i] = winding * (vecax * vecpy - vecay * vecpx) <= 0.00001
allin = allin and inside[i]
# If all the points are inside, just return the polygon intact.
if allin:
return p
rv = Polygon(rvstride, p.points * 2, None)
j = p.points - 1
for 0 <= i < p.points:
if inside[i]:
if not inside[j]:
intersectLines(
a0x, a0y, a1x, a1y,
get(p, j, X), get(p, j, Y), get(p, i, X), get(p, i, Y),
ref(rv, rv.points, X), ref(rv, rv.points, Y))
rv.points += 1
set(rv, rv.points, X, get(p, i, X))
set(rv, rv.points, Y, get(p, i, Y))
rv.points += 1
else:
if inside[j]:
intersectLines(
a0x, a0y, a1x, a1y,
get(p, j, X), get(p, j, Y), get(p, i, X), get(p, i, Y),
ref(rv, rv.points, X), ref(rv, rv.points, Y))
rv.points += 1
j = i
return rv
cdef Polygon restride_polygon(Polygon src, int new_stride):
"""
Returns a copy of the polygon with the stride changed to
be `new_stride`.
"""
cdef Polygon rv = Polygon(new_stride, src.points, None)
cdef float *ap = src.data
cdef float *bp = rv.data
cdef int i
for 0 <= i < src.points:
bp[X] = ap[X]
bp[Y] = ap[Y]
bp[Z] = ap[Z]
ap += src.stride
bp += rv.stride
rv.points = src.points
return rv
cpdef intersect(Polygon a, Polygon b, int new_stride, bint copy):
"""
Given two Polygons, returns a new Polygon that is the intersecti of
the two. This assumes that both polygons are convex, and wound in the
same direction (clockwise or counterclockwise).
`new_stride` is the stride of the new Polygon.
"""
cdef int i
cdef float a0x, a0y, a1x, a1y
cdef float winding
a0x = get(a, a.points-2, X)
a0y = get(a, a.points-2, Y)
a1x = get(a, a.points-1, X)
a1y = get(a, a.points-1, Y)
cdef Polygon rv = b
for 0 <= i < a.points:
a2x = get(a, i, X)
a2y = get(a, i, Y)
winding = (a2x - a0x) * (a1y - a0y) - (a2y - a0y) * (a1x - a0x)
winding = copysign(1.0, winding)
if winding:
rv = intersectOnce(winding, a1x, a1y, a2x, a2y, rv, new_stride)
if rv.points < 3:
return None
a0x = a1x
a0y = a1y
a1x = a2x
a1y = a2y
if rv is b:
if (not copy) and (new_stride == rv.stride):
return rv
rv = restride_polygon(rv, new_stride)
return rv
cpdef barycentric(Polygon a, Polygon b, int offset):
"""
This uses barycentric interpolation to transfer the attributes from
Polygon `a` to Polygon `b`, starting at `offset`.
"""
cdef int i
cdef int j
cdef int k
cdef float ax0 = get(a, 0, X)
cdef float ay0 = get(a, 0, Y)
cdef float az0 = get(a, 0, Z)
cdef float v0x = get(a, 1, X) - ax0
cdef float v0y = get(a, 1, Y) - ay0
cdef float v0z = get(a, 1, Z) - az0
cdef float v1x, v1y, v1z, v2x, v2y, v2z
cdef float d00, d01, d11, d20, d21
cdef float d003, d013, d113, d203, d213
cdef float denom
cdef float u, v, w
for 2 <= i < a.points:
v1x = get(a, i, X) - ax0
v1y = get(a, i, Y) - ay0
v1z = get(a, i, Z) - az0
d00 = v0x * v0x + v0y * v0y
d01 = v0x * v1x + v0y * v1y
d11 = v1x * v1x + v1y * v1y
d003 = d00 + v0z * v0z
d013 = d01 + v0z * v1z
d113 = d11 + v1z * v1z
denom = d00 * d11 - d01 * d01
denom3 = d003 * d113 - d013 * d013
if denom and denom3:
for 0 <= j < b.points:
v2x = get(b, j, X) - ax0
v2y = get(b, j, Y) - ay0
d20 = v2x * v0x + v2y * v0y
d21 = v2x * v1x + v2y * v1y
v = (d11 * d20 - d01 * d21) / denom
w = (d00 * d21 - d01 * d20) / denom
if not ((-0.00001 <= v <= 1.00001) and (-0.00001 <= w <= 1.00001)):
continue
u = 1.0 - v - w
z = u * az0 + v * get(a, i-1, Z) + w * get(a, i, Z)
set(b, j, Z, z)
set(b, j, W, 1.0)
v2z = z - az0
d203 = d20 + v2z * v0z
d213 = d21 + v2z * v1z
v = (d113 * d203 - d013 * d213) / denom3
w = (d003 * d213 - d013 * d203) / denom3
u = 1.0 - v - w
for 3 <= k < a.stride:
set(b, j, k + offset,
u * get(a, 0, k) +
v * get(a, i-1, k) +
w * get(a, i, k))
v0x = v1x
v0y = v1y
def barycentric_point(Polygon a, float x, float y):
"""
This expects Polygon a to be a polygon with a stride of 6, with a
position (x, y, z, 1) and a pair of attributes (tx, ty) at each
point.
Given `x` and `y`, this barycentricly interpolates the `x` and `y`
at the point, if it's inside the Polygon. It returns None, None
if the point is inside the polygon.
This is mostly intended to be used to interpolate screen-space
coordinates onto a Render's internal coordinate space.
"""
cdef Polygon b = Polygon(6, 1, None)
b.points = 1
cdef float *p = b.data
p[X] = x
p[Y] = y
p[Z] = 0.0
p[W] = 0.0
barycentric(a, b, 0)
if p[W] == 0:
return None, None
return (p[TX], p[TY])
cdef class Mesh:
def __init__(Mesh self):
"""
Represents a mesh consisting of one or more polygons. A mesh needs
to be created either by:
* First, create the mesh object.
* Next, add any additional attributes to it.
* Finally, add polygons to the mesh.
Or by copying the mesh or calling a method that returns a new mesh.
After that, the inplace methods can be called to further change
the mesh.
Lastly, get_data can be called to access the mesh data. Once get_data
is called, the mesh becomes immutable. (This is usally called by
Shader.draw.)
"""
self.points = 0
self.stride = 4
self.polygons = [ ]
self.attributes = { "aPosition" : 0 }
self.data = NULL
def __dealloc__(Mesh self):
if self.data:
free(self.data)
def add_attribute(Mesh self, name, size):
"""
Adds an attribute to this mesh.
`name`
The name of the attribute.
`size`
The number of floats per vertex that make up the attribute.
"""
self.attributes[name] = self.stride
self.stride += size
def add_polygon(Mesh self, data):
"""
Adds a polygon.
`data`
This is an iterable. It should have self.stride data for each
vertex, and the number of vertices is derived from the length
of the data.
"""
cdef Polygon p = Polygon(self.stride, len(data) // self.stride, data)
self.points += p.points
self.polygons.append(p)
def add_rectangle(Mesh self, double x, double y, double w, double h):
"""
Adds a polygon.
"""
self.points += 4
self.polygons.append(rectangle(x, y, w, h))
def add_texture_rectangle(Mesh self, double pl, double pt, double pr, double pb, double tl, double tt, double tr, double tb):
"""
Returns a polygon corresponding to a texture rectangle.
"""
self.points += 4
self.polygons.append(texture_rectangle(pl, pt, pr, pb, tl, tt, tr, tb))
cdef float *get_data(Mesh self, int offset):
"""
When called, this freezes the data of this mesh. It then returns
a pointer to the data for the attribute at ``offset``. This is a
pointer to the first piece of data, with others following at
self.stride intervals.
"""
cdef Polygon p
cdef int i
cdef int count
cdef int points
self.polygon_count = count = len(self.polygons)
if not count:
self.polygon_points = 0
return NULL
p = self.polygons[0]
points = p.points
if count == 1:
self.polygon_points = points
return p.data + offset
if not self.data:
self.data = <float *> malloc(self.points * self.stride * sizeof(float))
i = 0
for p in self.polygons:
memcpy(&self.data[i], p.data, p.points * self.stride * sizeof(float))
i += p.points * self.stride
if p.points != points:
points = 0
self.polygon_points = points
return self.data + offset
cpdef Mesh copy(Mesh self):
"""
Returns a copy of this Mesh.
"""
rv = Mesh()
rv.stride = self.stride
rv.points = self.points
rv.polygons = [ i.copy() for i in self.polygons ]
rv.attributes = self.attributes
return rv
cpdef void offset_inplace(Mesh self, double x, double y, double z):
"""
Offsets each polygon in the mesh, in place.
"""
cdef Polygon p
for p in self.polygons:
p.offset_inplace(x, y, z)
cpdef Mesh offset(self, double x, double y, double z):
"""
Returns a copy of the mesh with each polygon offset.
"""
cdef Mesh rv = self.copy()
rv.offset_inplace(x, y, z)
return rv
cpdef void perspective_divide_inplace(Mesh self):
"""
Performs a perspective divide on this mesh, inplace.
"""
cdef Polygon p
for p in self.polygons:
p.perspective_divide_inplace()
cpdef Mesh perspective_divide(Mesh self):
"""
Returns a copy of this mesh that has been perspective divided.
"""
cdef Mesh rv = self.copy()
rv.perspective_divide_inplace()
return rv
cpdef void multiply_matrix_inplace(Mesh self, Matrix matrix):
"""
Multiplies the position data in this Mesh by the matrix, in place.
"""
cdef Polygon p
for p in self.polygons:
p.multiply_matrix_inplace(matrix)
cpdef Mesh multiply_matrix(Mesh self, Matrix matrix):
"""
Returns a copy of this Mesh with the position data multipled by Matrix.
"""
cdef Mesh rv = self.copy()
rv.multiply_matrix_inplace(matrix)
return rv
def __mul__(self, other):
if isinstance(other, Matrix):
return self.multiply_matrix(other)
return NotImplemented
def __rmul__(self, other):
if isinstance(other, Matrix):
return self.multiply_matrix(other)
return NotImplemented
cpdef Mesh intersect(Mesh self, Mesh other):
"""
Intersects this mesh with another mesh, and returns a new mesh that
is the result. The resulting mesh has z- and w-coordinates from this
mesh. Attributes that are present in this mesh are taken from
this mesh, otherwise the attributes from the other mesh are used.
"""
rv = Mesh()
rv.stride = self.stride + other.stride - 4
rv.attributes = { k : v + self.stride - 4 for k, v in other.attributes.iteritems() }
rv.attributes.update(self.attributes)
cdef Polygon op
cdef Polygon sp
cdef Polygon p
for op in other.polygons:
for sp in self.polygons:
p = intersect(op, sp, rv.stride, True)
if p is None:
continue
# We only need to interpolate OP if it has something other than
# position data.
if op.stride > 4:
barycentric(op, p, self.stride - 4)
barycentric(sp, p, 0)
rv.polygons.append(p)
rv.points += p.points
return rv
cpdef Mesh crop(Mesh self, Polygon op):
"""
This uses the polygon `op` to crop this mesh.
This may or may not return a new Mesh. (If op contains all of self, it
will return self.)
"""
same = True
cdef Polygon sp
cdef Polygon p
cdef list polygons = [ ]
cdef int points = 0
for sp in self.polygons:
p = intersect(op, sp, self.stride, False)
if p is None:
same = False
continue
if p is not sp:
same = False
barycentric(sp, p, 0)
polygons.append(p)
points += p.points
if same:
return self
rv = Mesh()
rv.stride = self.stride
rv.attributes = self.attributes
rv.polygons = polygons
rv.points = points
return rv
def __repr__(self):
rv = "<Mesh "
for p in self.polygons:
rv += repr(p)
rv += ", "
rv += ">"
return rv
-96
View File
@@ -1,96 +0,0 @@
#@PydevCodeAnalysisIgnore
#cython: profile=False
# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function
from uguugl cimport *
from gl2draw cimport *
from gl2draw import Rtt
import renpy
# The framebuffer object we use.
cdef GLuint fbo
# The root framebuffer.
cdef GLint root_fbo
class FboRtt(Rtt):
"""
This class uses texture copying to implement Render-to-texture.
"""
def init(self):
glGenFramebuffers(1, &fbo)
cdef int i
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &i)
self.size_limit = i
renpy.display.log.write("FBO Maximum Texture Size: %d", i)
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &root_fbo);
renpy.display.log.write("Root FBO is: %d", root_fbo)
def deinit(self):
"""
Called before changing the GL context.
"""
glBindFramebuffer(GL_FRAMEBUFFER, root_fbo)
glDeleteFramebuffers(1, &fbo)
def begin(self):
"""
This function should be called when a Render-to-texture
session begins. It's responsible for setting the GPU to
RTT mode.
"""
def render(self, Environ environ, texture, x, y, w, h, draw_func):
"""
This function is called to trigger a rendering to a texture.
`x`, `y`, `w`, and `h` specify the location and dimensions of
the sub-image to render to the texture. `draw_func` is called
to render the texture.
"""
glBindFramebuffer(GL_FRAMEBUFFER, fbo)
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0)
environ.viewport(0, 0, w, h)
environ.ortho(x, x + w, y, y + h, -1, 1)
draw_func(x, y, w, h)
glBindFramebuffer(GL_FRAMEBUFFER, root_fbo)
def get_size_limit(self, dimension):
return self.size_limit
+21
View File
@@ -0,0 +1,21 @@
from uguugl cimport *
cdef class Program:
# The name of this program.
cdef public object name
# The number of the OpenGL program created.
cdef GLuint program
# The text of the vertex and fragment shaders.
cdef object vertex
cdef object fragment
cdef public dict uniforms
cdef public list attributes
cdef public int sampler
cdef GLuint load_shader(self, GLenum shader_type, source) except? 0
+302
View File
@@ -0,0 +1,302 @@
from uguugl cimport *
from libc.stdlib cimport malloc, free
from renpy.gl2.gl2geometry cimport Polygon, Mesh
from renpy.gl2.gl2texture cimport GLTexture
from renpy.display.matrix cimport Matrix
class ShaderError(Exception):
pass
GLSL_PRECISIONS = {
"highp",
"mediump",
"lowp",
}
cdef class Uniform:
cdef Program program
cdef GLint location
cdef bint ready
def __init__(self, program, location):
self.program = program
self.location = location
self.ready = False
cdef void assign(self, data):
return
cdef void finish(self):
return
cdef class UniformFloat(Uniform):
cdef void assign(self, data):
glUniform1f(self.location, data)
cdef class UniformVec2(Uniform):
cdef void assign(self, data):
glUniform2f(self.location, data[0], data[1])
cdef class UniformVec3(Uniform):
cdef void assign(self, data):
glUniform3f(self.location, data[0], data[1], data[2])
cdef class UniformVec4(Uniform):
cdef void assign(self, data):
glUniform4f(self.location, data[0], data[1], data[2], data[3])
cdef class UniformMat4(Uniform):
cdef void assign(self, data):
glUniformMatrix4fv(self.location, 1, GL_TRUE, (<Matrix> data).m)
cdef class UniformSampler2D(Uniform):
cdef int sampler
def __init__(self, program, location):
Uniform.__init__(self, program, location)
self.sampler = program.sampler
program.sampler += 1
cdef void assign(self, data):
glActiveTexture(GL_TEXTURE0 + self.sampler)
glUniform1i(self.location, self.sampler)
if isinstance(data, GLTexture):
glBindTexture(GL_TEXTURE_2D, data.number)
else:
glBindTexture(GL_TEXTURE_2D, data)
cdef void finish(self):
glActiveTexture(GL_TEXTURE0 + self.sampler)
return
UNIFORM_TYPES = {
"float" : UniformFloat,
"vec2" : UniformVec2,
"vec3" : UniformVec3,
"vec4" : UniformVec4,
"mat4" : UniformMat4,
"sampler2D" : UniformSampler2D,
}
cdef class Attribute:
cdef object name
cdef GLint location
cdef GLint size
def __init__(self, name, GLint location, GLint size):
self.name = name
self.location = location
self.size = size
ATTRIBUTE_TYPES = {
"float" : 1,
"vec2" : 2,
"vec3" : 3,
"vec4" : 4,
}
cdef class Program:
"""
Represents an OpenGL program.
"""
def __init__(self, name, vertex, fragment):
self.name = name
self.vertex = vertex
self.fragment = fragment
# A map from uniform name to a Uniform object.
self.uniforms = { }
# A list of Attribute objects
self.attributes = [ ]
# The index of the next sampler to be added.
self.sampler = 0
def find_variables(self, source):
for l in source.split("\n"):
l = l.strip()
l = l.rstrip("; ")
tokens = l.split()
def advance():
if not tokens:
return None
else:
return tokens.pop(0)
token = advance()
if token == "invariant":
token = advance()
if token == "uniform":
storage = "uniform"
types = UNIFORM_TYPES
elif token == "attribute":
storage = "attribute"
types = ATTRIBUTE_TYPES
else:
continue
token = advance()
if token in ( "highp", "mediump", "lowp"):
token = advance()
continue
if token not in types:
raise ShaderError("Unsupported type {} in '{}'. Only float, vec<2-4>, mat<2-4>, and sampler2D are supported.".format(token, l))
type = token
name = advance()
if name is None:
raise ShaderError("Couldn't finds name in {}".format(l))
if tokens:
raise ShaderError("Spurious tokens after the name in '{}'. Arrays are not supported in Ren'Py.".format(l))
if storage == "uniform":
location = glGetUniformLocation(self.program, name)
if location >= 0:
self.uniforms[name] = types[type](self, location)
else:
location = glGetAttribLocation(self.program, name)
if location >= 0:
self.attributes.append(Attribute(name, location, types[type]))
cdef GLuint load_shader(self, GLenum shader_type, source) except? 0:
"""
This loads a shader into the GPU, and returns the number.
"""
cdef GLuint shader
cdef GLchar *source_ptr = <char *> source
cdef GLint length
cdef GLint status
cdef char error[1024]
shader = glCreateShader(shader_type)
length = len(source)
glShaderSource(shader, 1, <const GLchar * const *> &source_ptr, &length)
glCompileShader(shader)
glGetShaderiv(shader, GL_COMPILE_STATUS, &status)
if status == GL_FALSE:
glGetShaderInfoLog(shader, 1024, NULL, error)
raise ShaderError((<object> error).decode("utf-8"))
return shader
def load(self):
"""
This loads the program into the GPU.
"""
cdef GLuint fragment
cdef GLuint vertex
cdef GLuint program
cdef GLint status
cdef char error[1024]
vertex = self.load_shader(GL_VERTEX_SHADER, self.vertex)
fragment = self.load_shader(GL_FRAGMENT_SHADER, self.fragment)
program = glCreateProgram()
glAttachShader(program, vertex)
glAttachShader(program, fragment)
glLinkProgram(program)
glGetProgramiv(program, GL_LINK_STATUS, &status)
if status == GL_FALSE:
glGetProgramInfoLog(program, 1024, NULL, error)
raise ShaderError((<object> error).decode("utf-8"))
glDeleteShader(vertex)
glDeleteShader(fragment)
self.program = program
self.find_variables(self.vertex)
self.find_variables(self.fragment)
def missing(self, kind, name):
raise Exception("Shader {} has not been given {} {}.".format(self.name, kind, name))
def start(self):
glUseProgram(self.program)
def set_uniform(self, name, value):
cdef Uniform u
u = self.uniforms.get(name, None)
if u is None:
return
u.assign(value)
u.ready = True
def set_uniforms(self, dict uniforms):
cdef Uniform u
for name, value in uniforms.iteritems():
u = self.uniforms.get(name, None)
if u is None:
continue
u.assign(value)
u.ready = True
def draw(self, Mesh mesh):
cdef Attribute a
# Set up the attributes.
for a in self.attributes:
offset = mesh.attributes.get(a.name, None)
if offset is None:
self.missing("mesh attribute", a.ame)
glVertexAttribPointer(a.location, a.size, GL_FLOAT, GL_FALSE, mesh.stride * sizeof(float), mesh.get_data(offset))
glEnableVertexAttribArray(a.location)
if mesh.polygon_points == 3:
glDrawArrays(GL_TRIANGLES, 0, 3 * mesh.polygon_count)
return
cdef int i = 0
cdef Polygon p
for p in mesh.polygons:
glDrawArrays(GL_TRIANGLE_FAN, i, p.points)
i += p.points
def finish(self):
cdef Attribute a
cdef Uniform u
for a in self.attributes:
glDisableVertexAttribArray(a.location)
for u in self.uniforms.itervalues():
u.finish()
+326
View File
@@ -0,0 +1,326 @@
import re
import io
import os
import renpy.display
# A map from shader part name to ShaderPart
shader_part = { }
def register_shader(name, **kwargs):
"""
This registers a part of a shader. Shader parts have a name, and then
`name`
A string giving the name of the shader part. Names starting with an
underscore or "renpy." are reserved for Ren'Py.
`variables`
The variables used by the shader part. These should be listed one per
line, a storage (uniform, attribute, or varing) followed by a type,
name, and semicolon. For example::
variables='''
uniform sampler2D uTex0;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
'''
`vertex_functions`
If given, a string containing functions that will be included in the
vertex shader.
`fragment_functions`
If given, a string containing functions that will be included in the
fragment shader.
Other keyword arguments should start with ``vertex_`` or ``fragment_``,
and end with an integer priority. So "fragment_120" or "vertex_30". These
give text that's placed in the appropriate shader at the given priority,
with lower priority numbers inserted before higher priority numbers.
"""
ShaderPart(name, **kwargs)
class ShaderPart(object):
"""
Arguments are as for register_shader.
"""
def __init__(self, name, variables="", vertex_functions="", fragment_functions="", **kwargs):
if not re.match(r'^[\w\.]+$', name):
raise Exception("The shader name {!r} contains an invalid character. Shader names are limited to ASCII alphanumeric characters, _, and .".format(name))
self.name = name
shader_part[name] = self
self.vertex_functions = vertex_functions
self.fragment_functions = fragment_functions
# A list of priority, text pairs for each section of the vertex and fragment shaders.
self.vertex_parts = [ ]
self.fragment_parts = [ ]
# Sets of (storage, type, name) tuples, where storage is one of 'uniform', 'attribute', or 'varying',
self.vertex_variables = set()
self.fragment_variables = set()
# A sets of variable names used in the vertex and fragments shader.
vertex_used = set()
fragment_used = set()
for k, v in kwargs.iteritems():
shader, _, priority = k.partition('_')
if not priority:
# Trigger error handling.
shader = None
try:
priority = int(priority)
except:
shader = None
if shader == "vertex":
parts = self.vertex_parts
used = vertex_used
elif shader == "fragment":
parts = self.fragment_parts
used = fragment_used
else:
raise Exception("Keyword arguments to ShaderPart must be of the form {vertex,fragment}_{priority}.")
parts.append((priority, v))
for m in re.finditer(r'\b\w+\b', v):
used.add(m.group(0))
for l in variables.split("\n"):
l = l.strip(' ;')
a = l.split()
if not a:
continue
if len(a) != 3:
print("Unknown shader variable line {!r}. Only the form '{{uniform,attribute,vertex}} {{type}} {{name}} is allowed.".format(l))
a = tuple(a)
name = a[2]
if name in vertex_used:
self.vertex_variables.add(a)
if name in fragment_used:
self.fragment_variables.add(a)
# A map from a tuple giving the parts that comprise a shader, to the Shader
# object. The same shader might appear multiple times, to optimize performance.
cache = { }
def source(variables, parts, functions, fragment, gles):
"""
Given lists of variables and parts, converts them into textual source
code for a shader.
`fragment`
Should be set to true to generate the code for a fragment shader.
"""
rv = [ ]
if gles:
rv.append("""
#version 100 es
""")
if fragment:
rv.append("""
precision mediump float;
""")
else:
rv.append("""
#version 120
""")
rv.extend(functions)
for storage, type_, name in sorted(variables):
rv.append("{} {} {};\n".format(storage, type_, name))
rv.append("\nvoid main() {\n")
parts.sort()
for _, part in parts:
rv.append(part)
rv.append("}\n")
return "".join(rv)
class ShaderCache(object):
"""
This class caches shaders that were compiled. It's also responsible for
recording shaders that have been used, persisting them to disk, and then
loading the shaders back into the cache.
"""
def __init__(self, filename, gles):
# The filename that we'll load the list of shaders from, and
# persist it to.
self.filename = filename
# Are we gles?
self.gles = gles
# A map from tuples of partnames to the shaders that have been
# created.
self.cache = { }
# A set of tuples of partnames corresponding to shaders that existed
# in the past, but do not exist now.
self.missing = set()
def get(self, partnames):
"""
Gets a shader, creating it if necessary.
`partnames`
A tuple of strings, giving the names of the shader parts to include in
the cache.
"""
rv = self.cache.get(partnames, None)
if rv is not None:
return rv
partnameset = set(partnames)
partnameset.add(renpy.config.default_shader)
sortedpartnames = tuple(sorted(partnameset))
rv = self.cache.get(sortedpartnames, None)
if rv is not None:
self.cache[partnames] = rv
return rv
# If the cache missed entirely, we have to generate the source code for the
# shaders.
vertex_variables = set()
vertex_parts = [ ]
vertex_functions = [ ]
fragment_variables = set()
fragment_parts = [ ]
fragment_functions = [ ]
for i in sortedpartnames:
p = shader_part.get(i, None)
if p is None:
raise Exception("{!r} is not a known shader part.".format(i))
vertex_variables |= p.vertex_variables
vertex_parts.extend(p.vertex_parts)
vertex_functions.append(p.vertex_functions)
fragment_variables |= p.fragment_variables
fragment_parts.extend(p.fragment_parts)
fragment_functions.append(p.fragment_functions)
vertex = source(vertex_variables, vertex_parts, vertex_functions, False, self.gles)
fragment = source(fragment_variables, fragment_parts, fragment_functions, True, self.gles)
from renpy.gl2.gl2shader import Program
rv = Program(sortedpartnames, vertex, fragment)
rv.load()
self.cache[partnames] = rv
self.cache[sortedpartnames] = rv
return rv
def check(self, partnames):
"""
Returns true if every part in partnames is a known part, or False
otherwise.
"""
for i in partnames:
if i not in shader_part:
return False
return True
def save(self):
"""
Saves the list of shaders to the file.
"""
fn = "<unknown>"
try:
fn = renpy.loader.get_path(self.filename)
tmp = fn + ".tmp"
with io.open(tmp, "w", encoding="utf-8") as f:
shaders = set(self.cache.keys()) | self.missing
for i in shaders:
f.write(u" ".join(i) + "\r\n")
try:
os.unlink(fn)
except:
pass
os.rename(tmp, fn)
except:
renpy.display.log.write("Saving shaders to {!r}:".format(fn))
renpy.display.log.exception()
def load(self):
"""
Loads the list of shaders from the file, and compiles all shaders
for which the parts exist, and for which compilation can succeed.
"""
try:
f = renpy.loader.load(self.filename)
except:
renpy.display.log.write("Could not open {!r}:".format(self.filename))
return
for l in f:
l = l.strip()
partnames = tuple(l.strip().split())
if not partnames:
continue
if not self.check(partnames):
self.missing.add(partnames)
continue
try:
self.get(partnames)
except:
renpy.display.log.write("Precompiling shader {!r}:".format(partnames))
renpy.display.log.exception()
self.missing.add(partnames)
f.close()
+45 -74
View File
@@ -19,85 +19,56 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cimport renpy.display.render as render
from gl2draw cimport Environ
from uguugl cimport *
from renpy.gl2.gl2shader cimport Program
from renpy.gl2.gl2geometry cimport Mesh
from renpy.gl2.gl2draw cimport GL2Draw
cdef class TextureCore:
cdef class TextureLoader:
# The draw object associated with this TextureLoader
cdef GL2Draw draw
# All the texture number currently allocated by this loader.
cdef set allocated
# A list of (number, generation) pairs for textures that need to be freed.
cdef list free_list
# The total size (in bytes) of all the textures that have been allocated
# but not deallocated.
cdef int total_texture_size
# The program used for fast texture loading
cdef Program ftl_program
# The queue of textures that need to be loaded.
cdef object texture_load_queue
# The maximum size of a texture.
cdef GLint max_texture_width
cdef GLint max_texture_height
cdef class GLTexture:
# The size of the texture, in pixels.
cdef public int width
cdef public int height
cdef public int generation
# The number of the texture in OpenGL.
cdef public unsigned int number
cdef bint loaded
cdef double xmul
cdef double xadd
cdef double ymul
cdef double yadd
cdef object premult
cdef tuple premult_size
cdef int premult_left
cdef int premult_right
cdef int premult_top
cdef int premult_bottom
cdef bint nearest
cdef public list free_list
cdef void make_ready(TextureCore)
cdef void make_nearest(TextureCore)
cdef void make_linear(TextureCore)
cpdef int allocate(TextureCore)
# Has this texture been loaded yet?
cdef public bint loaded
cdef public object debug
# If we are doing in-place loading, this is the data that's used for
# that.
cdef object surface
cdef class TextureGrid:
# If we're not doing in-place loading, this is the data that's used for
# that.
cdef unsigned char *data
cdef object __weakref__
cdef public int width
cdef public int height
cdef list rows
cdef list columns
cdef list tiles # list of lists.
cdef public TextureGrid half_cache
cpdef void make_ready(self, bint nearest)
cdef public object debug
cdef public bint ready
cpdef blit(
TextureGrid tg,
double sx,
double sy,
render.Matrix2D transform,
double alpha,
double over,
Environ environ,
bint nearest)
cpdef blend(
TextureGrid tg0,
TextureGrid tg1,
double sx,
double sy,
render.Matrix2D transform,
double alpha,
double over,
double fraction,
Environ environ,
bint nearest)
cpdef imageblend(
TextureGrid tg0,
TextureGrid tg1,
TextureGrid tg2,
double sx,
double sy,
render.Matrix2D transform,
double alpha,
double over,
double fraction,
int ramp,
Environ environ,
bint nearest)
# The texture loader associated with this texture.
cdef TextureLoader loader
+464 -1305
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -38,7 +38,7 @@ from libc.stdlib cimport calloc, free
from cpython.buffer cimport PyObject_GetBuffer, PyBuffer_Release, PyBUF_CONTIG, PyBUF_CONTIG_RO
cimport uguugl
from uguugl import reset_error, get_error, enable_check_error, load
from uguugl import load
cdef object proxy_return_string(const GLubyte *s):
"""
+273 -1300
View File
File diff suppressed because it is too large Load Diff
+15 -6
View File
@@ -734,14 +734,23 @@ class RenpyImporter(object):
if filename.endswith("__init__.py"):
mod.__path__ = [ filename[:-len("__init__.py")] ]
source = load(filename).read().decode("utf8")
if source and source[0] == u'\ufeff':
source = source[1:]
source = source.encode("raw_unicode_escape")
for encoding in [ "utf-8", "latin-1" ]:
source = source.replace("\r", "")
try:
source = load(filename).read().decode(encoding)
if source and source[0] == u'\ufeff':
source = source[1:]
source = source.encode("raw_unicode_escape")
source = source.replace("\r", "")
code = compile(source, filename, 'exec', renpy.python.old_compile_flags, 1)
break
except:
if encoding == "latin-1":
raise
code = compile(source, filename, 'exec', renpy.python.old_compile_flags, 1)
exec code in mod.__dict__
return sys.modules[fullname]
+14 -5
View File
@@ -439,6 +439,8 @@ def save(slotname, extra_info='', mutate_flag=False):
location.scan()
clear_slot(slotname)
# The thread used for autosave.
autosave_thread = None
# Flag that lets us know if an autosave is in progress.
autosave_not_running = threading.Event()
@@ -448,7 +450,7 @@ autosave_not_running.set()
autosave_counter = 0
def autosave_thread(take_screenshot):
def autosave_thread_function(take_screenshot):
global autosave_counter
@@ -521,6 +523,8 @@ def force_autosave(take_screenshot=False, block=False):
If True, blocks until the autosave completes.
"""
global autosave_thread
if renpy.game.after_rollback or renpy.exports.in_rollback():
return
@@ -528,6 +532,11 @@ def force_autosave(take_screenshot=False, block=False):
if not autosave_not_running.isSet():
return
# Join the autosave thread to clear resources.
if autosave_thread is not None:
autosave_thread.join()
autosave_thread = None
# Do not save if we're in the main menu.
if renpy.store.main_menu:
return
@@ -555,12 +564,12 @@ def force_autosave(take_screenshot=False, block=False):
autosave_not_running.clear()
if not renpy.emscripten:
t = threading.Thread(target=autosave_thread, args=(take_screenshot,))
t.daemon = True
t.start()
autosave_thread = threading.Thread(target=autosave_thread_function, args=(take_screenshot,))
autosave_thread.daemon = True
autosave_thread.start()
else:
import emscripten
emscripten.async_call(autosave_thread, take_screenshot, -1)
emscripten.async_call(autosave_thread_function, take_screenshot, -1)
################################################################################
+49 -4
View File
@@ -228,11 +228,56 @@ def choose_variants():
renpy.config.variants.insert(0, 'phone')
renpy.config.variants.insert(0, 'small')
else:
if renpy.emscripten:
renpy.config.variants.insert(0, 'web')
elif renpy.emscripten:
import emscripten, re
# web
renpy.config.variants.insert(0, 'web')
# mobile
userAgent = emscripten.run_script_string(r'''navigator.userAgent''')
mobile = re.search('Mobile|Android|iPad|iPhone', userAgent)
if mobile:
renpy.config.variants.insert(0, 'mobile')
# Reserve android/ios for when the OS API is exposed
#if re.search('Android', userAgent):
# renpy.config.variants.insert(0, 'android')
#if re.search('iPad|iPhone', userAgent):
# renpy.config.variants.insert(0, 'ios')
# touch
touch = emscripten.run_script_int(r'''
('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0)''')
if touch == 1:
# mitigate hybrids (e.g. ms surface) by restricting touch to mobile
if mobile:
renpy.config.variants.insert(0, 'touch')
# large/medium/small
# tablet/phone
# screen.width/height is auto-adjusted by browser,
# so it can be used as a physical sizereference
# (see also window.devicePixelRatio)
# e.g. Galaxy S5:
# - physical / OpenGL: 1080x1920
# - web screen: 360x640 w/ devicePixelRatio=3
ref_width = emscripten.run_script_int(r'''screen.width''')
ref_height = emscripten.run_script_int(r'''screen.height''')
# medium reference point: ipad 1024x768, ipad pro 1336x1024 (browser "pixels")
if mobile:
if (ref_width < 768 or ref_height < 768):
renpy.config.variants.insert(0, 'small')
renpy.config.variants.insert(0, 'phone')
else:
renpy.config.variants.insert(0, 'medium')
renpy.config.variants.insert(0, 'tablet')
else:
renpy.config.variants.insert(0, 'pc')
renpy.config.variants.insert(0, 'large')
else:
renpy.config.variants.insert(0, 'pc')
renpy.config.variants.insert(0, 'large')
+3
View File
@@ -1970,6 +1970,9 @@ def menu_statement(l, loc):
rv.extend(menu)
for i in rv:
i.statement_start = rv[0]
return rv
+3
View File
@@ -53,6 +53,9 @@ pure_functions = {
"_",
"_p",
"absolute",
"__renpy__list__",
"__renpy__dict__",
"__renpy__set__",
# defaultstore.py
"ImageReference", "Image", "Frame", "Solid", "LiveComposite", "LiveCrop",
+9 -5
View File
@@ -1646,9 +1646,9 @@ class RollbackLog(renpy.object.Object):
fwd_name, fwd_data = self.forward[0]
if (self.current.context.current == fwd_name
and data == fwd_data
and (keep_rollback or self.rolled_forward)
):
and data == fwd_data
and (keep_rollback or self.rolled_forward)
):
self.forward.pop(0)
else:
del self.forward[:]
@@ -1705,7 +1705,11 @@ class RollbackLog(renpy.object.Object):
This is called to try to recover when rollback fails.
"""
if not renpy.config.load_failed_label:
lfl = renpy.config.load_failed_label
if callable(lfl):
lfl = lfl()
if not lfl:
raise Exception("Couldn't find a place to stop rolling back. Perhaps the script changed in an incompatible way?")
rb = self.log.pop()
@@ -1715,7 +1719,7 @@ class RollbackLog(renpy.object.Object):
renpy.exports.pop_call()
renpy.game.contexts[0].force_checkpoint = True
renpy.game.contexts[0].goto_label(renpy.config.load_failed_label)
renpy.game.contexts[0].goto_label(lfl)
raise renpy.game.RestartTopContext()
+4 -1
View File
@@ -418,7 +418,7 @@ many = renpy.object.Sentinel("many")
def register_sl_displayable(*args, **kwargs):
"""
:doc: custom_sl class
:args: (name, displayable, style, nchildren=0, scope=False, replaces=False, default_keywords={})
:args: (name, displayable, style, nchildren=0, scope=False, replaces=False, default_keywords={}, default_properties=True)
Registers a screen language statement that creates a displayable.
@@ -464,6 +464,9 @@ def register_sl_displayable(*args, **kwargs):
`default_keywords`
The default set of keyword arguments to supply to the displayable.
`default_properties`
If true, the ui and position properties are added by default.
Returns an object that can have positional arguments and properties
added to it by calling the following methods. Each of these methods
returns the object it is called on, allowing methods to be chained
+3 -9
View File
@@ -39,6 +39,9 @@ position_property_names = [
"maximum",
"xmaximum",
"ymaximum",
"minimum",
"xminimum",
"yminimum",
"area",
"clipping",
"xfill",
@@ -93,9 +96,6 @@ text_property_names = [
"text_align",
"text_y_fudge",
"underline",
"minimum",
"xminimum",
"yminimum",
"hinting",
"adjust_spacing",
]
@@ -121,9 +121,6 @@ window_properties = [ Style(i) for i in [
"ypadding",
"padding",
"size_group",
"minimum",
"xminimum",
"yminimum",
] ]
button_properties = [ Style(i) for i in [
@@ -182,9 +179,6 @@ box_property_names = [
"fit_first",
"xfit",
"yfit",
"minimum",
"xminimum",
"yminimum",
]
box_properties = [ Style(i) for i in box_property_names ]
+5 -1
View File
@@ -250,6 +250,10 @@ def substitute(s, scope=None, force=False, translate=True):
else:
kwargs = renpy.store.__dict__ # @UndefinedVariable
s = formatter.vformat(s, (), kwargs)
try:
s = formatter.vformat(s, (), kwargs)
except:
if renpy.display.predict.predicting: # @UndefinedVariable
return " ", True
return s, (s != old_s)
+8 -1
View File
@@ -302,7 +302,14 @@ class TextSegment(object):
tf = font_func(tf)
if not isinstance(tf, font.FontGroup):
yield (self, s)
if self.font is tf:
yield (self , s)
else:
seg = TextSegment(self)
seg.font = tf
yield (seg, s)
return
segs = { }
+6 -3
View File
@@ -75,6 +75,8 @@ class BarValue(renpy.object.Object):
# Alt text.
alt = "Bar"
force_step = False
def replaces(self, other):
return
@@ -817,8 +819,6 @@ def menu(menuitems,
default=False,
**properties):
# menu is now a conglomeration of other widgets. And bully for it.
renpy.ui.vbox(style=style, **properties)
for label, val in menuitems:
@@ -829,7 +829,10 @@ def menu(menuitems,
text = choice_style
button = choice_button_style
clicked = ChoiceReturn(label, val, location)
if isinstance(val, ChoiceReturn):
clicked = val
else:
clicked = ChoiceReturn(label, val, location)
if clicked.get_chosen():
text = choice_chosen_style
-1499
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
set -e
cd "$(dirname $0)/.."
pushd notarized/in
rm -Rf /tmp/notarize
mkdir -p /tmp/notarize
tar xjf renpy.tar.bz2 -C /tmp/notarize
popd
pushd /tmp/notarize
mv renpy-*-sdk/renpy.app .
rm -Rf renpy-*-sdk
codesign --verify --verbose renpy.app
zip -r renpy.app.zip renpy.app
xcrun altool --asc-provider XHTE5H7Z79 -u tom@rothamel.us -p "@keychain:altool" \
--notarize-app \
--primary-bundle-id org.renpy.renpy \
-f renpy.app.zip
popd
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
set -e
cd "$(dirname $0)/.."
pushd /tmp/notarize
result="$(xcrun altool --notarization-history 0 --asc-provider XHTE5H7Z79 -u tom@rothamel.us -p '@keychain:altool' | head -6 | tail -1)"
echo $result
status="$(echo $result | cut -d ' ' -f 5)"
if [ $status = "success" ]; then
echo "Success... now stapling."
else
exit
fi
xcrun stapler staple -v renpy.app
popd
rm -Rf notarized/out/renpy.app || true
cp -a /tmp/notarize/renpy.app notarized/out/
echo "Done."
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
dmg="/Users/tom/magnetic/ab/website/renpy/dl/$1/renpy-$1-sdk.dmg"
if [ ! -e "$dmg" ]; then
echo "$dmg" not found.
exit 1
else
echo "$dmg" found.
fi
xcrun altool --asc-provider XHTE5H7Z79 -u tom@rothamel.us -p "@keychain:altool" \
--notarize-app \
--primary-bundle-id org.renpy.renpy.dmg \
-f "$dmg"
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
dmg="/Users/tom/magnetic/ab/website/renpy/dl/$1/renpy-$1-sdk.dmg"
if [ ! -e "$dmg" ]; then
echo "$dmg" not found.
exit 1
else
echo "$dmg" found.
fi
xcrun stapler staple --verbose "$dmg"

Some files were not shown because too many files have changed in this diff Show More