renpy/module/symbolic_matrix.py

347 lines
7.7 KiB
Python
Raw Permalink Normal View History

2023-01-18 22:13:55 +00:00
from __future__ import print_function, unicode_literals
from sympy import symbols, Matrix, pi, cos, sin, simplify
import io
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 ])
###############################################################################
generators = [ ]
class Generator(object):
def __init__(self, name, docs):
self.pyd_f = io.StringIO()
self.pyx_f = io.StringIO()
self.f = io.StringIO()
self.name = name
self.docs = docs
generators.append(self)
self.first_let = True
def parameters(self, params):
# PYD.
print(" @staticmethod", file=self.pyd_f)
print(" cdef Matrix c{}({})".format(
self.name,
", ".join("float " + i for i in params.split())), file=self.pyd_f)
# PYX.
print(" @staticmethod", file=self.pyx_f)
print(" cdef Matrix c{}({}):".format(
self.name,
", ".join("float " + i for i in params.split())), file=self.pyx_f)
print(" return {}_matrix({})".format(
self.name, ", ".join(params.split())), file=self.pyx_f)
print(file=self.pyx_f)
print(" @staticmethod", file=self.pyx_f)
print(" def {}({}):".format(
self.name,
", ".join(params.split())), file=self.pyx_f)
if self.docs:
print(' """' + self.docs.replace("\n", "\n ") + '"""', file=self.pyx_f)
print(" return {}_matrix({})".format(
self.name, ", ".join(params.split())), file=self.pyx_f)
# PXI.
print(file=self.f)
print(file=self.f)
print("cpdef Matrix {}_matrix({}):".format(
self.name,
", ".join("float " + i for i in params.split())), 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())
print("pxd ---------------------------------")
for i in generators:
print(i.pyd_f.getvalue())
print("pyx ---------------------------------")
for i in generators:
print(i.pyx_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.
The rotations are applied in order:
* A clockwise rotation by `x` degrees in the Y/Z plane.
* A clockwise rotation by `y` degrees in the Z/X plane.
* A clockwise rotation by `z` degrees in the X/Y plane.
"""
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 scale(g):
"""
Returns a matrix that scales the displayable.
`x`, `y`, `z`
The factor to scale each axis by.
"""
x, y, z = g.parameters("x y z")
m = Matrix(4, 4, [
x, 0, 0, 0,
0, y, 0, 0,
0, 0, z, 0,
0, 0, 0, 1 ])
g.matrix(m)
@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"))