diff --git a/konabot/plugins/marchtoy/__init__.py b/konabot/plugins/marchtoy/__init__.py index 7ed576e..844e7be 100644 --- a/konabot/plugins/marchtoy/__init__.py +++ b/konabot/plugins/marchtoy/__init__.py @@ -2,7 +2,7 @@ from nonebot import on_command from nonebot.adapters import Message from nonebot_plugin_alconna import UniMessage from nonebot.params import CommandArg -import render +import konabot.plugins.marchtoy.gl_render as render import io cmd_marchtoy = on_command("march", priority=10, block=True) @cmd_marchtoy.handle() diff --git a/konabot/plugins/marchtoy/command.py b/konabot/plugins/marchtoy/command.py index 128dcc3..4c67868 100644 --- a/konabot/plugins/marchtoy/command.py +++ b/konabot/plugins/marchtoy/command.py @@ -18,9 +18,9 @@ from dataclasses import dataclass import re import numpy as np from dataclasses import dataclass -from obj import Object, Camera, OBJECT_ENTRIES -from op import OPERATION_ENTRIES from typing import Optional +from konabot.plugins.marchtoy.obj import Object, Camera, OBJECT_ENTRIES +from konabot.plugins.marchtoy.op import OPERATION_ENTRIES @dataclass class Command: diff --git a/konabot/plugins/marchtoy/gl_render.py b/konabot/plugins/marchtoy/gl_render.py index 813dbe2..70d1ff8 100644 --- a/konabot/plugins/marchtoy/gl_render.py +++ b/konabot/plugins/marchtoy/gl_render.py @@ -3,11 +3,14 @@ headless moderngl """ import pathlib import moderngl -from command import Scene import numpy as np from PIL import Image - -def render(vs: str, fs: str, res: tuple[int, int]): +from konabot.plugins.marchtoy.command import Scene +async def render(instruction: str, res: tuple[int, int]): + fs = Scene(instruction).compile() + PATH = pathlib.Path(__file__).parent / "shaders" + with (PATH / "vert.glsl").open(encoding='utf-8') as f: + vs = f.read() ctx = moderngl.create_context(standalone=True) try: program = ctx.program( @@ -27,11 +30,4 @@ def render(vs: str, fs: str, res: tuple[int, int]): fbo.use() fbo.clear(0.0, 0.0, 0.0, 0.0) vao.render(moderngl.TRIANGLES) - Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1).show() - -fs = Scene("cube(0.5).pos(1) camera.pos(1).lookat(0)").compile() -PATH = pathlib.Path(__file__).parent / "shaders" -with (PATH / "vert.glsl").open(encoding='utf-8') as f: - vs = f.read() - -render(vs, fs, (320, 320)) + return Image.frombytes('RGBA', fbo.size, fbo.read(components=4), 'raw', 'RGBA', 0, -1) \ No newline at end of file diff --git a/konabot/plugins/marchtoy/obj.py b/konabot/plugins/marchtoy/obj.py index 3aa500b..ceabaf6 100644 --- a/konabot/plugins/marchtoy/obj.py +++ b/konabot/plugins/marchtoy/obj.py @@ -1,14 +1,14 @@ import numpy as np -from texture import Texture -from utilities import ArgParser, Formatter +from konabot.plugins.marchtoy.texture import Texture +from konabot.plugins.marchtoy.utilities import ArgParser, Formatter OBJECT_ENTRIES = {} -def make_obj(name: str, aliases: list[str] = []): +def make_obj(*name: str): def decorator(cls): - OBJECT_ENTRIES[name] = cls - for alias in aliases: + # OBJECT_ENTRIES[name] = cls + for alias in [*name]: OBJECT_ENTRIES[alias] = cls return cls @@ -29,7 +29,7 @@ class Transform: self.t = mat @ self.t return self - # scale 会破坏 sdf 的性质,不应该使用 + # scale 会破坏 sdf 的性质 梯度大小会变 导致 overshoot 等问题 def scale(self, x: float, y: float, z: float): mat = np.identity(4, dtype=np.float32) mat[0, 0], mat[1, 1], mat[2, 2] = x, y, z @@ -72,7 +72,7 @@ class Transform: def p_expr(self) -> str: inv = np.linalg.inv(self.t) # + 1e-5 * np.identity(4, dtype=np.float32)) - return f"({Formatter.float4(inv)} * float4(p, 1.0)).xyz" + return f"({Formatter.float4(inv)} * vec4(p, 1.0)).xyz" class Object: @@ -87,7 +87,7 @@ class Object: raise NotImplementedError -@make_obj("cube") +@make_obj("cube", "box") class Cube(Object): def __init__(self, _size: np.ndarray = np.array([1.0, 1.0, 1.0])) -> None: super().__init__() @@ -97,10 +97,10 @@ class Cube(Object): self.size = ArgParser.as_vec3(args) def sdf_block(self) -> str: - return f"sdCube({self.transform.p_expr()}, float3({self.size[0]}, {self.size[1]}, {self.size[2]}))" + return f"sdCube({self.transform.p_expr()}, vec3({self.size[0]}, {self.size[1]}, {self.size[2]}))" -@make_obj("sphere") +@make_obj("sphere", "ball") class Sphere(Object): def __init__(self, _radius: float = 1.0) -> None: super().__init__() @@ -113,7 +113,7 @@ class Sphere(Object): return f"sdSphere({self.transform.p_expr()}, {self.radius})" -@make_obj("camera", ["cam"]) +@make_obj("camera", "cam") class Camera(Object): def __init__(self, _focus: float = 1.0) -> None: super().__init__() diff --git a/konabot/plugins/marchtoy/op.py b/konabot/plugins/marchtoy/op.py index cfea952..a896e42 100644 --- a/konabot/plugins/marchtoy/op.py +++ b/konabot/plugins/marchtoy/op.py @@ -1,39 +1,37 @@ -from obj import Object -import numpy as np -from utilities import ArgParser +from konabot.plugins.marchtoy.obj import Object +from konabot.plugins.marchtoy.utilities import ArgParser OPERATION_ENTRIES = {} - -def make_operation(name: str, aliases: list[str] = []): +def make_operation(*name: str): def decorator(op): - OPERATION_ENTRIES[name] = op - for alias in aliases: + # OPERATION_ENTRIES[name] = op + for alias in [*name]: OPERATION_ENTRIES[alias] = op return op return decorator -@make_operation("pos", ["translate", "position", "p"]) +@make_operation("pos", "translate", "position", "p") def translate(obj: Object, args: list[str]): pos = ArgParser.as_vec3(args) obj.transform.translate(pos[0], pos[1], pos[2]) -@make_operation("rot", ["rotate", "r"]) +@make_operation("rot", "rotate", "r") def rotate(obj: Object, args: list[str]): pos = ArgParser.as_vec3(args) obj.transform.rotate(pos[0], pos[1], pos[2]) -@make_operation("lookat", ["look", "l"]) +@make_operation("lookat", "look", "l") def lookat(obj: Object, args: list[str]): pos = ArgParser.as_vec3(args) obj.transform.lookat(pos[0], pos[1], pos[2]) -@make_operation("color", ["col", "texture"]) +@make_operation("color", "col", "texture") def color(obj: Object, args: list[str]): try: if len(args) == 1: diff --git a/konabot/plugins/marchtoy/render.py b/konabot/plugins/marchtoy/skia_render.py similarity index 98% rename from konabot/plugins/marchtoy/render.py rename to konabot/plugins/marchtoy/skia_render.py index 8a87425..f04ef40 100644 --- a/konabot/plugins/marchtoy/render.py +++ b/konabot/plugins/marchtoy/skia_render.py @@ -1,3 +1,4 @@ +raise DeprecationWarning from command import Scene import skia import struct diff --git a/konabot/plugins/marchtoy/texture.py b/konabot/plugins/marchtoy/texture.py index c1b9833..01c5ff6 100644 --- a/konabot/plugins/marchtoy/texture.py +++ b/konabot/plugins/marchtoy/texture.py @@ -1,3 +1,5 @@ +from dataclasses import dataclass + COLORS = { "red": (1.0, 0.0, 0.0, 1.0), "green": (0.0, 1.0, 0.0, 1.0), @@ -5,9 +7,6 @@ COLORS = { "white": (1.0, 1.0, 1.0, 1.0), } -from dataclasses import dataclass - - @dataclass class Texture: color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0) diff --git a/konabot/plugins/marchtoy/utilities.py b/konabot/plugins/marchtoy/utilities.py index 60d82c0..0806c75 100644 --- a/konabot/plugins/marchtoy/utilities.py +++ b/konabot/plugins/marchtoy/utilities.py @@ -1,17 +1,26 @@ import numpy as np -from texture import COLORS - +from konabot.plugins.marchtoy.texture import COLORS class Formatter: @staticmethod def float4(m: np.ndarray) -> str: if m.shape != (4, 4): m = np.identity(4) - v_0 = ", ".join([str(x) for x in m[0]]) - v_1 = ", ".join([str(x) for x in m[1]]) - v_2 = ", ".join([str(x) for x in m[2]]) - v_3 = ", ".join([str(x) for x in m[3]]) + v_0 = ", ".join([str(x) for x in m[:, 0]]) + v_1 = ", ".join([str(x) for x in m[:, 1]]) + v_2 = ", ".join([str(x) for x in m[:, 2]]) + v_3 = ", ".join([str(x) for x in m[:, 3]]) return f"float4x4(float4({v_0}), float4({v_1}), float4({v_2}), float4({v_3}))" + + @staticmethod + def vec4(m: np.ndarray) -> str: + if m.shape != (4, 4): + m = np.identity(4) + v_0 = ", ".join([str(x) for x in m[:, 0]]) + v_1 = ", ".join([str(x) for x in m[:, 1]]) + v_2 = ", ".join([str(x) for x in m[:, 2]]) + v_3 = ", ".join([str(x) for x in m[:, 3]]) + return f"mat4(vec4({v_0}), vec4({v_1}), vec4({v_2}), vec4({v_3}))" """ TODO: 除零出现 nan 情况的单独处理 diff --git a/tests/test_marchtoy_transform.py b/tests/test_marchtoy_transform.py new file mode 100644 index 0000000..add962f --- /dev/null +++ b/tests/test_marchtoy_transform.py @@ -0,0 +1,17 @@ +import sys +from pathlib import Path + + +PLUGIN_DIR = Path(__file__).resolve().parents[1] / "konabot" / "plugins" / "marchtoy" +if str(PLUGIN_DIR) not in sys.path: + sys.path.insert(0, str(PLUGIN_DIR)) + +from obj import Transform + + +def test_translate_expression_puts_offset_in_matrix_column(): + transform = Transform().translate(1.0, 2.0, 3.0) + + expr = transform.p_expr() + + assert "float4(-1.0, -2.0, -3.0, 1.0)" in expr