Files
konabot/konabot/plugins/marchtoy/obj.py
2026-04-25 14:31:23 +08:00

124 lines
3.4 KiB
Python

import numpy as np
from texture import Texture
from utilities import ArgParser, Formatter
OBJECT_ENTRIES = {}
def make_obj(name: str, aliases: list[str] = []):
def decorator(cls):
OBJECT_ENTRIES[name] = cls
for alias in aliases:
OBJECT_ENTRIES[alias] = cls
return cls
return decorator
class Transform:
def __init__(self) -> None:
self.t: np.ndarray = np.identity(4, dtype=np.float32)
@staticmethod
def normalize(p: np.ndarray) -> np.ndarray:
return p / np.sqrt(np.dot(p, p))
def translate(self, x: float, y: float, z: float):
mat = np.identity(4, dtype=np.float32)
mat[0:3, 3] = [x, y, z]
self.t = mat @ self.t
return self
# scale 会破坏 sdf 的性质,不应该使用
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
self.t = mat @ self.t
return self
def rotate(self, x: float, y: float, z: float):
cx, sx = np.cos(x), np.sin(x)
cy, sy = np.cos(y), np.sin(y)
cz, sz = np.cos(z), np.sin(z)
mat = np.identity(4, dtype=np.float32)
mat[0, 0] = cy * cz
mat[0, 1] = sx * sy * cz - cx * sz
mat[0, 2] = cx * sy * cz + sx * sz
mat[1, 0] = cy * sz
mat[1, 1] = sx * sy * sz + cx * cz
mat[1, 2] = cx * sy * sz - sx * cz
mat[2, 0] = -sy
mat[2, 1] = sx * cy
mat[2, 2] = cx * cy
self.t = mat @ self.t
return self
def lookat(
self, x: float, y: float, z: float, up: np.ndarray = np.array([0.0, 1.0, 0.0])
):
p = self.t[0:3, 3]
q = np.array([x, y, z])
zaxis = Transform.normalize(q - p)
xaxis = Transform.normalize(np.cross(zaxis, up))
yaxis = Transform.normalize(np.cross(xaxis, zaxis))
self.t[:3, 0] = xaxis
self.t[:3, 1] = yaxis
self.t[:3, 2] = -zaxis
return self
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"
class Object:
def __init__(self) -> None:
self.transform: Transform = Transform()
self.texture: Texture = Texture()
def parse_args(self, args: list[str]):
raise NotImplementedError
def sdf_block(self) -> str:
raise NotImplementedError
@make_obj("cube")
class Cube(Object):
def __init__(self, _size: np.ndarray = np.array([1.0, 1.0, 1.0])) -> None:
super().__init__()
self.size = _size
def parse_args(self, args: list[str]):
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]}))"
@make_obj("sphere")
class Sphere(Object):
def __init__(self, _radius: float = 1.0) -> None:
super().__init__()
self.radius = _radius
def parse_args(self, args: list[str]):
self.radius = ArgParser.as_float(args)
def sdf_block(self) -> str:
return f"sdSphere({self.transform.p_expr()}, {self.radius})"
@make_obj("camera", ["cam"])
class Camera(Object):
def __init__(self, _focus: float = 1.0) -> None:
super().__init__()
self.focus = _focus
def parse_args(self, args: list[str]):
self.focus = ArgParser.as_float(args)