Skip to content

Instantly share code, notes, and snippets.

@methanoliver
Created September 17, 2022 08:26
Show Gist options
  • Save methanoliver/f265cd68bf13ac1008d7140d07b2e023 to your computer and use it in GitHub Desktop.
Save methanoliver/f265cd68bf13ac1008d7140d07b2e023 to your computer and use it in GitHub Desktop.
Gradient() equivalent of Solid()
## This file *MUST* be named `game/python-packages/gradient.py` to work properly.
from renpy.gl2.gl2mesh3 import Mesh3 # type: ignore
from renpy.gl2.gl2model import GL2Model # type: ignore
def solid_gradient(w, h, color_start, color_end, vertical):
"""
Returns a texture with a gradient in it.
"""
def __color_to_tuple(color):
# Actually, why does it premultiply alpha here?
a = color[3] / 255.0
r = a * color[0] / 255.0
g = a * color[1] / 255.0
b = a * color[2] / 255.0
return (r, g, b, a)
mesh = Mesh3.rectangle(0, 0, w, h)
start_color = __color_to_tuple(color_start)
end_color = __color_to_tuple(color_end)
return GL2Model((w, h), mesh, ("oliver.gradient", ),
({ "u_gradient_start" : start_color, "u_gradient_end": end_color,
"u_vertical": vertical }))
## This file can be placed anywhere,
## but I recommend you put it in `game/python-packages/gradient.rpy`.
init python:
from gradient import solid_gradient
class Gradient(renpy.Displayable):
"""
A displayable that fills the area it's assigned with a linear gradient.
Mostly duplicated from RenPy's original Solid code.
Limitations: Will only work if `config.gl2` is True,
i.e. if model-based rendering is enabled. What happens if it isn't
has never been tested.
Arguments:
`color` - the color of the start of the gradient.
`end_color` - the color of the end of the gradient.
`vertical` - True if the gradient is to be vertical. Defaults to false.
Horizontal gradients start at the left, while vertical gradients
start at the top.
"""
def __init__(self, color, end_color, vertical=False, **properties):
super(Gradient, self).__init__(**properties)
if color is not None and end_color is not None:
self.color = renpy.easy.color(color)
self.end_color = renpy.easy.color(end_color)
else:
self.color = None
self.end_color = None
self.vertical = vertical
def __hash__(self):
return hash((self.color, self.end_color, self.vertical))
def __eq__(self, o):
if not self._equals(o):
return False
return (all([self.color == o.color,
self.end_color == o.end_color,
self.vertical == o.vertical]))
def visit(self):
return [ ]
def render(self, width, height, st, at):
xminimum, yminimum = renpy.display.layout.xyminimums(self.style, width, height)
width = max(xminimum, width)
height = max(yminimum, height)
color = self.color or self.style.color
# Misusing a property here.
end_color = self.end_color or self.style.black_color
# We can't pass a boolean to the shader.
vertical = 0.0
if self.vertical:
vertical = 1.0
rv = renpy.display.render.Render(width, height)
if color is None or width <= 0 or height <= 0:
return rv
SIZE = 10
if width < SIZE or height < SIZE:
tex = solid_gradient(width, height, color, end_color, vertical)
else:
tex = solid_gradient(SIZE, SIZE, color, end_color, vertical)
rv.forward = renpy.display.render.Matrix2D(1.0 * SIZE / width, 0, 0, 1.0 * SIZE / height)
rv.reverse = renpy.display.render.Matrix2D(1.0 * width / SIZE, 0, 0, 1.0 * height / SIZE)
rv.blit(tex, (0, 0))
return rv
init python hide:
renpy.register_shader("oliver.gradient",
variables="""
uniform vec2 u_model_size;
uniform vec4 u_gradient_start;
uniform vec4 u_gradient_end;
uniform float u_vertical;
varying vec2 v_uv;
""",
vertex_200="""
v_uv = a_position.xy / u_model_size;
""",
fragment_200="""
float coord = u_vertical > 0.5 ? v_uv.y : v_uv.x;
gl_FragColor = vec4(
smoothstep(u_gradient_start.r, u_gradient_end.r, coord),
smoothstep(u_gradient_start.g, u_gradient_end.g, coord),
smoothstep(u_gradient_start.b, u_gradient_end.b, coord),
smoothstep(u_gradient_start.a, u_gradient_end.a, coord)
);
""")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment