306 lines
14 KiB
Python
306 lines
14 KiB
Python
from stdint import *
|
|
import vipermath
|
|
import string
|
|
import t, c
|
|
|
|
|
|
PI: t.CDefine = float(3.14159265358979323846)
|
|
|
|
SPHERE_SLICES: t.CDefine = (24 * 1)
|
|
SPHERE_STACKS: t.CDefine = (16 * 1)
|
|
SIN_WAVE_GRID: t.CDefine = (40 * 1)
|
|
|
|
def COLOR_RGB(r: int, g: int, b: int) -> t.CInline | t.CInt:
|
|
return (255 << 24) | (r << 16) | (g << 8) | (b)
|
|
|
|
@t.Object
|
|
class Vec3:
|
|
x: float
|
|
y: float
|
|
z: float
|
|
def __init__(self, x: float, y: float, z: float):
|
|
self.x = x
|
|
self.y = y
|
|
self.z = z
|
|
def Sub(self, b: 'Vec3') -> 'Vec3':
|
|
return Vec3(self.x - b.x, self.y - b.y, self.z - b.z)
|
|
def Cross(self, b: 'Vec3') -> 'Vec3':
|
|
return Vec3(self.y * b.z - self.z * b.y, self.z * b.x - self.x * b.z, self.x * b.y - self.y * b.x)
|
|
def Dot(self, b: 'Vec3') -> float:
|
|
return self.x * b.x + self.y * b.y + self.z * b.z
|
|
def Norm(self) -> 'Vec3':
|
|
l = vipermath.sqrtf(self.Dot(self))
|
|
return Vec3(self.x / l, self.y / l, self.z / l)
|
|
|
|
class Mat4:
|
|
m: t.CArray[t.CArray[float, 4], 4]
|
|
|
|
def Mat4_Identity() -> Mat4:
|
|
m: Mat4 = Mat4()
|
|
string.memset(c.Addr(m), 0, Mat4.__sizeof__())
|
|
m.m[0][0] = 1; m.m[1][1] = 1; m.m[2][2] = 1; m.m[3][3] = 1
|
|
return m
|
|
|
|
def Mat4_Mul(a: Mat4, b: Mat4) -> Mat4:
|
|
res: Mat4 = Mat4()
|
|
string.memset(c.Addr(res), 0, Mat4.__sizeof__())
|
|
for i in range(4):
|
|
for j in range(4):
|
|
for k in range(4):
|
|
res.m[i][j] += a.m[i][k] * b.m[k][j]
|
|
return res
|
|
|
|
def Mat4_MulVec(m: Mat4, v: Vec3) -> Vec3:
|
|
x: float = m.m[0][0] * v.x + m.m[0][1] * v.y + m.m[0][2] * v.z + m.m[0][3]
|
|
y: float = m.m[1][0] * v.x + m.m[1][1] * v.y + m.m[1][2] * v.z + m.m[1][3]
|
|
z: float = m.m[2][0] * v.x + m.m[2][1] * v.y + m.m[2][2] * v.z + m.m[2][3]
|
|
w: float = m.m[3][0] * v.x + m.m[3][1] * v.y + m.m[3][2] * v.z + m.m[3][3]
|
|
if w != 0:
|
|
x /= w; y /= w; z /= w
|
|
return Vec3(x, y, z)
|
|
|
|
def Mat4_Perspective(fov: float, aspect: float, zn: float, zf: float) -> Mat4:
|
|
m: Mat4 = Mat4()
|
|
string.memset(c.Addr(m), 0, Mat4.__sizeof__())
|
|
f: float = float(1.0) / vipermath.tanf(fov / float(2.0))
|
|
m.m[0][0] = f / aspect; m.m[1][1] = f
|
|
m.m[2][2] = zf / (zn - zf); m.m[2][3] = (zf * zn) / (zn - zf); m.m[3][2] = -1
|
|
return m
|
|
|
|
def Mat4_LookAt(eye: Vec3, target: Vec3, up: Vec3) -> Mat4:
|
|
zaxis: Vec3 = eye.Sub(target).Norm()
|
|
xaxis: Vec3 = up.Cross(zaxis).Norm()
|
|
yaxis: Vec3 = zaxis.Cross(xaxis)
|
|
m: Mat4 = Mat4_Identity()
|
|
m.m[0][0]=xaxis.x; m.m[0][1]=xaxis.y; m.m[0][2]=xaxis.z; m.m[0][3]= -xaxis.Dot(eye)
|
|
m.m[1][0]=yaxis.x; m.m[1][1]=yaxis.y; m.m[1][2]=yaxis.z; m.m[1][3]= -yaxis.Dot(eye)
|
|
m.m[2][0]=zaxis.x; m.m[2][1]=zaxis.y; m.m[2][2]=zaxis.z; m.m[2][3]= -zaxis.Dot(eye)
|
|
m.m[3][3] = 1
|
|
return m
|
|
|
|
def Mat4_Translate(x: float, y: float, z: float) -> Mat4:
|
|
m: Mat4 = Mat4_Identity()
|
|
m.m[0][3] = x; m.m[1][3] = y; m.m[2][3] = z
|
|
return m
|
|
|
|
def Mat4_RotateY(a: float) -> Mat4:
|
|
m: Mat4 = Mat4_Identity()
|
|
m.m[0][0] = vipermath.cosf(a); m.m[0][2] = vipermath.sinf(a)
|
|
m.m[2][0] = -vipermath.sinf(a); m.m[2][2] = vipermath.cosf(a)
|
|
return m
|
|
|
|
|
|
@t.Object
|
|
class Surface:
|
|
fb: UINT32PTR
|
|
zb: float | t.CPtr
|
|
w: int
|
|
h: int
|
|
def __init__(self, fb: UINT32PTR, zb: float | t.CPtr, w: int, h: int):
|
|
self.fb = fb
|
|
self.zb = zb
|
|
self.w = w
|
|
self.h = h
|
|
|
|
@t.Object
|
|
class Renderer:
|
|
_fb: UINT32PTR
|
|
_zb: float | t.CPtr
|
|
_w: int
|
|
_h: int
|
|
|
|
def __init__(self, fb: UINT32PTR, zb: float | t.CPtr, w: int, h: int):
|
|
self._fb = fb
|
|
self._zb = zb
|
|
self._w = w
|
|
self._h = h
|
|
|
|
def Clear(self, color: t.CUInt32T):
|
|
fb: UINT32PTR = self._fb
|
|
zb: float | t.CPtr = self._zb
|
|
size: int = self._w * self._h
|
|
for i in range(size):
|
|
fb[i] = color
|
|
zb[i] = float(1.0)
|
|
|
|
def DrawLine2D(self, x0: int, y0: int, x1: int, y1: int, col: t.CUInt32T):
|
|
fb: UINT32PTR = self._fb
|
|
w: int = self._w; h: int = self._h
|
|
dx: int = abs(x1-x0); dy: int = abs(y1-y0)
|
|
sx: int = 1 if x0 < x1 else -1; sy: int = 1 if y0 < y1 else -1
|
|
err: int = dx - dy
|
|
while True:
|
|
if x0 >= 0 and x0 < w and y0 >= 0 and y0 < h: fb[y0 * w + x0] = col
|
|
if x0 == x1 and y0 == y1: break
|
|
e2: int = 2 * err
|
|
if e2 > -dy: err -= dy; x0 += sx
|
|
if e2 < dx: err += dx; y0 += sy
|
|
|
|
def DrawTriangle(self, v0: Vec3, v1: Vec3, v2: Vec3, col: t.CUInt32T):
|
|
if v0.z < float(0.0) or v1.z < float(0.0) or v2.z < float(0.0): return
|
|
if (v0.x > float(10.0) or v0.x < float(-10.0) or v0.y > float(10.0) or v0.y < float(-10.0) or
|
|
v1.x > float(10.0) or v1.x < float(-10.0) or v1.y > float(10.0) or v1.y < float(-10.0) or
|
|
v2.x > float(10.0) or v2.x < float(-10.0) or v2.y > float(10.0) or v2.y < float(-10.0)): return
|
|
|
|
fb: UINT32PTR = self._fb
|
|
zb: float | t.CPtr = self._zb
|
|
w: int = self._w
|
|
h: int = self._h
|
|
|
|
sx0: float = (v0.x + float(1.0)) * float(0.5) * w; sy0: float = (float(1.0) - v0.y) * float(0.5) * h
|
|
sx1: float = (v1.x + float(1.0)) * float(0.5) * w; sy1: float = (float(1.0) - v1.y) * float(0.5) * h
|
|
sx2: float = (v2.x + float(1.0)) * float(0.5) * w; sy2: float = (float(1.0) - v2.y) * float(0.5) * h
|
|
|
|
minX: int = max(0, int(vipermath.floorf(min(min(sx0, sx1), sx2))))
|
|
maxX: int = min(w - 1, int(vipermath.ceilf(max(max(sx0, sx1), sx2))))
|
|
minY: int = max(0, int(vipermath.floorf(min(min(sy0, sy1), sy2))))
|
|
maxY: int = min(h - 1, int(vipermath.ceilf(max(max(sy0, sy1), sy2))))
|
|
|
|
dx12: float = sx1 - sx2; dy12: float = sy1 - sy2
|
|
dx02: float = sx0 - sx2; dy02: float = sy0 - sy2
|
|
denom: float = dx12 * dy02 - dy12 * dx02
|
|
if denom < float(0.001) and denom > float(-0.001): return
|
|
invDenom: float = float(1.0) / denom
|
|
|
|
row_dxP_start: float = float(minX) - sx2
|
|
for y in range(minY, maxY + 1):
|
|
dyP: float = float(y) - sy2
|
|
dxP: float = row_dxP_start
|
|
row_base: int = y * w
|
|
for x in range(minX, maxX + 1):
|
|
w0: float = (dx12 * dyP - dy12 * dxP) * invDenom
|
|
w1: float = (dxP * dy02 - dyP * dx02) * invDenom
|
|
w2: float = float(1.0) - w0 - w1
|
|
if w0 >= 0 and w1 >= 0 and w2 >= 0:
|
|
z: float = w0 * v0.z + w1 * v1.z + w2 * v2.z
|
|
idx: int = row_base + x
|
|
if z < zb[idx]:
|
|
zb[idx] = z
|
|
fb[idx] = col
|
|
dxP += float(1.0)
|
|
|
|
def DrawGlowDot(self, x: float, y: float, radius: float, core_col: t.CUInt32T, intensity: float):
|
|
fb: UINT32PTR = self._fb; w: int = self._w; h: int = self._h
|
|
r: int = int(radius) + 4
|
|
ix: int = int(x); iy: int = int(y)
|
|
for dy in range(-r, r + 1):
|
|
py: int = iy + dy
|
|
if py < 0 or py >= h: continue
|
|
for dx in range(-r, r + 1):
|
|
px: int = ix + dx
|
|
if px < 0 or px >= w: continue
|
|
dist: float = vipermath.sqrtf(float(dx*dx + dy*dy))
|
|
if dist < radius:
|
|
self.BlendPixel(px, py, core_col, intensity)
|
|
elif dist < float(r):
|
|
fade: float = 1.0 - (dist - radius) / 4.0
|
|
self.BlendPixel(px, py, core_col, intensity * fade * 0.5)
|
|
|
|
def BlendPixel(self, x: int, y: int, col: t.CUInt32T, alpha: float):
|
|
if alpha <= 0.0: return
|
|
fb: UINT32PTR = self._fb; w: int = self._w; h: int = self._h
|
|
if x < 0 or x >= w or y < 0 or y >= h: return
|
|
|
|
idx: int = y * w + x
|
|
bg: t.CUInt32T = fb[idx]
|
|
bg_b: int = bg & 0xFF; bg_g: int = (bg >> 8) & 0xFF; bg_r: int = (bg >> 16) & 0xFF
|
|
f_b: int = col & 0xFF; f_g: int = (col >> 8) & 0xFF; f_r: int = (col >> 16) & 0xFF
|
|
|
|
a: int = int(alpha * 255.0)
|
|
inv_a: int = 255 - a
|
|
nr: int = (f_r * a + bg_r * inv_a) >> 8
|
|
ng: int = (f_g * a + bg_g * inv_a) >> 8
|
|
nb: int = (f_b * a + bg_b * inv_a) >> 8
|
|
|
|
fb[idx] = COLOR_RGB(255 if (nr > 255) else nr,
|
|
255 if (ng > 255) else ng,
|
|
255 if (nb > 255) else nb)
|
|
|
|
|
|
@t.Object
|
|
class Scene3D:
|
|
renderer: Renderer
|
|
|
|
def __init__(self, fb: UINT32PTR, zb: float | t.CPtr, w: int, h: int):
|
|
self.renderer = Renderer(fb, zb, w, h)
|
|
|
|
def Render(self, time: float):
|
|
rw: int = self.renderer._w
|
|
rh: int = self.renderer._h
|
|
self.renderer.Clear(COLOR_RGB(15, 15, 25))
|
|
|
|
yaw: float = float(45.0) * PI / float(180.0); pitch: float = float(45.0) * PI / float(180.0); radius: float = float(10.0)
|
|
eye: Vec3 = Vec3(radius*vipermath.cosf(pitch)*vipermath.cosf(yaw), radius*vipermath.sinf(pitch), radius*vipermath.cosf(pitch)*vipermath.sinf(yaw))
|
|
target: Vec3 = Vec3(0, 0, 0); up: Vec3 = Vec3(0, 1, 0)
|
|
view: Mat4 = Mat4_LookAt(eye, target, up)
|
|
proj: Mat4 = Mat4_Perspective(PI / float(3.0), float(rw)/float(rh), float(0.1), float(100.0))
|
|
vp: Mat4 = Mat4_Mul(proj, view)
|
|
|
|
cubeX: float = float(-3.5)
|
|
cubeMVP: Mat4 = Mat4_Mul(vp, Mat4_Translate(cubeX, 0, 0))
|
|
self._DrawCube(cubeMVP, COLOR_RGB(180, 180, 180))
|
|
|
|
colors: t.CArray[t.CUInt32T, 4] = [COLOR_RGB(255,50,50), COLOR_RGB(50,50,255), COLOR_RGB(255,255,50), COLOR_RGB(50,255,255)]
|
|
for i in range(4):
|
|
a: float = time * float(1.5) + i * (PI / float(2.0)); r: float = float(3.0)
|
|
pos: Vec3 = Vec3(vipermath.cosf(a) * r + cubeX, 0, vipermath.sinf(a) * r)
|
|
sphereMVP: Mat4 = Mat4_Mul(vp, Mat4_Translate(pos.x, pos.y, pos.z))
|
|
self._DrawSphere(sphereMVP, Vec3(0,0,0), float(0.5), colors[i])
|
|
|
|
self._Draw3DSinWave(vp, Vec3(float(3.5), 0, 0), time)
|
|
|
|
self.renderer.DrawLine2D(50, rh - 150, rw - 50, rh - 150, COLOR_RGB(80, 80, 80))
|
|
self.renderer.DrawLine2D(50, rh - 50, 50, rh - 250, COLOR_RGB(80, 80, 80))
|
|
wave_shift: float = time * float(2.0)
|
|
prev_x: int = 50; prev_y: int = rh - 150 - int(vipermath.sinf(0 - wave_shift) * float(100.0))
|
|
for i in range(1, rw - 100):
|
|
tr: float = float(i) / float(rw-100) * float(4.0) * PI
|
|
cx: int = 50 + i; cy: int = rh - 150 - int(vipermath.sinf(tr - wave_shift) * float(100.0))
|
|
self.renderer.DrawLine2D(prev_x, prev_y, cx, cy, COLOR_RGB(0, 255, 100))
|
|
prev_x = cx; prev_y = cy
|
|
|
|
def _DrawCube(self, mvp: Mat4, col: t.CUInt32T):
|
|
v: t.CArray[Vec3, 8] = [
|
|
Vec3(-1, -1, -1), Vec3(-1, -1, 1), Vec3(-1, 1, -1), Vec3(-1, 1, 1),
|
|
Vec3( 1, -1, -1), Vec3( 1, -1, 1), Vec3( 1, 1, -1), Vec3( 1, 1, 1)
|
|
]
|
|
for i in range(8): v[i] = Mat4_MulVec(mvp, v[i])
|
|
faces: t.CArray[t.CArray[int, 3], 12] = [
|
|
[0, 2, 1], [1, 2, 3], [4, 1, 5], [4, 0, 1], [6, 3, 2], [6, 7, 3],
|
|
[4, 6, 0], [0, 6, 2], [5, 7, 4], [4, 7, 6], [1, 3, 5], [5, 3, 7]
|
|
]
|
|
for i in range(12): self.renderer.DrawTriangle(v[faces[i][0]], v[faces[i][1]], v[faces[i][2]], col)
|
|
|
|
def _DrawSphere(self, mvp: Mat4, center: Vec3, radius: float, col: t.CUInt32T):
|
|
step_pi: float = PI / SPHERE_STACKS; step_2pi: float = float(2.0) * PI / SPHERE_SLICES
|
|
for i in range(SPHERE_STACKS):
|
|
for j in range(SPHERE_SLICES):
|
|
theta1: float = i * step_pi; theta2: float = (i + 1) * step_pi
|
|
phi1: float = j * step_2pi; phi2: float = (j + 1) * step_2pi
|
|
v: t.CArray[Vec3, 4]
|
|
v[0] = Vec3(center.x+radius*vipermath.sinf(theta1)*vipermath.cosf(phi1), center.y+radius*vipermath.cosf(theta1), center.z+radius*vipermath.sinf(theta1)*vipermath.sinf(phi1))
|
|
v[1] = Vec3(center.x+radius*vipermath.sinf(theta1)*vipermath.cosf(phi2), center.y+radius*vipermath.cosf(theta1), center.z+radius*vipermath.sinf(theta1)*vipermath.sinf(phi2))
|
|
v[2] = Vec3(center.x+radius*vipermath.sinf(theta2)*vipermath.cosf(phi1), center.y+radius*vipermath.cosf(theta2), center.z+radius*vipermath.sinf(theta2)*vipermath.sinf(phi1))
|
|
v[3] = Vec3(center.x+radius*vipermath.sinf(theta2)*vipermath.cosf(phi2), center.y+radius*vipermath.cosf(theta2), center.z+radius*vipermath.sinf(theta2)*vipermath.sinf(phi2))
|
|
for k in range(4): v[k] = Mat4_MulVec(mvp, v[k])
|
|
self.renderer.DrawTriangle(v[0], v[1], v[2], col)
|
|
self.renderer.DrawTriangle(v[1], v[3], v[2], col)
|
|
|
|
def _Draw3DSinWave(self, vp: Mat4, pos: Vec3, time: float):
|
|
_range: float = float(2.0) * PI; step: float = _range / SIN_WAVE_GRID; scale: float = float(0.5)
|
|
mvp: Mat4 = Mat4_Mul(vp, Mat4_Mul(Mat4_Translate(pos.x, pos.y, pos.z), Mat4_RotateY(time * float(1.0))))
|
|
for i in range(SIN_WAVE_GRID):
|
|
for j in range(SIN_WAVE_GRID):
|
|
x0: float = -PI + i * step; z0 = -PI + j * step; x1: float = x0 + step; z1 = z0 + step
|
|
y00: float = vipermath.sinf(x0) * vipermath.cosf(z0) * scale; y10: float = vipermath.sinf(x1) * vipermath.cosf(z0) * scale
|
|
y01: float = vipermath.sinf(x0) * vipermath.cosf(z1) * scale; y11: float = vipermath.sinf(x1) * vipermath.cosf(z1) * scale
|
|
|
|
t1: float = (y00 / scale + float(1.0)) * float(0.5); r1: int = int(50 + 205 * t1); g1: int = int(80 + 120 * vipermath.sinf(t1 * PI)); b1: int = int(255 - 205 * t1)
|
|
col1: t.CUInt32T = COLOR_RGB(255 if (r1 > 255) else (0 if (r1 < 0) else r1), 255 if (g1 > 255) else (0 if (g1 < 0) else g1), 255 if (b1 > 255) else (0 if (b1 < 0) else b1))
|
|
t2: float = (y11 / scale + float(1.0)) * float(0.5); r2: int = int(50 + 205 * t2); g2: int = int(80 + 120 * vipermath.sinf(t2 * PI)); b2: int = int(255 - 205 * t2)
|
|
col2: t.CUInt32T = COLOR_RGB(255 if (r2 > 255) else (0 if (r2 < 0) else r2), 255 if (g2 > 255) else (0 if (g2 < 0) else g2), 255 if (b2 > 255) else (0 if (b2 < 0) else b2))
|
|
|
|
v00: Vec3 = Mat4_MulVec(mvp, Vec3(x0, y00, z0)); v10: Vec3 = Mat4_MulVec(mvp, Vec3(x1, y10, z0))
|
|
v01: Vec3 = Mat4_MulVec(mvp, Vec3(x0, y01, z1)); v11: Vec3 = Mat4_MulVec(mvp, Vec3(x1, y11, z1))
|
|
self.renderer.DrawTriangle(v00, v10, v11, col1); self.renderer.DrawTriangle(v00, v11, v01, col2)
|