diff --git a/konabot/docs/user/march.txt b/konabot/docs/user/march.txt new file mode 100644 index 0000000..855b509 --- /dev/null +++ b/konabot/docs/user/march.txt @@ -0,0 +1,4 @@ +# 指令介绍 +简易 Raymarch 小玩具 +用法:march +例:march sphere(1) cam(4).pos(-5).lookat(0) \ No newline at end of file diff --git a/konabot/plugins/sksl/__init__.py b/konabot/plugins/sksl/__init__.py index 494134e..8661df5 100644 --- a/konabot/plugins/sksl/__init__.py +++ b/konabot/plugins/sksl/__init__.py @@ -2,16 +2,23 @@ from nonebot_plugin_alconna import Alconna, Args, Option, UniMessage, on_alconna from konabot.common.nb.exc import BotExceptionMessage from konabot.plugins.sksl.run_sksl import render_sksl_shader_to_gif +from nonebot import on_command +from nonebot.adapters import Bot, Event, Message +from nonebot.params import CommandArg +import marchtoy +import io +cmd_run_sksl = on_alconna( + Alconna( + "shadertool", + Option("--width", Args["width_", int]), + Option("--height", Args["height_", int]), + Option("--duration", Args["duration_", float]), + Option("--fps", Args["fps_", float]), + Args["code", str], + ) +) -cmd_run_sksl = on_alconna(Alconna( - "shadertool", - Option("--width", Args["width_", int]), - Option("--height", Args["height_", int]), - Option("--duration", Args["duration_", float]), - Option("--fps", Args["fps_", float]), - Args["code", str], -)) @cmd_run_sksl.handle() async def _( @@ -34,10 +41,23 @@ async def _( if width_ > 640 or height_ > 640: raise BotExceptionMessage("最大支持 640x640 啦!不要太大啦!") - code = code.strip("\"").strip("'") + code = code.strip('"').strip("'") try: res = await render_sksl_shader_to_gif(code, width_, height_, duration_, fps_) await cmd_run_sksl.send(await UniMessage().image(raw=res).export()) except (ValueError, RuntimeError) as e: - await cmd_run_sksl.send(await UniMessage().text(f"渲染时遇到了问题:\n\n{e}").export()) + await cmd_run_sksl.send( + await UniMessage().text(f"渲染时遇到了问题:\n\n{e}").export() + ) + + +cmd_marchtoy = on_command("march", priority=10, block=True) +@cmd_marchtoy.handle() +async def _(args: Message = CommandArg()): + if cmd := args.extract_plain_text(): + img = await marchtoy.render.render(cmd, 256, 256) + buffer = io.BytesIO() + img.save(buffer, format="PNG") + buffer.seek(0) + await cmd_run_sksl.send(await UniMessage().image(raw=buffer).export()) \ No newline at end of file diff --git a/konabot/plugins/sksl/marchtoy/__init__.py b/konabot/plugins/sksl/marchtoy/__init__.py new file mode 100644 index 0000000..17e1f95 --- /dev/null +++ b/konabot/plugins/sksl/marchtoy/__init__.py @@ -0,0 +1 @@ +import render \ No newline at end of file diff --git a/konabot/plugins/sksl/marchtoy/command.py b/konabot/plugins/sksl/marchtoy/command.py index 5982b47..e1d3157 100644 --- a/konabot/plugins/sksl/marchtoy/command.py +++ b/konabot/plugins/sksl/marchtoy/command.py @@ -22,19 +22,6 @@ from obj import Object, Camera, OBJECT_ENTRIES from op import OPERATION_ENTRIES from typing import Optional -""" -# cmd = on_command("march", priority=10, block=True) -# @cmd.handle() -# async def _(args: Message = CommandArg()): -# if inst_text := args.extract_plain_text(): -# instructions = inst_text.split(' ') -# for instruction in instructions: -# if instruction == '': continue -# if parsed_inst := parse_instruction(instruction): -# pass -""" - - @dataclass class Command: id: str diff --git a/konabot/plugins/sksl/marchtoy/render.py b/konabot/plugins/sksl/marchtoy/render.py new file mode 100644 index 0000000..8a87425 --- /dev/null +++ b/konabot/plugins/sksl/marchtoy/render.py @@ -0,0 +1,41 @@ +from command import Scene +import skia +import struct +from PIL import Image +import numpy as np + +# 暂时先照抄小帕的了,之后有空再单独封装一下 + + +async def render(cmd: str, width: int, height: int): + scene = Scene(cmd) + surface = skia.Surface(width, height) + sksl_code = scene.compile() + + runtime_effect = skia.RuntimeEffect.MakeForShader(scene.compile()) + if runtime_effect is None: + raise Exception("cannot compile sksl shader") + + uv_uniform = struct.pack("ff", float(width), float(height)) + uniform_data = skia.Data.MakeWithCopy(uv_uniform) + shader = runtime_effect.makeShader(uniform_data, None, 0) + canvas = surface.getCanvas() + canvas.clear(skia.Color(0, 0, 0, 0)) + paint = skia.Paint() + paint.setShader(shader) + canvas.drawRect(skia.Rect.MakeWH(width, height), paint) + image = surface.makeImageSnapshot() + target_info = skia.ImageInfo.Make( + image.width(), + image.height(), + skia.ColorType.kBGRA_8888_ColorType, + skia.AlphaType.kPremul_AlphaType, + ) + pixel_data = bytearray( + image.width() * image.height() * 4 + ) # 4 bytes per pixel (BGRA) + success = image.readPixels(target_info, pixel_data, target_info.minRowBytes()) + img_array = np.frombuffer(pixel_data, dtype=np.uint8).reshape((height, width, 4)) + rgb_array = img_array[:, :, [2, 1, 0]] + pil_img = Image.fromarray(rgb_array) + return pil_img diff --git a/konabot/plugins/sksl/marchtoy/test.py b/konabot/plugins/sksl/marchtoy/test.py deleted file mode 100644 index 8c45091..0000000 --- a/konabot/plugins/sksl/marchtoy/test.py +++ /dev/null @@ -1,39 +0,0 @@ -from command import Scene -import skia -import struct -from PIL import Image -import numpy as np - -# 暂时先照抄小帕的了,之后有空再单独封装一下 - -width = 480 -height = 480 -scene = Scene("sphere(1).pos(0, 0, 0) camera(4.0).pos(1).lookat(0.0)") -surface = skia.Surface(width, height) -sksl_code = scene.compile() - -runtime_effect = skia.RuntimeEffect.MakeForShader(scene.compile()) -if runtime_effect is None: - raise Exception("cannot compile sksl shader") - -uv_uniform = struct.pack("ff", float(width), float(height)) -uniform_data = skia.Data.MakeWithCopy(uv_uniform) -shader = runtime_effect.makeShader(uniform_data, None, 0) -canvas = surface.getCanvas() -canvas.clear(skia.Color(0, 0, 0, 0)) -paint = skia.Paint() -paint.setShader(shader) -canvas.drawRect(skia.Rect.MakeWH(width, height), paint) -image = surface.makeImageSnapshot() -target_info = skia.ImageInfo.Make( - image.width(), - image.height(), - skia.ColorType.kBGRA_8888_ColorType, - skia.AlphaType.kPremul_AlphaType, -) -pixel_data = bytearray(image.width() * image.height() * 4) # 4 bytes per pixel (BGRA) -success = image.readPixels(target_info, pixel_data, target_info.minRowBytes()) -img_array = np.frombuffer(pixel_data, dtype=np.uint8).reshape((height, width, 4)) -rgb_array = img_array[:, :, [2, 1, 0]] -pil_img = Image.fromarray(rgb_array) -pil_img.save("1.png") \ No newline at end of file