Created
August 11, 2023 04:14
-
-
Save tigercoding56/4391935c64fae79f182e303e98c4a23b to your computer and use it in GitHub Desktop.
lobster3d functions (credit to https://pypi.org/project/Lobster-3dEngine/#files i just added function for merging multiple objects)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from math import sin, cos, tan, sqrt, pi | |
class Object: | |
def __init__(self, position, vertices, faces, colors): | |
self.position = position | |
self.vertices = vertices | |
self.faces = faces | |
self.colors = colors | |
class Camera: | |
def __init__(self, position, rotation): | |
self.position = position | |
self.rotation = rotation | |
def get_rotation_matrix(self): | |
return rotation_matrix(*self.rotation) | |
class Vector: | |
def __init__(self, vector): | |
self.vector = vector | |
def __add__(self, other): | |
return Vector([a + b for a, b in zip(self.vector, other.vector)]) | |
def __sub__(self, other): | |
return Vector([a - b for a, b in zip(self.vector, other.vector)]) | |
def dot_product(self, other): | |
"""returns the dot product of two 3D vectors""" | |
return self.vector[0] * other.vector[0] + self.vector[1] * other.vector[1] + self.vector[2] * other.vector[2] | |
def cross_product(self, other): | |
"""returns the cross product of two 3D vectors""" | |
return (self.vector[1] * other.vector[2] - self.vector[2] * other.vector[1], | |
self.vector[2] * other.vector[0] - self.vector[0] * other.vector[2], | |
self.vector[0] * other.vector[1] - self.vector[1] * other.vector[0]) | |
class Matrices: | |
def __init__(self, matrix): | |
self.matrix = matrix | |
self.rows = len(self.matrix) | |
self.columns = len(self.matrix[0]) | |
def __mul__(self, other): | |
"""multiples 2 matrices together""" | |
assert self.columns == other.rows, "Multiplying matrix rules not met" # remove this line | |
new_matrix_dimensions = (self.rows, other.columns) | |
new_matrix = list() | |
for a in range(self.rows): | |
for b in range(other.columns): | |
value = 0 | |
for c in range(other.rows): | |
value += self.matrix[a][c] * other.matrix[c][b] | |
new_matrix.append(round(value, 10)) | |
new_matrix = [new_matrix[i:i + new_matrix_dimensions[1]] | |
for i in range(0, len(new_matrix), new_matrix_dimensions[1])] | |
return Matrices(new_matrix) | |
def init(screen_width, screen_height, field_of_view, z_near, z_far): | |
"""required to initiate this library""" | |
global _screen_height, _screen_width, _aspect_ratio, _field_of_view, _z_near, _z_far | |
_screen_width = screen_width | |
_screen_height = screen_height | |
_aspect_ratio = screen_height / screen_width | |
_field_of_view = field_of_view | |
_z_near = z_near | |
_z_far = z_far | |
def rotation_matrix(x, y, z) -> Matrices: | |
"""rotation matrix of x, y, z radians around x, y, z axes (respectively)""" | |
sx, cx = sin(x), cos(x) | |
sy, cy = sin(y), cos(y) | |
sz, cz = sin(z), cos(z) | |
return Matrices(( | |
(cy * cz, -cy * sz, sy), | |
(cx * sz + sx * sy * cz, cx * cz - sz * sx * sy, -cy * sx), | |
(sz * sx - cx * sy * cz, cx * sz * sy + sx * cz, cx * cy) | |
)) | |
def get_normal(vector0, vector1, vector2): | |
A = vector1 - vector0 | |
B = vector2 - vector0 | |
vector = A.cross_product(B) | |
magnitude = sqrt(vector[0] ** 2 + vector[1] ** 2 + vector[2] ** 2) | |
return vector[0] / magnitude, vector[1] / magnitude, vector[2] / magnitude | |
def projection(vertex): | |
if vertex[2] == 0: | |
vertex[2] += 0.001 | |
field_of_view = 1 / tan(_field_of_view / 2) | |
q_val = _z_far / (_z_far - _z_near) | |
x = _aspect_ratio * field_of_view * vertex[0] / vertex[2] | |
y = field_of_view * vertex[1] / vertex[2] | |
z = vertex[2] * q_val - _z_near * q_val | |
return x, y, z | |
def display(obj: Object, cam: Camera): | |
points_list = [] | |
camera_rotation = cam.get_rotation_matrix() | |
for faces, color in zip(obj.faces, obj.colors): | |
vertices = [projection((Vector((Matrices([obj.vertices[vertex]]) * camera_rotation).matrix[0]) + | |
Vector(obj.position) - Vector(cam.position)).vector) for vertex in faces] | |
normal_vector = Vector(get_normal(Vector(vertices[0][:3]), Vector(vertices[1][:3]), Vector(vertices[2][:3]))) | |
if normal_vector.dot_product(Vector(vertices[0]) - Vector(cam.position)) < 0.2: # normal_vector.vector[2] < 0: | |
points = [(int((vertex[0] + 1) / 2 * _screen_width), | |
int(_screen_height - (vertex[1] + 1) / 2 * _screen_height)) for vertex in vertices] | |
points_list.append((points, vertices[0][2], color)) | |
points_list = sorted(points_list, key=lambda z: z[1], reverse=True) | |
return points_list | |
def merge_objects(obj1, obj2): | |
merged_vertices = [] | |
merged_faces = [] | |
merged_colors = [] | |
# Add the position of obj1 to its vertices | |
for vertex in obj1.vertices: | |
merged_vertices.append(tuple(v + p for v, p in zip(vertex, obj1.position))) | |
# Add the position of obj2 to its vertices | |
for vertex in obj2.vertices: | |
merged_vertices.append(tuple(v + p for v, p in zip(vertex, obj2.position))) | |
# Merge the faces and colors | |
merged_faces.extend(obj1.faces) | |
merged_faces.extend(obj2.faces) | |
merged_colors.extend(obj1.colors) | |
merged_colors.extend(obj2.colors) | |
merged_object = Object(vertices=merged_vertices, | |
position=[0, 0, 0], | |
faces=merged_faces, | |
colors=merged_colors) | |
return merged_object | |
import numpy as np | |
import cv2 | |
def warp(surf, warp_pts,smooth=False): | |
"""Stretches a pygame surface to fill a quad using cv2's perspective warp. | |
Args: | |
surf: The surface to transform. | |
warp_pts: A list of four xy coordinates representing the polygon to fill. | |
Points should be specified in clockwise order starting from the top left. | |
smooth: Whether to use linear interpolation for the image transformation. | |
If false, nearest neighbor will be used. | |
out: An optional surface to use for the final output. If None or not | |
the correct size, a new surface will be made instead. | |
Returns: | |
[0]: A Surface containing the warped image. | |
[1]: A Rect describing where to blit the output surface to make its coordinates | |
match the input coordinates. | |
""" | |
if len(warp_pts) != 4: | |
raise ValueError("warp_pts must contain four points") | |
out = None | |
w, h = surf.get_size() | |
is_alpha = surf.get_flags() & pygame.SRCALPHA | |
# XXX throughout this method we need to swap x and y coordinates | |
# when we pass stuff between pygame and cv2. I'm not sure why .-. | |
src_corners = numpy.float32([(0, 0), (0, w), (h, w), (h, 0)]) | |
quad = [tuple(reversed(p)) for p in warp_pts] | |
# find the bounding box of warp points | |
# (this gives the size and position of the final output surface). | |
min_x, max_x = float('inf'), -float('inf') | |
min_y, max_y = float('inf'), -float('inf') | |
for p in quad: | |
min_x, max_x = min(min_x, p[0]), max(max_x, p[0]) | |
min_y, max_y = min(min_y, p[1]), max(max_y, p[1]) | |
warp_bounding_box = pygame.Rect(int(min_x), int(min_y), | |
int(max_x - min_x), | |
int(max_y - min_y)) | |
shifted_quad = [(p[0] - min_x, p[1] - min_y) for p in quad] | |
dst_corners = numpy.float32(shifted_quad) | |
mat = cv2.getPerspectiveTransform(src_corners, dst_corners) | |
orig_rgb = pygame.surfarray.pixels3d(surf) | |
flags = cv2.INTER_LINEAR if smooth else cv2.INTER_NEAREST | |
out_rgb = cv2.warpPerspective(orig_rgb, mat, warp_bounding_box.size, flags=flags) | |
if out is None or out.get_size() != out_rgb.shape[0:2]: | |
out = pygame.Surface(out_rgb.shape[0:2], pygame.SRCALPHA if is_alpha else 0) | |
pygame.surfarray.blit_array(out, out_rgb) | |
if is_alpha: | |
orig_alpha = pygame.surfarray.pixels_alpha(surf) | |
out_alpha = cv2.warpPerspective(orig_alpha, mat, warp_bounding_box.size, flags=flags) | |
alpha_px = pygame.surfarray.pixels_alpha(out) | |
alpha_px[:] = out_alpha | |
else: | |
out.set_colorkey(surf.get_colorkey()) | |
# XXX swap x and y once again... | |
return out | |
def stretch_texture_to_polygon(screen, points, texture): | |
texture = warp(texture,points) | |
min_x = min(point[0] for point in points) | |
max_x = max(point[0] for point in points) | |
min_y = min(point[1] for point in points) | |
max_y = max(point[1] for point in points) | |
screen.blit(texture,(min_x, min_y)) | |
#screen.blit(texture, (0, 0)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment