column major
This commit is contained in:
@ -2,7 +2,7 @@ from nonebot import on_command
|
|||||||
from nonebot.adapters import Message
|
from nonebot.adapters import Message
|
||||||
from nonebot_plugin_alconna import UniMessage
|
from nonebot_plugin_alconna import UniMessage
|
||||||
from nonebot.params import CommandArg
|
from nonebot.params import CommandArg
|
||||||
import render
|
import konabot.plugins.marchtoy.gl_render as render
|
||||||
import io
|
import io
|
||||||
cmd_marchtoy = on_command("march", priority=10, block=True)
|
cmd_marchtoy = on_command("march", priority=10, block=True)
|
||||||
@cmd_marchtoy.handle()
|
@cmd_marchtoy.handle()
|
||||||
|
|||||||
@ -18,9 +18,9 @@ from dataclasses import dataclass
|
|||||||
import re
|
import re
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from obj import Object, Camera, OBJECT_ENTRIES
|
|
||||||
from op import OPERATION_ENTRIES
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from konabot.plugins.marchtoy.obj import Object, Camera, OBJECT_ENTRIES
|
||||||
|
from konabot.plugins.marchtoy.op import OPERATION_ENTRIES
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Command:
|
class Command:
|
||||||
|
|||||||
@ -3,11 +3,14 @@ headless moderngl
|
|||||||
"""
|
"""
|
||||||
import pathlib
|
import pathlib
|
||||||
import moderngl
|
import moderngl
|
||||||
from command import Scene
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from konabot.plugins.marchtoy.command import Scene
|
||||||
def render(vs: str, fs: str, res: tuple[int, int]):
|
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)
|
ctx = moderngl.create_context(standalone=True)
|
||||||
try:
|
try:
|
||||||
program = ctx.program(
|
program = ctx.program(
|
||||||
@ -27,11 +30,4 @@ def render(vs: str, fs: str, res: tuple[int, int]):
|
|||||||
fbo.use()
|
fbo.use()
|
||||||
fbo.clear(0.0, 0.0, 0.0, 0.0)
|
fbo.clear(0.0, 0.0, 0.0, 0.0)
|
||||||
vao.render(moderngl.TRIANGLES)
|
vao.render(moderngl.TRIANGLES)
|
||||||
Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1).show()
|
return Image.frombytes('RGBA', fbo.size, fbo.read(components=4), 'raw', 'RGBA', 0, -1)
|
||||||
|
|
||||||
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))
|
|
||||||
@ -1,14 +1,14 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from texture import Texture
|
from konabot.plugins.marchtoy.texture import Texture
|
||||||
from utilities import ArgParser, Formatter
|
from konabot.plugins.marchtoy.utilities import ArgParser, Formatter
|
||||||
|
|
||||||
OBJECT_ENTRIES = {}
|
OBJECT_ENTRIES = {}
|
||||||
|
|
||||||
|
|
||||||
def make_obj(name: str, aliases: list[str] = []):
|
def make_obj(*name: str):
|
||||||
def decorator(cls):
|
def decorator(cls):
|
||||||
OBJECT_ENTRIES[name] = cls
|
# OBJECT_ENTRIES[name] = cls
|
||||||
for alias in aliases:
|
for alias in [*name]:
|
||||||
OBJECT_ENTRIES[alias] = cls
|
OBJECT_ENTRIES[alias] = cls
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class Transform:
|
|||||||
self.t = mat @ self.t
|
self.t = mat @ self.t
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# scale 会破坏 sdf 的性质,不应该使用
|
# scale 会破坏 sdf 的性质 梯度大小会变 导致 overshoot 等问题
|
||||||
def scale(self, x: float, y: float, z: float):
|
def scale(self, x: float, y: float, z: float):
|
||||||
mat = np.identity(4, dtype=np.float32)
|
mat = np.identity(4, dtype=np.float32)
|
||||||
mat[0, 0], mat[1, 1], mat[2, 2] = x, y, z
|
mat[0, 0], mat[1, 1], mat[2, 2] = x, y, z
|
||||||
@ -72,7 +72,7 @@ class Transform:
|
|||||||
|
|
||||||
def p_expr(self) -> str:
|
def p_expr(self) -> str:
|
||||||
inv = np.linalg.inv(self.t) # + 1e-5 * np.identity(4, dtype=np.float32))
|
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:
|
class Object:
|
||||||
@ -87,7 +87,7 @@ class Object:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
@make_obj("cube")
|
@make_obj("cube", "box")
|
||||||
class Cube(Object):
|
class Cube(Object):
|
||||||
def __init__(self, _size: np.ndarray = np.array([1.0, 1.0, 1.0])) -> None:
|
def __init__(self, _size: np.ndarray = np.array([1.0, 1.0, 1.0])) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -97,10 +97,10 @@ class Cube(Object):
|
|||||||
self.size = ArgParser.as_vec3(args)
|
self.size = ArgParser.as_vec3(args)
|
||||||
|
|
||||||
def sdf_block(self) -> str:
|
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):
|
class Sphere(Object):
|
||||||
def __init__(self, _radius: float = 1.0) -> None:
|
def __init__(self, _radius: float = 1.0) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -113,7 +113,7 @@ class Sphere(Object):
|
|||||||
return f"sdSphere({self.transform.p_expr()}, {self.radius})"
|
return f"sdSphere({self.transform.p_expr()}, {self.radius})"
|
||||||
|
|
||||||
|
|
||||||
@make_obj("camera", ["cam"])
|
@make_obj("camera", "cam")
|
||||||
class Camera(Object):
|
class Camera(Object):
|
||||||
def __init__(self, _focus: float = 1.0) -> None:
|
def __init__(self, _focus: float = 1.0) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|||||||
@ -1,39 +1,37 @@
|
|||||||
from obj import Object
|
from konabot.plugins.marchtoy.obj import Object
|
||||||
import numpy as np
|
from konabot.plugins.marchtoy.utilities import ArgParser
|
||||||
from utilities import ArgParser
|
|
||||||
|
|
||||||
OPERATION_ENTRIES = {}
|
OPERATION_ENTRIES = {}
|
||||||
|
|
||||||
|
def make_operation(*name: str):
|
||||||
def make_operation(name: str, aliases: list[str] = []):
|
|
||||||
def decorator(op):
|
def decorator(op):
|
||||||
OPERATION_ENTRIES[name] = op
|
# OPERATION_ENTRIES[name] = op
|
||||||
for alias in aliases:
|
for alias in [*name]:
|
||||||
OPERATION_ENTRIES[alias] = op
|
OPERATION_ENTRIES[alias] = op
|
||||||
return op
|
return op
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
@make_operation("pos", ["translate", "position", "p"])
|
@make_operation("pos", "translate", "position", "p")
|
||||||
def translate(obj: Object, args: list[str]):
|
def translate(obj: Object, args: list[str]):
|
||||||
pos = ArgParser.as_vec3(args)
|
pos = ArgParser.as_vec3(args)
|
||||||
obj.transform.translate(pos[0], pos[1], pos[2])
|
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]):
|
def rotate(obj: Object, args: list[str]):
|
||||||
pos = ArgParser.as_vec3(args)
|
pos = ArgParser.as_vec3(args)
|
||||||
obj.transform.rotate(pos[0], pos[1], pos[2])
|
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]):
|
def lookat(obj: Object, args: list[str]):
|
||||||
pos = ArgParser.as_vec3(args)
|
pos = ArgParser.as_vec3(args)
|
||||||
obj.transform.lookat(pos[0], pos[1], pos[2])
|
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]):
|
def color(obj: Object, args: list[str]):
|
||||||
try:
|
try:
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
raise DeprecationWarning
|
||||||
from command import Scene
|
from command import Scene
|
||||||
import skia
|
import skia
|
||||||
import struct
|
import struct
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
COLORS = {
|
COLORS = {
|
||||||
"red": (1.0, 0.0, 0.0, 1.0),
|
"red": (1.0, 0.0, 0.0, 1.0),
|
||||||
"green": (0.0, 1.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),
|
"white": (1.0, 1.0, 1.0, 1.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Texture:
|
class Texture:
|
||||||
color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0)
|
color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0)
|
||||||
|
|||||||
@ -1,17 +1,26 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from texture import COLORS
|
from konabot.plugins.marchtoy.texture import COLORS
|
||||||
|
|
||||||
|
|
||||||
class Formatter:
|
class Formatter:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def float4(m: np.ndarray) -> str:
|
def float4(m: np.ndarray) -> str:
|
||||||
if m.shape != (4, 4):
|
if m.shape != (4, 4):
|
||||||
m = np.identity(4)
|
m = np.identity(4)
|
||||||
v_0 = ", ".join([str(x) for x in m[0]])
|
v_0 = ", ".join([str(x) for x in m[:, 0]])
|
||||||
v_1 = ", ".join([str(x) for x in m[1]])
|
v_1 = ", ".join([str(x) for x in m[:, 1]])
|
||||||
v_2 = ", ".join([str(x) for x in m[2]])
|
v_2 = ", ".join([str(x) for x in m[:, 2]])
|
||||||
v_3 = ", ".join([str(x) for x in m[3]])
|
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}))"
|
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 情况的单独处理
|
TODO: 除零出现 nan 情况的单独处理
|
||||||
|
|||||||
17
tests/test_marchtoy_transform.py
Normal file
17
tests/test_marchtoy_transform.py
Normal file
@ -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
|
||||||
Reference in New Issue
Block a user