347 lines
7.7 KiB
Python
347 lines
7.7 KiB
Python
|
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"))
|