INV / helium /transform3d.py
Fred808's picture
Upload 256 files
7a0c684 verified
"""
3D transformation module for GPU-accelerated geometry processing
"""
from typing import Tuple, Optional, Union
import numpy as np
class Transform3D:
"""GPU-accelerated 3D transformation operations"""
def __init__(self, driver):
self.driver = driver
self.prefix = "transform3d"
def projection_matrix(
self,
fov_y: float = 60.0,
aspect: float = 1.0,
near: float = 0.1,
far: float = 1000.0,
perspective: bool = True
) -> str:
"""Create projection matrix in driver memory"""
if perspective:
# Perspective projection
f = 1.0 / np.tan(np.radians(fov_y) / 2.0)
mat = np.zeros((4, 4), dtype=np.float32)
mat[0, 0] = f / aspect
mat[1, 1] = f
mat[2, 2] = (far + near) / (near - far)
mat[2, 3] = 2.0 * far * near / (near - far)
mat[3, 2] = -1.0
else:
# Orthographic projection
mat = np.zeros((4, 4), dtype=np.float32)
mat[0, 0] = 2.0 / aspect
mat[1, 1] = 2.0
mat[2, 2] = -2.0 / (far - near)
mat[2, 3] = -(far + near) / (far - near)
mat[3, 3] = 1.0
mat_name = f"{self.prefix}_proj_matrix"
self.driver.create_tensor(mat_name, mat)
return mat_name
def view_matrix(
self,
eye: Tuple[float, float, float],
target: Tuple[float, float, float],
up: Tuple[float, float, float] = (0, 1, 0)
) -> str:
"""Create view matrix in driver memory"""
eye = np.array(eye, dtype=np.float32)
target = np.array(target, dtype=np.float32)
up = np.array(up, dtype=np.float32)
# Calculate forward (negative z), right, and up vectors
forward = target - eye
forward = forward / np.linalg.norm(forward)
right = np.cross(forward, up)
right = right / np.linalg.norm(right)
up = np.cross(right, forward)
up = up / np.linalg.norm(up)
# Build view matrix
mat = np.zeros((4, 4), dtype=np.float32)
mat[0, :3] = right
mat[1, :3] = up
mat[2, :3] = -forward
mat[0, 3] = -np.dot(right, eye)
mat[1, 3] = -np.dot(up, eye)
mat[2, 3] = np.dot(forward, eye)
mat[3, 3] = 1.0
mat_name = f"{self.prefix}_view_matrix"
self.driver.create_tensor(mat_name, mat)
return mat_name
def model_matrix(
self,
translation: Tuple[float, float, float] = (0, 0, 0),
rotation: Tuple[float, float, float] = (0, 0, 0),
scale: Union[float, Tuple[float, float, float]] = 1.0
) -> str:
"""Create model matrix in driver memory"""
# Scale matrix
if isinstance(scale, (int, float)):
scale = (scale, scale, scale)
sx, sy, sz = scale
# Rotation matrices (Euler angles in degrees)
rx, ry, rz = map(np.radians, rotation)
rot_x = np.array([
[1, 0, 0, 0],
[0, np.cos(rx), -np.sin(rx), 0],
[0, np.sin(rx), np.cos(rx), 0],
[0, 0, 0, 1]
], dtype=np.float32)
rot_y = np.array([
[np.cos(ry), 0, np.sin(ry), 0],
[0, 1, 0, 0],
[-np.sin(ry), 0, np.cos(ry), 0],
[0, 0, 0, 1]
], dtype=np.float32)
rot_z = np.array([
[np.cos(rz), -np.sin(rz), 0, 0],
[np.sin(rz), np.cos(rz), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
], dtype=np.float32)
# Translation matrix
tx, ty, tz = translation
# Combine transformations
mat = np.eye(4, dtype=np.float32)
mat = mat @ rot_z @ rot_y @ rot_x # Rotation order: Z-Y-X
mat[0, 0] *= sx; mat[1, 1] *= sy; mat[2, 2] *= sz # Scale
mat[0:3, 3] = [tx, ty, tz] # Translation
mat_name = f"{self.prefix}_model_matrix"
self.driver.create_tensor(mat_name, mat)
return mat_name
def transform_vertices(
self,
vertices_name: str,
model_matrix_name: Optional[str] = None,
view_matrix_name: Optional[str] = None,
proj_matrix_name: Optional[str] = None
) -> str:
"""Transform vertices through model-view-projection pipeline"""
vertices = self.driver.get_tensor(vertices_name)
# Convert to homogeneous coordinates if needed
if vertices.shape[-1] == 3:
ones = np.ones((*vertices.shape[:-1], 1), dtype=vertices.dtype)
vertices = np.concatenate([vertices, ones], axis=-1)
homogeneous_name = f"{self.prefix}_homogeneous"
self.driver.create_tensor(homogeneous_name, vertices)
vertices_name = homogeneous_name
result_name = vertices_name
# Apply model transformation
if model_matrix_name is not None:
result_name = f"{self.prefix}_model_result"
self.driver.matmul(vertices_name, model_matrix_name, out=result_name)
# Apply view transformation
if view_matrix_name is not None:
view_result = f"{self.prefix}_view_result"
self.driver.matmul(result_name, view_matrix_name, out=view_result)
result_name = view_result
# Apply projection transformation
if proj_matrix_name is not None:
proj_result = f"{self.prefix}_proj_result"
self.driver.matmul(result_name, proj_matrix_name, out=proj_result)
result_name = proj_result
# Perform perspective divide if needed
if self.driver.get_tensor(proj_matrix_name)[3, 2] != 0: # Check if perspective
verts = self.driver.get_tensor(result_name)
w = verts[..., 3:]
verts = verts / w # Perspective divide
self.driver.create_tensor(result_name, verts)
return result_name
def normal_matrix(self, model_matrix_name: str) -> str:
"""Create normal matrix (inverse transpose of 3x3 model matrix)"""
model_mat = self.driver.get_tensor(model_matrix_name)
normal_mat = np.linalg.inv(model_mat[:3, :3]).T
mat_name = f"{self.prefix}_normal_matrix"
self.driver.create_tensor(mat_name, normal_mat)
return mat_name
def transform_normals(
self,
normals_name: str,
normal_matrix_name: str
) -> str:
"""Transform normal vectors using normal matrix"""
result_name = f"{self.prefix}_transformed_normals"
self.driver.matmul(normals_name, normal_matrix_name, out=result_name)
# Renormalize
normals = self.driver.get_tensor(result_name)
normals = normals / np.linalg.norm(normals, axis=-1, keepdims=True)
self.driver.create_tensor(result_name, normals)
return result_name