Compare commits
325 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d337e47e1f | |||
| d10d81647b | |||
| e7708c23fb | |||
| cc45453f4a | |||
| 4dba39c57a | |||
| c570d96458 | |||
| 0888c1ae1c | |||
| 9222ca20c6 | |||
| 4ad03d0f98 | |||
| de33e805d4 | |||
| dab57aeec3 | |||
| 82caa0a66a | |||
| fe3c6908ea | |||
| 0c39b34c79 | |||
| 05b35745a1 | |||
| 77c04ac67d | |||
| bb4154c25f | |||
| d8ad65e912 | |||
| be678bd4ba | |||
| e12c00ee6d | |||
| b22c4c1379 | |||
| 3a58834de0 | |||
| d2b96c0eab | |||
| b3b4036955 | |||
| 544dde6d5c | |||
| 3002a1b019 | |||
| 08a621fdcf | |||
| 3ba9569ace | |||
| 166807fc76 | |||
| 628a09d187 | |||
| e4348c52d6 | |||
| 8f62aac732 | |||
| 9f0a05fc6b | |||
| d492249835 | |||
| dcfc23d2f3 | |||
| ad5e11c710 | |||
| 5354bb0762 | |||
| f721662ef9 | |||
| c6641e53fe | |||
| b97b29d894 | |||
| 32eb7be5a1 | |||
| 0b8bf87eaf | |||
| 857d776538 | |||
| df51f5233f | |||
| aed2625486 | |||
| 47271b7af9 | |||
| d8050e9f5d | |||
| b639171118 | |||
| 25e66d3150 | |||
| dcaa97a70e | |||
| e0fd8dc4d5 | |||
| 2cc4bdd407 | |||
| 8ccfb9abc7 | |||
| 4734fb62f4 | |||
| 8353120f55 | |||
| 5a081b6dc7 | |||
| 52cdcb699a | |||
| e190596f3d | |||
| 9352339f5b | |||
| 5c315d02d1 | |||
| 86a95abb60 | |||
| cc03f54738 | |||
| f40ff85436 | |||
| 3c6918c36f | |||
| 754aec0507 | |||
| 9f800275ad | |||
| cf05311158 | |||
| 644d24dcc4 | |||
| 87f076e78c | |||
| b0cc98cea2 | |||
| 498ea82ec9 | |||
| e587790021 | |||
| bbaf654ef9 | |||
| 44b5af95ff | |||
| 5470f86c39 | |||
| 8e04c766de | |||
| 82b396d424 | |||
| a0dad58dbf | |||
| d28ecb39ff | |||
| 6597edcada | |||
| 5c44e738e6 | |||
| 7e295d67b5 | |||
| 61eb81c79d | |||
| df32d31113 | |||
| 59587015ba | |||
| 312c49f194 | |||
| 7c75a79c13 | |||
| e6546189eb | |||
| 9500f658a3 | |||
| 6d9bcff1f6 | |||
| 3a352af1e5 | |||
| 87b323a3d4 | |||
| 1bf97965b8 | |||
| eafcbf572f | |||
| 427a1542d8 | |||
| ee53f232aa | |||
| 7c03642cb5 | |||
| 4347c89c15 | |||
| 9783a0a733 | |||
| e5252af42b | |||
| 094009fa3a | |||
| 6471986ea9 | |||
| ab97732865 | |||
| 4f90718eaa | |||
| 2aa796d418 | |||
| e62ee21922 | |||
| 218cb677cd | |||
| fe0e129ad3 | |||
| 630ed80eb5 | |||
| 2b5d40053d | |||
| 8728fd137a | |||
| 7b34ec1ba3 | |||
| 3f785ee366 | |||
| 1f9401a602 | |||
| 91a0677f67 | |||
| 1392fb9239 | |||
| ac902ee733 | |||
| 6031917511 | |||
| 0e6ad371fe | |||
| ead5c63eac | |||
| 18827a8ab3 | |||
| 8c81811177 | |||
| ce634662bd | |||
| 13c32894cf | |||
| 26e0f1617e | |||
| 6754d50c12 | |||
| ebe8264b1d | |||
| 5c9a1066e3 | |||
| 07a6f3dcbd | |||
| 3441e0e7f0 | |||
| 3bd9055c5f | |||
| 2941031b2a | |||
| 627b18a8e8 | |||
| 681d3a5772 | |||
| 39be517a0a | |||
| 8916575921 | |||
| 5288ff876d | |||
| 067d98cdf0 | |||
| 65957b3236 | |||
| 726e535f1b | |||
| 095a253bf8 | |||
| d3c36b843e | |||
| b5707d7091 | |||
| 024b0aef81 | |||
| 365913c267 | |||
| a2b8842206 | |||
| dc51db06b1 | |||
| c590c4f956 | |||
| 3871803088 | |||
| 40ec6f87ef | |||
| 6339c0dfef | |||
| 510b7cb909 | |||
| 3a011ca2c6 | |||
| d12fe12c2b | |||
| 37a128a470 | |||
| 9e8b074a16 | |||
| 3147e71c45 | |||
| 60d413f96f | |||
| 5029e6d078 | |||
| ef167433f4 | |||
| aac1f4c421 | |||
| 0ab0856e37 | |||
| 760503b124 | |||
| d16e3eea75 | |||
| 09fda5505e | |||
| 5d4bb738ef | |||
| 204a48f577 | |||
| 8889ab487d | |||
| 06684d692a | |||
| 5f4e6d4ba1 | |||
| 200af0d584 | |||
| e78828fa3b | |||
| 7e602c256e | |||
| 1a8154c78a | |||
| 2d7460bafd | |||
| ae998912f5 | |||
| e8cd034b7a | |||
| 900e291cdc | |||
| cb98d18aa9 | |||
| ef4c5aff9e | |||
| 6cdcb5dfbc | |||
| 797ef0fc25 | |||
| c132df21ca | |||
| 6ab2df60f4 | |||
| 101cb8c348 | |||
| 2f53ddc03e | |||
| 8a042fe187 | |||
| f9a61f7567 | |||
| 79c965ba5d | |||
| 340db775c5 | |||
| d1224c0707 | |||
| afeb06c1d9 | |||
| afad82b082 | |||
| 2f683b2d69 | |||
| d8ec4503a6 | |||
| bb207a7fce | |||
| 629ea6f13f | |||
| 2a78e0141f | |||
| 8da9eafd28 | |||
| 1f8e1cacf6 | |||
| 048f8fea8f | |||
| 229bd5a82d | |||
| 12405a22d9 | |||
| 3529d40ba0 | |||
| 02be00bb78 | |||
| 189e7d8e4f | |||
| 0956519365 | |||
| efbd897832 | |||
| c6d9b6e0c1 | |||
| d0bbc81ebf | |||
| 662e8227ab | |||
| 33e3174692 | |||
| dde68dd735 | |||
| 5accb1c3e4 | |||
| 77d8b9b318 | |||
| 202fc47419 | |||
| 49c0ec0504 | |||
| bc437b5e9b | |||
| b4dfcc44cf | |||
| 1a98502bdc | |||
| 977726c3ab | |||
| ed9cbfd21c | |||
| 261a0b807d | |||
| 261718967a | |||
| 3e5e77b400 | |||
| 7fa4265f15 | |||
| 995823fbcf | |||
| 663961581f | |||
| d32946ce53 | |||
| 6bd9523780 | |||
| b0b49b353e | |||
| b1b943b456 | |||
| dd5326c205 | |||
| e62cc921c7 | |||
| d343617616 | |||
| 0cf72637b2 | |||
| e9312a3604 | |||
| 39420b940a | |||
| d8655be2c5 | |||
| c276ac8ec8 | |||
| 37e7060baa | |||
| b155fa9411 | |||
| 4ef1e05dc8 | |||
| da6e5e2c73 | |||
| 95c1cd5ad8 | |||
| f8747f6e38 | |||
| 450aeb55dc | |||
| 878e2284f8 | |||
| a4291ccddb | |||
| 2091161241 | |||
| c8ac8b511f | |||
| ed3182c74c | |||
| 82b0d59f19 | |||
| 836238de5c | |||
| 60b42fd79b | |||
| ba2c72eaac | |||
| 7751d5603e | |||
| 6e8d622282 | |||
| 30b3366168 | |||
| 736d9487c5 | |||
| 7bf7af52b1 | |||
| be7daf39d5 | |||
| 0faf7810f6 | |||
| bd333803b2 | |||
| f24e3f199e | |||
| 13a755ae93 | |||
| a907cfaa2e | |||
| b236453972 | |||
| 95fe225a8c | |||
| 621fbe9449 | |||
| f6029db185 | |||
| 38f8e1f16f | |||
| ca95350b70 | |||
| 985b54d284 | |||
| 5664e29c7d | |||
| ae61f801f8 | |||
| cac70f9544 | |||
| 45b792b4bd | |||
| 97f578baac | |||
| 2d9c8565be | |||
| a4596ecfbc | |||
| 740400db2c | |||
| b1c6a3a16d | |||
| 0f90a189f3 | |||
| d427968170 | |||
| 673b1fcb4a | |||
| 9a6f7f3975 | |||
| af5e885e17 | |||
| af0314885b | |||
| e76411a6c9 | |||
| bf9b60aa59 | |||
| d9aa172882 | |||
| 79a67705f1 | |||
| df8f27f0d0 | |||
| d85241bfa7 | |||
| c5cf467274 | |||
| 641a1b4f99 | |||
| 01a0b6ed42 | |||
| 8c36a9a4a5 | |||
| bfeb11a7be | |||
| f937f4bf42 | |||
| ba9accfd5e | |||
| 0ca1f3ef66 | |||
| d19148f282 | |||
| 72d7b9ba59 | |||
| 6231ffe2f3 | |||
| 097c24b140 | |||
| c49f697b4d | |||
| 8fc5225985 | |||
| ac60c3ec9e | |||
| 7d655595e0 | |||
| 6165d4aa11 | |||
| 5e717b9bbe | |||
| 90b2f69876 | |||
| adbb05c99d | |||
| ce2b11e7cf | |||
| ae4369d223 | |||
| 9a2cf214dc | |||
| 2ac606980a | |||
| 669a25d530 | |||
| f89de7e332 | |||
| 2f12cdf843 | |||
| c0ed44651c | |||
| ecbe0952c8 | |||
| e48f24d8d1 |
@@ -82,3 +82,5 @@ sphinx/source/inc
|
||||
sphinx/source/thequestion.rst
|
||||
tutorial/game/tutorial_director.rpy
|
||||
launcher/theme
|
||||
module/emscripten-static/
|
||||
web/
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
@@ -14,6 +14,7 @@ SOURCE = [
|
||||
"/home/tom/ab/ripe/renios",
|
||||
"/home/tom/ab/renpy-deps",
|
||||
"/home/tom/ab/pygame_sdl2",
|
||||
"/home/tom/ab/renpyweb",
|
||||
]
|
||||
|
||||
version = ".".join(str(i) for i in version_tuple)
|
||||
|
||||
+11
-2
@@ -9,6 +9,7 @@ import compileall
|
||||
import shutil
|
||||
import subprocess
|
||||
import argparse
|
||||
import time
|
||||
|
||||
if not sys.flags.optimize:
|
||||
raise Exception("Optimization disabled.")
|
||||
@@ -43,6 +44,8 @@ def copy_tutorial_file(src, dest):
|
||||
|
||||
def main():
|
||||
|
||||
start = time.time()
|
||||
|
||||
if not sys.flags.optimize:
|
||||
raise Exception("Not running with python optimization.")
|
||||
|
||||
@@ -54,6 +57,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 +179,12 @@ def main():
|
||||
destination,
|
||||
]
|
||||
|
||||
if args.notarized:
|
||||
cmd.extend([
|
||||
"--macapp",
|
||||
"notarized/out",
|
||||
])
|
||||
|
||||
print()
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
@@ -250,8 +260,7 @@ 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.")
|
||||
print("Distribute took {:.0f} seconds.".format(time.time() - start))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -190,11 +190,12 @@ init -1 python:
|
||||
interface.processing(self.info_msg, show_screen=True, cancel=cancel_action)
|
||||
|
||||
kwargs = { }
|
||||
if yes:
|
||||
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
|
||||
if not yes:
|
||||
self.process.stdin.close()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc(file=f)
|
||||
@@ -215,6 +216,7 @@ init -1 python:
|
||||
if yes and self.yes_thread:
|
||||
self.run_yes = False
|
||||
self.yes_thread.join()
|
||||
self.process.stdin.close()
|
||||
|
||||
self.process = None
|
||||
self.yes_thread = None
|
||||
|
||||
@@ -760,6 +760,10 @@
|
||||
old "Clipboard"
|
||||
new "클립보드"
|
||||
|
||||
# 00accessibility.rpy:146
|
||||
old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was."
|
||||
new "이 메뉴의 옵션은 접근성을 향상시키기 위한 것입니다. 모든 게임에서 작동하지 않을 수 있고 일부 옵션 조합은 게임을 플레이할 수 없게 만들 수 있으며 이러한 경우 게임이나 엔진의 문제가 아닙니다. 글꼴을 변경할 때 최상의 결과를 얻으려면 글자 크기를 원래대로 유지하십시오."
|
||||
|
||||
# 00preferences.rpy:430
|
||||
old "font transform"
|
||||
new "글꼴 변형"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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."
|
||||
@@ -1525,8 +1525,12 @@
|
||||
new "빌드 및 브라우저에서 열기"
|
||||
|
||||
# web.rpy:150
|
||||
old "Open without Build"
|
||||
new "빌드하지 않고 열기"
|
||||
old "Open in Browser"
|
||||
new "브라우저에서 열기"
|
||||
|
||||
# web.rpy:151
|
||||
old "Open build directory"
|
||||
new "빌드 디렉토리 열기"
|
||||
|
||||
# web.rpy:154
|
||||
old "Support:"
|
||||
@@ -1538,7 +1542,7 @@
|
||||
|
||||
# web.rpy:163
|
||||
old "Beuc's Patreon"
|
||||
new "Beuc의 페트론"
|
||||
new "Beuc의 페이트리온"
|
||||
|
||||
# web.rpy:181
|
||||
old "Ren'Py web applications require the entire game to be downloaded to the player's computer before it can start."
|
||||
@@ -1550,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"
|
||||
|
||||
@@ -1,55 +1,103 @@
|
||||
|
||||
translate piglatin strings:
|
||||
|
||||
# renpy/common/00accessibility.rpy:83
|
||||
# renpy/common/00accessibility.rpy:28
|
||||
old "Self-voicing disabled."
|
||||
new "Elfsay-oicingvay isabledday."
|
||||
|
||||
# renpy/common/00accessibility.rpy:29
|
||||
old "Clipboard voicing enabled. "
|
||||
new "Lipboardcay oicingvay enableday. "
|
||||
|
||||
# renpy/common/00accessibility.rpy:30
|
||||
old "Self-voicing enabled. "
|
||||
new "Elfsay-oicingvay enableday. "
|
||||
|
||||
# renpy/common/00accessibility.rpy:32
|
||||
old "bar"
|
||||
new "arbay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:33
|
||||
old "selected"
|
||||
new "electedsay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:34
|
||||
old "viewport"
|
||||
new "iewportvay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:35
|
||||
old "horizontal scroll"
|
||||
new "orizontalhay crollsay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:36
|
||||
old "vertical scroll"
|
||||
new "erticalvay crollsay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:37
|
||||
old "activate"
|
||||
new "activateay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:38
|
||||
old "deactivate"
|
||||
new "eactivateday"
|
||||
|
||||
# renpy/common/00accessibility.rpy:39
|
||||
old "increase"
|
||||
new "increaseay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:40
|
||||
old "decrease"
|
||||
new "ecreaseday"
|
||||
|
||||
# renpy/common/00accessibility.rpy:128
|
||||
old "Font Override"
|
||||
new "Ontfay Verrideoay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:87
|
||||
# renpy/common/00accessibility.rpy:132
|
||||
old "Default"
|
||||
new "Efaultday"
|
||||
|
||||
# renpy/common/00accessibility.rpy:91
|
||||
# renpy/common/00accessibility.rpy:136
|
||||
old "DejaVu Sans"
|
||||
new "Ejavuday Anssay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:95
|
||||
# renpy/common/00accessibility.rpy:140
|
||||
old "Opendyslexic"
|
||||
new "Pendyslexicoay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:101
|
||||
# renpy/common/00accessibility.rpy:146
|
||||
old "Text Size Scaling"
|
||||
new "Exttay Izesay Calingsay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:107
|
||||
# renpy/common/00accessibility.rpy:152
|
||||
old "Reset"
|
||||
new "Esetray"
|
||||
|
||||
# renpy/common/00accessibility.rpy:112
|
||||
# renpy/common/00accessibility.rpy:157
|
||||
old "Line Spacing Scaling"
|
||||
new "Inelay Pacingsay Calingsay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:124
|
||||
# renpy/common/00accessibility.rpy:169
|
||||
old "Self-Voicing"
|
||||
new "Elfsay-Oicingvay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:128
|
||||
# renpy/common/00accessibility.rpy:173
|
||||
old "Off"
|
||||
new "Ffoay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:132
|
||||
# renpy/common/00accessibility.rpy:177
|
||||
old "Text-to-speech"
|
||||
new "Exttay-otay-peechsay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:136
|
||||
# renpy/common/00accessibility.rpy:181
|
||||
old "Clipboard"
|
||||
new "Lipboardcay"
|
||||
|
||||
# renpy/common/00accessibility.rpy:140
|
||||
# renpy/common/00accessibility.rpy:185
|
||||
old "Debug"
|
||||
new "Ebugday"
|
||||
|
||||
# renpy/common/00accessibility.rpy:146
|
||||
# renpy/common/00accessibility.rpy:191
|
||||
old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was."
|
||||
new "Hetay optionsay onay histay enumay areay intendeday otay improveay accessibilityay. Heytay aymay otnay orkway ithway allay amesgay, anday omesay ombinationscay ofay optionsay aymay enderray hetay amegay unplayableay. Histay isay otnay anay issueay ithway hetay amegay oray engineay. Orfay hetay estbay esultsray henway angingchay ontsfay, ytray otay eepkay hetay exttay izesay hetay amesay asay itay originallyay asway."
|
||||
|
||||
@@ -397,59 +445,11 @@ translate piglatin strings:
|
||||
old "Saved screenshot as %s."
|
||||
new "Avedsay creenshotsay asay %say."
|
||||
|
||||
# renpy/common/00library.rpy:175
|
||||
old "Self-voicing disabled."
|
||||
new "Elfsay-oicingvay isabledday."
|
||||
|
||||
# renpy/common/00library.rpy:176
|
||||
old "Clipboard voicing enabled. "
|
||||
new "Lipboardcay oicingvay enableday. "
|
||||
|
||||
# renpy/common/00library.rpy:177
|
||||
old "Self-voicing enabled. "
|
||||
new "Elfsay-oicingvay enableday. "
|
||||
|
||||
# renpy/common/00library.rpy:179
|
||||
old "bar"
|
||||
new "arbay"
|
||||
|
||||
# renpy/common/00library.rpy:180
|
||||
old "selected"
|
||||
new "electedsay"
|
||||
|
||||
# renpy/common/00library.rpy:181
|
||||
old "viewport"
|
||||
new "iewportvay"
|
||||
|
||||
# renpy/common/00library.rpy:182
|
||||
old "horizontal scroll"
|
||||
new "orizontalhay crollsay"
|
||||
|
||||
# renpy/common/00library.rpy:183
|
||||
old "vertical scroll"
|
||||
new "erticalvay crollsay"
|
||||
|
||||
# renpy/common/00library.rpy:184
|
||||
old "activate"
|
||||
new "activateay"
|
||||
|
||||
# renpy/common/00library.rpy:185
|
||||
old "deactivate"
|
||||
new "eactivateday"
|
||||
|
||||
# renpy/common/00library.rpy:186
|
||||
old "increase"
|
||||
new "increaseay"
|
||||
|
||||
# renpy/common/00library.rpy:187
|
||||
old "decrease"
|
||||
new "ecreaseday"
|
||||
|
||||
# renpy/common/00library.rpy:222
|
||||
# renpy/common/00library.rpy:195
|
||||
old "Skip Mode"
|
||||
new "Kipsay Odemay"
|
||||
|
||||
# renpy/common/00library.rpy:308
|
||||
# renpy/common/00library.rpy:281
|
||||
old "This program contains free software under a number of licenses, including the MIT License and GNU Lesser General Public License. A complete list of software, including links to full source code, can be found {a=https://www.renpy.org/l/license}here{/a}."
|
||||
new "Histay rogrampay ontainscay eefray oftwaresay underay aay umbernay ofay icenseslay, includingay hetay Itmay Icenselay anday Nugay Esserlay Eneralgay Ublicpay Icenselay. Aay ompletecay istlay ofay oftwaresay, includingay inkslay otay ullfay ourcesay odecay, ancay ebay oundfay {a=https://www.renpy.org/l/license}erehay{/a}."
|
||||
|
||||
@@ -697,23 +697,23 @@ translate piglatin strings:
|
||||
old "The updates were cancelled."
|
||||
new "Hetay updatesay ereway ancelledcay."
|
||||
|
||||
# renpy/common/00gallery.rpy:589
|
||||
# renpy/common/00gallery.rpy:592
|
||||
old "Image [index] of [count] locked."
|
||||
new "Mageiay [index] ofay [count] ockedlay."
|
||||
|
||||
# renpy/common/00gallery.rpy:609
|
||||
# renpy/common/00gallery.rpy:612
|
||||
old "prev"
|
||||
new "revpay"
|
||||
|
||||
# renpy/common/00gallery.rpy:610
|
||||
# renpy/common/00gallery.rpy:613
|
||||
old "next"
|
||||
new "extnay"
|
||||
|
||||
# renpy/common/00gallery.rpy:611
|
||||
# renpy/common/00gallery.rpy:614
|
||||
old "slideshow"
|
||||
new "ideshowslay"
|
||||
|
||||
# renpy/common/00gallery.rpy:612
|
||||
# renpy/common/00gallery.rpy:615
|
||||
old "return"
|
||||
new "eturnray"
|
||||
|
||||
|
||||
@@ -876,3 +876,7 @@ translate russian strings:
|
||||
# 00preferences.rpy:441
|
||||
old "font line spacing"
|
||||
new "межстрочный интервал"
|
||||
|
||||
# renpy/common/00accessibility.rpy:191
|
||||
old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was."
|
||||
new "Параметры этого меню предназначены для увеличения доступности. Они могут работать не со всеми играми, а некоторые комбинации опций могут сделать игру отображаемой некорректно. Это не проблема с игрой или движком. Для достижения наилучших результатов при изменении шрифтов старайтесь сохранять размер текста таким, каким он был изначально."
|
||||
|
||||
@@ -1517,9 +1517,9 @@ translate russian strings:
|
||||
old "Web"
|
||||
new "Веб-инструменты"
|
||||
|
||||
# front_page.rpy:252
|
||||
old "Web (Beta)"
|
||||
new "Веб-инструменты (Бета)"
|
||||
# game/front_page.rpy:252
|
||||
old "(Beta)"
|
||||
new "(Бета)"
|
||||
|
||||
# interface.rpy:394
|
||||
old "While [what!qt], an error occured:"
|
||||
@@ -1596,3 +1596,11 @@ translate russian strings:
|
||||
# updater.rpy:198
|
||||
old "parsing the list of update channels"
|
||||
new "разбор списка каналов обновления"
|
||||
|
||||
# game/web.rpy:150
|
||||
old "Open in Browser"
|
||||
new "Открыть в браузере"
|
||||
|
||||
# game/web.rpy:151
|
||||
old "Open build directory"
|
||||
new "Открыть папку сборки"
|
||||
|
||||
@@ -158,8 +158,8 @@ translate russian strings:
|
||||
new "Помощь"
|
||||
|
||||
# screens.rpy:331
|
||||
old "## The quit button is banned on iOS and unnecessary on Android."
|
||||
new "## Кнопка выхода блокирована в iOS и не нужна на Android."
|
||||
old "## The quit button is banned on iOS and unnecessary on Android and Web."
|
||||
new "## Кнопка выхода блокирована в iOS и не нужна на Android и в веб-версии."
|
||||
|
||||
# screens.rpy:332
|
||||
old "Quit"
|
||||
@@ -672,4 +672,3 @@ translate russian strings:
|
||||
# screens.rpy:1431
|
||||
old "Menu"
|
||||
new "Меню"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# 00action_file.rpy:26
|
||||
old "{#weekday}Monday"
|
||||
@@ -735,8 +734,6 @@ translate spanish strings:
|
||||
old "Show deleted"
|
||||
new "Mostrar borrados"
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# renpy/common/00accessibility.rpy:146
|
||||
old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was."
|
||||
new "Las opciones en este menú están destinadas a mejorar la accesibilidad. Es posible que no funcionen con todos los juegos, y algunas combinaciones de opciones pueden hacer que el juego no se pueda jugar. Esto no es un problema con el juego o el motor. Para obtener los mejores resultados al cambiar las fuentes, intente mantener el tamaño del texto igual al original."
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# _developer/developer.rpym:38
|
||||
old "Developer Menu"
|
||||
@@ -189,13 +188,11 @@ translate spanish strings:
|
||||
old "jump <label>: jumps to label"
|
||||
new "jump <label>: salta a la etiqueta"
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# 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."
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# 00gltest.rpy:70
|
||||
old "Renderer"
|
||||
@@ -213,8 +212,6 @@ translate spanish strings:
|
||||
old "Copies the errors.txt file to the clipboard."
|
||||
new "Copia el archivo errors.txt al portapapeles."
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# _errorhandling.rpym:542
|
||||
old "Copy BBCode"
|
||||
new "Copiar BBCode"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# gui.rpy:2
|
||||
old "## Initialization"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# about.rpy:39
|
||||
old "[version!q]"
|
||||
@@ -1643,8 +1642,6 @@ translate spanish strings:
|
||||
old "Before packaging web apps, you'll need to download RenPyWeb, Ren'Py's web support. Would you like to download RenPyWeb now?"
|
||||
new "Antes de empaquetar aplicaciones web, deberá descargar RenPyWeb, el soporte web de Ren'Py. ¿Te gustaría descargar RenPyWeb ahora?"
|
||||
|
||||
translate spanish strings:
|
||||
|
||||
# game/web.rpy:150
|
||||
old "Open in Browser"
|
||||
new "Abrir en Navegador"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# _layout/classic_joystick_preferences.rpym:94
|
||||
old "Joystick Mapping"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
translate spanish strings:
|
||||
translate spanish strings:
|
||||
|
||||
# options.rpy:1
|
||||
old "## This file contains options that can be changed to customize your game."
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
+
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef GL2DEBUG_H
|
||||
#define GL2DEBUG_H
|
||||
|
||||
void gl2_enable_debug(void);
|
||||
|
||||
#endif
|
||||
+7
-27
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -48,3 +48,4 @@ BLIT = int
|
||||
DISSOLVE = int
|
||||
IMAGEDISSOLVE = int
|
||||
PIXELLATE = int
|
||||
FLATTEN = int
|
||||
|
||||
+13
-2
@@ -40,7 +40,7 @@ except ImportError:
|
||||
vc_version = 0
|
||||
|
||||
# The tuple giving the version number.
|
||||
version_tuple = (7, 3, 0, vc_version)
|
||||
version_tuple = (7, 3, 4, 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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+7
-1
@@ -388,7 +388,12 @@ class ATLTransformBase(renpy.object.Object):
|
||||
self.atl_st_offset = t.atl_st_offset
|
||||
|
||||
if self.child is renpy.display.motion.null:
|
||||
self.child = t.child
|
||||
|
||||
if t.child and t.child._duplicatable:
|
||||
self.child = t.child._duplicate(None)
|
||||
else:
|
||||
self.child = t.child
|
||||
|
||||
self.raw_child = t.raw_child
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
@@ -1823,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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+13
-6
@@ -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()
|
||||
@@ -1109,7 +1109,10 @@ class ADVCharacter(object):
|
||||
|
||||
# If dynamic is set, evaluate the name expression.
|
||||
if self.dynamic:
|
||||
who = renpy.python.py_eval(who)
|
||||
if callable(who):
|
||||
who = who()
|
||||
else:
|
||||
who = renpy.python.py_eval(who)
|
||||
|
||||
def sub(s, scope=None, force=False, translate=True):
|
||||
return renpy.substitutions.substitute(s, scope=scope, force=force, translate=translate)[0]
|
||||
@@ -1329,9 +1332,13 @@ def Character(name=NotSet, kind=None, **properties):
|
||||
These options help to control the display of the name.
|
||||
|
||||
`dynamic`
|
||||
If true, then `name` should be a string containing a Python
|
||||
expression. That string will be evaluated before each line
|
||||
of dialogue, and the result used as the name of the character.
|
||||
If true, then `name` should either be a string containing a Python
|
||||
expression, a function, or a callable object. If it's a string,
|
||||
That string will be evaluated before each line of dialogue, and
|
||||
the result used as the name of the character. Otherwise, the
|
||||
function or callable object will be called with no arguments
|
||||
before each line of dialogue, and the return value of the call will
|
||||
be used as the name of the character.
|
||||
|
||||
**Controlling Interactions.**
|
||||
These options control if the dialogue is displayed, if an
|
||||
|
||||
@@ -19,8 +19,53 @@
|
||||
# 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 -1500 python:
|
||||
|
||||
##########################################################################
|
||||
# Self-voicing
|
||||
|
||||
# Strings used internally in Ren'Py.
|
||||
_("Self-voicing disabled.")
|
||||
_("Clipboard voicing enabled. ")
|
||||
_("Self-voicing enabled. ")
|
||||
|
||||
_("bar")
|
||||
_("selected")
|
||||
_("viewport")
|
||||
_("horizontal scroll")
|
||||
_("vertical scroll")
|
||||
_("activate")
|
||||
_("deactivate")
|
||||
_("increase")
|
||||
_("decrease")
|
||||
|
||||
|
||||
# The character that's used for descriptive text.
|
||||
config.descriptive_text_character = None
|
||||
|
||||
def alt(what, interact=True):
|
||||
"""
|
||||
Uses the narrator to speak `what` if self-voicing is enabled.
|
||||
"""
|
||||
|
||||
if _preferences.self_voicing:
|
||||
|
||||
c = config.descriptive_text_character
|
||||
|
||||
if c is None:
|
||||
c = narrator
|
||||
|
||||
return c(what, interact=interact)
|
||||
|
||||
# Old name for alt.
|
||||
sv = alt
|
||||
|
||||
|
||||
init -1500 python hide:
|
||||
|
||||
##########################################################################
|
||||
# Font Transforms.
|
||||
|
||||
store.__opendyslexic = { }
|
||||
|
||||
def opendyslexic(f):
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -185,9 +185,14 @@ init -1900 python:
|
||||
|
||||
if version <= (7, 2, 2):
|
||||
config.say_attribute_transition_callback_attrs = False
|
||||
config.keep_side_render_order = False
|
||||
|
||||
if version <= (7, 3, 0):
|
||||
config.keep_side_render_order = False
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ init -1500 python in iap:
|
||||
def init(self):
|
||||
restore(False)
|
||||
|
||||
if renpy.ios:
|
||||
if renpy.renpy.ios:
|
||||
import pyobjus
|
||||
IAPHelper = pyobjus.autoclass(b"IAPHelper")
|
||||
NSMutableArray = pyobjus.autoclass(b"NSMutableArray")
|
||||
@@ -583,9 +583,9 @@ init -1500 python in iap:
|
||||
persistent._iap_purchases[p.identifier] = False
|
||||
|
||||
# Set up the back end.
|
||||
if renpy.android:
|
||||
if renpy.renpy.android:
|
||||
backend = init_android()
|
||||
elif renpy.ios:
|
||||
elif renpy.renpy.ios:
|
||||
backend = IOSBackend()
|
||||
else:
|
||||
backend = NoneBackend()
|
||||
|
||||
@@ -168,33 +168,6 @@ init -1700 python:
|
||||
extend.record_say = False
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Self-voicing
|
||||
|
||||
# Strings used internally in Ren'Py.
|
||||
_("Self-voicing disabled.")
|
||||
_("Clipboard voicing enabled. ")
|
||||
_("Self-voicing enabled. ")
|
||||
|
||||
_("bar")
|
||||
_("selected")
|
||||
_("viewport")
|
||||
_("horizontal scroll")
|
||||
_("vertical scroll")
|
||||
_("activate")
|
||||
_("deactivate")
|
||||
_("increase")
|
||||
_("decrease")
|
||||
|
||||
def sv(what, interact=True):
|
||||
"""
|
||||
Uses the narrator to speak `what` iff self-voicing is enabled.
|
||||
"""
|
||||
|
||||
if _preferences.self_voicing:
|
||||
return narrator(what, interact=interact)
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Skip indicator
|
||||
|
||||
@@ -326,6 +299,7 @@ init -1000 python:
|
||||
|
||||
for i in """
|
||||
adv
|
||||
alt
|
||||
anim
|
||||
blinds
|
||||
center
|
||||
|
||||
@@ -24,9 +24,8 @@
|
||||
|
||||
init -1600 python hide:
|
||||
|
||||
# Set to true in the very unlikely event you want to manually init
|
||||
# the sound system.
|
||||
config.force_sound = False
|
||||
# Set to False if you don't want sound to initialize without any mixers.
|
||||
config.force_sound = True
|
||||
|
||||
# basics: True if the game will have music.
|
||||
config.has_music = True
|
||||
|
||||
@@ -456,6 +456,7 @@ init -1500 python:
|
||||
|
||||
|
||||
renpy.mode('nvl_menu')
|
||||
renpy.shown_window()
|
||||
|
||||
if nvl_list is None:
|
||||
store.nvl_list = [ ]
|
||||
|
||||
@@ -200,11 +200,11 @@ 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.
|
||||
* Preference("font vertical spacing", 1.0) - Sets the accessibility font vertical spacing scaling factor.
|
||||
* Preference("font line spacing", 1.0) - Sets the accessibility font vertical spacing scaling factor.
|
||||
|
||||
Values that can be used with bars are:
|
||||
|
||||
@@ -215,7 +215,7 @@ init -1500 python:
|
||||
* Preference("voice volume")
|
||||
* Preference("mixer <mixer> volume")
|
||||
* Preference("font size")
|
||||
* Preference("font vertical spacing")
|
||||
* Preference("font line spacing")
|
||||
|
||||
The `range` parameter can be given to give the range of certain bars.
|
||||
For "text speed", it defaults to 200 cps. For "auto-forward time", it
|
||||
|
||||
@@ -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);
|
||||
""")
|
||||
@@ -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,10 +201,10 @@ label _start:
|
||||
|
||||
renpy.block_rollback()
|
||||
|
||||
|
||||
scene black
|
||||
|
||||
call _gl_test
|
||||
if not _restart:
|
||||
call _gl_test
|
||||
|
||||
python hide:
|
||||
renpy.warp.warp()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+110
-44
@@ -620,7 +620,7 @@ class SayBehavior(renpy.display.layout.Null):
|
||||
|
||||
if not renpy.config.enable_rollback_side:
|
||||
rollback_side = "disable"
|
||||
if renpy.mobile:
|
||||
if renpy.exports.mobile:
|
||||
rollback_side = renpy.game.preferences.mobile_rollback_side
|
||||
else:
|
||||
rollback_side = renpy.game.preferences.desktop_rollback_side
|
||||
@@ -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
|
||||
|
||||
+21
-15
@@ -316,7 +316,7 @@ class Displayable(renpy.object.Object):
|
||||
def __ne__(self, o):
|
||||
return not (self == o)
|
||||
|
||||
def __init__(self, focus=None, default=False, style='default', _args=None, tooltip=None, **properties):
|
||||
def __init__(self, focus=None, default=False, style='default', _args=None, tooltip=None, default_focus=False, **properties):
|
||||
|
||||
global default_style
|
||||
|
||||
@@ -326,7 +326,7 @@ class Displayable(renpy.object.Object):
|
||||
self.style = renpy.style.Style(style, properties) # @UndefinedVariable
|
||||
|
||||
self.focus_name = focus
|
||||
self.default = default
|
||||
self.default = default or default_focus
|
||||
self._tooltip = tooltip
|
||||
|
||||
if _args is not None:
|
||||
@@ -1413,15 +1413,13 @@ class SceneLists(renpy.object.Object):
|
||||
else:
|
||||
at = 0
|
||||
|
||||
surf = renpy.display.render.render(sle.displayable, width, height, st, at)
|
||||
surf = renpy.display.render.render_for_size(sle.displayable, width, height, st, at)
|
||||
|
||||
sw = surf.width
|
||||
sh = surf.height
|
||||
|
||||
x, y = place(width, height, sw, sh, sle.displayable.get_placement())
|
||||
|
||||
surf.kill()
|
||||
|
||||
return (x, y, sw, sh)
|
||||
|
||||
|
||||
@@ -1923,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" ]
|
||||
|
||||
@@ -1970,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()
|
||||
|
||||
@@ -1979,7 +1984,6 @@ class Interface(object):
|
||||
"""
|
||||
|
||||
self.kill_textures()
|
||||
|
||||
renpy.display.im.cache.clear()
|
||||
renpy.display.module.bo_cache = None
|
||||
|
||||
@@ -1992,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()
|
||||
|
||||
@@ -2007,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
|
||||
@@ -2044,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()
|
||||
@@ -3421,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)
|
||||
|
||||
|
||||
+38
-16
@@ -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
|
||||
@@ -408,12 +409,31 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
|
||||
if self.drag_group is not None:
|
||||
self.drag_group.lower_children([ self ])
|
||||
|
||||
def update_style_prefix(self):
|
||||
"""
|
||||
This updates the style prefix for all Drag's associated
|
||||
with this drag movement.
|
||||
"""
|
||||
# We may not be in the drag_joined group.
|
||||
self.set_style_prefix("idle_", True)
|
||||
|
||||
# Set the style for joined_set
|
||||
for i in [i[0] for i in self.drag_joined(self)]:
|
||||
i.set_style_prefix("selected_hover_", True)
|
||||
|
||||
if self.last_drop is not None:
|
||||
self.last_drop.set_style_prefix("selected_idle_", True)
|
||||
|
||||
def visit(self):
|
||||
return [ self.child ]
|
||||
|
||||
def focus(self, default=False):
|
||||
super(Drag, self).focus(default)
|
||||
|
||||
# Update state back after restart_interaction
|
||||
if default and self.drag_moved:
|
||||
self.update_style_prefix()
|
||||
|
||||
rv = None
|
||||
|
||||
if not default:
|
||||
@@ -601,10 +621,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 +634,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"):
|
||||
@@ -631,17 +655,10 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
|
||||
|
||||
handled = True
|
||||
|
||||
if not self.drag_moved and (self.start_x != par_x or self.start_y != par_y):
|
||||
if (not self.drag_moved) and (self.start_x != par_x or self.start_y != par_y):
|
||||
self.drag_moved = True
|
||||
self.click_time = None
|
||||
|
||||
# We may not be in the drag_joined group.
|
||||
self.set_style_prefix("idle_", True)
|
||||
|
||||
# Set the style.
|
||||
for i in joined:
|
||||
i.set_style_prefix("selected_hover_", True)
|
||||
|
||||
# Raise the joined items.
|
||||
if self.drag_raise and self.drag_group is not None:
|
||||
self.drag_group.raise_children(joined)
|
||||
@@ -684,11 +701,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 self.drag_moved:
|
||||
self.update_style_prefix()
|
||||
|
||||
if map_event(ev, 'drag_deactivate'):
|
||||
|
||||
self.click_time = None
|
||||
@@ -843,8 +860,9 @@ class DragGroup(renpy.display.layout.MultiBox):
|
||||
|
||||
def raise_children(self, l):
|
||||
"""
|
||||
Raises the children in `l` to the top of this drag_group, using the
|
||||
order given in l for those children.
|
||||
Raises the children in the list `l` to the top of this drag group.
|
||||
Each is raised in the order that it appears in `l`, which means that
|
||||
the last element of `l` will be raised closest to the player.
|
||||
"""
|
||||
|
||||
self.sorted = False
|
||||
@@ -857,6 +875,10 @@ class DragGroup(renpy.display.layout.MultiBox):
|
||||
|
||||
def lower_children(self, l):
|
||||
"""
|
||||
Lowers the children in the list `l` to the bottom of this drag group.
|
||||
Each is lowered in the order that it appears in `l`, which means that
|
||||
the last element of `l` will be the lowest of the children.
|
||||
|
||||
Lowers the children in `l` to the bottom of this drag group, with
|
||||
the one at the bottom being the lowest.
|
||||
"""
|
||||
|
||||
@@ -149,10 +149,12 @@ def init_emulator():
|
||||
if name == "touch":
|
||||
emulator = touch_emulator
|
||||
overlay = [ renpy.store.DynamicDisplayable(dynamic_keyboard) ]
|
||||
|
||||
elif name == "ios-touch":
|
||||
emulator = touch_emulator
|
||||
overlay = [ renpy.store.DynamicDisplayable(dynamic_keyboard) ]
|
||||
ios = True
|
||||
|
||||
elif name == "tv":
|
||||
emulator = tv_emulator
|
||||
overlay = [ renpy.display.motion.Transform(
|
||||
@@ -161,6 +163,16 @@ def init_emulator():
|
||||
yalign=0.5,
|
||||
size=(int(renpy.config.screen_height * 16.0 / 9.0), renpy.config.screen_height),
|
||||
) ]
|
||||
|
||||
else:
|
||||
emulator = null_emulator
|
||||
overlay = [ ]
|
||||
|
||||
if emulator is not null_emulator:
|
||||
renpy.exports.windows = False
|
||||
renpy.exports.linux = False
|
||||
renpy.exports.macintosh = False
|
||||
renpy.exports.web = False
|
||||
renpy.exports.android = renpy.exports.variant("android")
|
||||
renpy.exports.ios = renpy.exports.variant("ios")
|
||||
renpy.exports.mobile = renpy.exports.android or renpy.exports.ios # @UndefinedVariable
|
||||
|
||||
+16
-6
@@ -281,16 +281,26 @@ def before_interact(roots):
|
||||
else:
|
||||
current = None
|
||||
|
||||
# Otherwise, focus the default widget, or nothing.
|
||||
# Otherwise, focus the default widget.
|
||||
if current is None:
|
||||
|
||||
defaults = [ ]
|
||||
|
||||
for f, n, screen in fwn:
|
||||
if f.default:
|
||||
current = f
|
||||
set_focused(f, None, screen)
|
||||
break
|
||||
else:
|
||||
set_focused(None, None, None)
|
||||
defaults.append((f.default, f, screen))
|
||||
|
||||
if defaults:
|
||||
if len(defaults) > 1:
|
||||
defaults.sort()
|
||||
|
||||
_, f, screen = defaults[-1]
|
||||
|
||||
current = f
|
||||
set_focused(f, None, screen)
|
||||
|
||||
if current is None:
|
||||
set_focused(None, None, None)
|
||||
|
||||
# Finally, mark the current widget as the focused widget, and
|
||||
# all other widgets as unfocused.
|
||||
|
||||
+2
-1
@@ -442,6 +442,8 @@ class Cache(object):
|
||||
self.preload_lock.wait()
|
||||
self.preload_lock.release()
|
||||
|
||||
self.preload_thread_pass()
|
||||
|
||||
def preload_thread_pass(self):
|
||||
|
||||
while self.preloads and self.keep_preloading:
|
||||
@@ -530,7 +532,6 @@ def free_memory():
|
||||
Frees some memory.
|
||||
"""
|
||||
|
||||
renpy.display.draw.free_memory()
|
||||
cache.clear()
|
||||
|
||||
|
||||
|
||||
+19
-7
@@ -558,12 +558,6 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
|
||||
self.name = name
|
||||
|
||||
if scope is not None:
|
||||
self.find_target(scope)
|
||||
self._uses_scope = True
|
||||
else:
|
||||
self._uses_scope = False
|
||||
|
||||
if isinstance(name, basestring) and ("[prefix_" in name):
|
||||
self._duplicatable = True
|
||||
|
||||
@@ -572,6 +566,12 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
if ("[prefix_" in i):
|
||||
self._duplicatable = True
|
||||
|
||||
if scope is not None:
|
||||
self.find_target(scope)
|
||||
self._uses_scope = True
|
||||
else:
|
||||
self._uses_scope = False
|
||||
|
||||
def _scope(self, scope, update):
|
||||
return self.find_target(scope, update)
|
||||
|
||||
@@ -609,13 +609,24 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
else:
|
||||
return self
|
||||
|
||||
def set_style_prefix(self, prefix, root):
|
||||
|
||||
if (prefix != self.style.prefix) and self._duplicatable:
|
||||
self.target = None
|
||||
self.raw_target = None
|
||||
|
||||
super(DynamicImage, self).set_style_prefix(prefix, root)
|
||||
|
||||
def find_target(self, scope=None, update=True):
|
||||
|
||||
if self.locked and (self.target is not None):
|
||||
return
|
||||
|
||||
if self._args.prefix is None:
|
||||
prefix = ""
|
||||
if self._duplicatable:
|
||||
prefix = self.style.prefix
|
||||
else:
|
||||
prefix = ""
|
||||
else:
|
||||
prefix = self._args.prefix
|
||||
|
||||
@@ -675,6 +686,7 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
|
||||
rv = self._copy(args)
|
||||
rv.target = None
|
||||
rv.raw_target = None
|
||||
# This does not set _duplicatable, since it should always remain the
|
||||
# same.
|
||||
return rv
|
||||
|
||||
+72
-18
@@ -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
|
||||
|
||||
|
||||
+26
-17
@@ -1038,7 +1038,7 @@ class SizeGroup(renpy.object.Object):
|
||||
maxwidth = 0
|
||||
|
||||
for i in self.members:
|
||||
rend = i.render(width, height, st, at)
|
||||
rend = renpy.display.render.render_for_size(i, width, height, st, at)
|
||||
maxwidth = max(rend.width, maxwidth)
|
||||
|
||||
self._width = maxwidth
|
||||
@@ -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
|
||||
@@ -1573,16 +1588,8 @@ class Side(Container):
|
||||
if pos not in pos_d:
|
||||
return owidth, oheight
|
||||
|
||||
old_sizing = renpy.display.render.sizing
|
||||
renpy.display.render.sizing = True
|
||||
|
||||
try:
|
||||
rend = render(pos_d[pos], width, height, st, at)
|
||||
rv = max(owidth, rend.width), max(oheight, rend.height)
|
||||
renpy.display.render.invalidate(pos_d[pos])
|
||||
return rv
|
||||
finally:
|
||||
renpy.display.render.sizing = old_sizing
|
||||
rend = renpy.display.render.render_for_size(pos_d[pos], width, height, st, at)
|
||||
return max(owidth, rend.width), max(oheight, rend.height)
|
||||
|
||||
cwidth, cheight = sizeit('c', width, height, 0, 0)
|
||||
cwidth, top = sizeit('t', cwidth, height, cwidth, top)
|
||||
@@ -1864,19 +1871,21 @@ 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) ]
|
||||
|
||||
return rv
|
||||
|
||||
def get_placement(self):
|
||||
return self.child.get_placement()
|
||||
|
||||
|
||||
class AlphaMask(Container):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
@@ -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(v - aligned_1[i])
|
||||
total_2 += abs(v - 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"
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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=*)
|
||||
|
||||
+98
-98
@@ -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
|
||||
@@ -261,18 +273,45 @@ cpdef render(d, object widtho, object heighto, double st, double at):
|
||||
rv.render_of.append(d)
|
||||
renpy.plog(4, "after clipping")
|
||||
|
||||
# We need to look this up again, as an invalidation might
|
||||
render_cache_d = render_cache[id_d]
|
||||
|
||||
render_cache_d[wh] = rv
|
||||
if not sizing:
|
||||
|
||||
if wh is not orig_wh:
|
||||
render_cache_d[orig_wh] = rv
|
||||
# This lookup is needed because invalidations are possible.
|
||||
render_cache_d = render_cache[id_d]
|
||||
render_cache_d[wh] = rv
|
||||
|
||||
if wh is not orig_wh:
|
||||
render_cache_d[orig_wh] = rv
|
||||
|
||||
renpy.plog(2, "end render {!r}", d)
|
||||
|
||||
return rv
|
||||
|
||||
def render_for_size(d, width, height, st, at):
|
||||
"""
|
||||
This returns a render of `d` that's useful for getting the size or
|
||||
screen location, but not for actual rendering.
|
||||
"""
|
||||
|
||||
global sizing
|
||||
|
||||
id_d = id(d)
|
||||
orig_wh = (width, height, frame_time-st, frame_time-at)
|
||||
render_cache_d = render_cache[id_d]
|
||||
rv = render_cache_d.get(orig_wh, None)
|
||||
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
||||
old_sizing = sizing
|
||||
sizing = True
|
||||
|
||||
try:
|
||||
return render(d, width, height, st, at)
|
||||
finally:
|
||||
sizing = old_sizing
|
||||
|
||||
|
||||
def invalidate(d):
|
||||
"""
|
||||
Removes d from the render cache. If we're not in a redraw, triggers
|
||||
@@ -383,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)
|
||||
@@ -603,6 +574,7 @@ BLIT = 0
|
||||
DISSOLVE = 1
|
||||
IMAGEDISSOLVE = 2
|
||||
PIXELLATE = 3
|
||||
FLATTEN = 4
|
||||
|
||||
cdef class Render:
|
||||
|
||||
@@ -622,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
|
||||
|
||||
@@ -632,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
|
||||
@@ -703,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
|
||||
@@ -711,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)
|
||||
|
||||
@@ -1028,6 +1027,7 @@ cdef class Render:
|
||||
rv.operation_alpha = self.operation_alpha
|
||||
rv.operation_complete = self.operation_complete
|
||||
rv.nearest = self.nearest
|
||||
rv.text_input = self.text_input
|
||||
|
||||
return rv
|
||||
|
||||
@@ -1382,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
@@ -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
@@ -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.
|
||||
|
||||
@@ -993,9 +993,6 @@ class Transform(Container):
|
||||
rv.take_execution_state(self)
|
||||
rv._unique()
|
||||
|
||||
if rv.child and rv.child._duplicatable:
|
||||
rv.child = rv.child._duplicate(None)
|
||||
|
||||
return rv
|
||||
|
||||
def _in_current_store(self):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,22 +310,43 @@ 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
|
||||
self.edge_yspeed = 0
|
||||
self.edge_last_st = None
|
||||
|
||||
inside = False
|
||||
|
||||
@@ -474,7 +496,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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
+38
-21
@@ -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,16 +182,21 @@ 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
|
||||
|
||||
if renpy.windows and (self.old_fullscreen is not None):
|
||||
renpy.display.interface.kill_textures_and_surfaces()
|
||||
pygame.display.quit()
|
||||
|
||||
pygame.display.init()
|
||||
@@ -414,8 +420,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 +452,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 +465,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
|
||||
|
||||
@@ -932,6 +931,9 @@ cdef class GLDraw:
|
||||
p /= 2
|
||||
pc = self.get_half(pc)
|
||||
|
||||
elif rend.operation == FLATTEN:
|
||||
child.render_to_texture(True)
|
||||
|
||||
if render_what:
|
||||
what.render_to_texture(True)
|
||||
|
||||
@@ -943,13 +945,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 +1041,7 @@ cdef class GLDraw:
|
||||
return 0
|
||||
|
||||
|
||||
if rend.operation == PIXELLATE:
|
||||
elif rend.operation == PIXELLATE:
|
||||
self.set_clip(clip)
|
||||
|
||||
p = rend.operation_parameter
|
||||
@@ -1049,7 +1051,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 +1065,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 +1117,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 +1226,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 +1416,7 @@ cdef class GLDraw:
|
||||
|
||||
return rv
|
||||
|
||||
def free_memory(self):
|
||||
def kill_textures(self):
|
||||
self.texture_cache.clear()
|
||||
gltexture.dealloc_textures()
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+33
-15
@@ -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):
|
||||
"""
|
||||
@@ -75,21 +95,19 @@ class FboRtt(Rtt):
|
||||
to render the texture.
|
||||
"""
|
||||
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)
|
||||
try:
|
||||
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)
|
||||
|
||||
environ.viewport(0, 0, w, h)
|
||||
environ.ortho(x, x + w, y, y + h, -1, 1)
|
||||
draw_func(x, y, w, h)
|
||||
|
||||
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)
|
||||
finally:
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, root_fbo)
|
||||
|
||||
|
||||
def get_size_limit(self, dimension):
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user