robolib.utils.raylibutils
raylibutils.py - functions to interact between python and raylib or helpers
1"""raylibutils.py - functions to interact between python and raylib or helpers""" 2from __future__ import annotations 3from typing import Any 4from dataclasses import dataclass 5from pathlib import Path 6 7import raylib as rl 8import numpy as np 9 10from .utils import logger 11 12RlTexture = Any 13RlRenderTexture = Any 14RlModel = Any 15RlVector3 = Any 16RlMesh = Any 17 18@dataclass 19class RlModelWithTexture: 20 """basic raylib model with an optional texture applied to it""" 21 model: RlModel 22 texture: RlTexture | None 23 _bbox: tuple[np.ndarray, np.ndarray] | None = None 24 25 def __post_init__(self): 26 if self.texture is not None: 27 self.model.materials.maps[rl.MATERIAL_MAP_ALBEDO].texture = self.texture 28 29 @staticmethod 30 def from_path(model_path: Path | str, texture_path: Path | str | None = None) -> RlModelWithTexture: 31 """builds a RLModelWithTexture from a path""" 32 model_path = Path(model_path) 33 texture_path = texture_path and Path(texture_path) 34 assert not model_path.is_absolute(), model_path 35 assert texture_path is None or not texture_path.is_absolute(), texture_path 36 assert model_path.exists(), f"Model path: '{model_path}' doesn't exist." 37 logger.info(f"Loading model from '{model_path}'") 38 model = rl.LoadModel(str(model_path).encode()) 39 texture = rl.LoadTexture(str(texture_path).encode()) if texture_path is not None else None 40 return RlModelWithTexture(model=model, texture=texture) 41 42 @property 43 def bbox(self) -> tuple[np.ndarray, np.ndarray]: 44 """gets the top left and bottom right 3D points of this mesh. Used for collisions. Cahed once after 1st call""" 45 if self._bbox is None: 46 mins, maxs = np.float32([1<<31, 1<<31, 1<<31]), np.float32([-1<<31, -1<<31, -1<<31]) 47 for i in range(self.model.meshCount): 48 mesh: RlMesh = self.model.meshes[i] # of type struct Mesh & 49 buf = rl.ffi.buffer(mesh.vertices, mesh.vertexCount * 3 * 4) 50 vertices = np.frombuffer(buf, "float32").reshape(-1, 3) 51 mins = np.minimum(mins, np.min(vertices, axis=0)) 52 maxs = np.maximum(maxs, np.max(vertices, axis=0)) 53 self._bbox = mins, maxs 54 return self._bbox 55 56 def __del__(self): 57 if not rl.IsWindowReady(): # in case rl.CloseWindow() was called 58 return 59 if self.model is not None: 60 rl.UnloadModel(self.model) 61 if self.texture is not None: 62 rl.UnloadTexture(self.texture) 63 64def vec3_from(arr: np.ndarray) -> RlVector3: 65 """convers the numpy array to a raylib vector""" 66 return rl.ffi.from_buffer("Vector3 *", np.ascontiguousarray(arr))[0] 67 68def rl_vec3_to_np(vec: RlVector3) -> np.ndarray: 69 """returns a np array from a raylib vec3""" 70 return np.array([vec.x, vec.y, vec.z], "float32") 71 72def rl_get_device_and_renderer() -> tuple[str, str]: 73 """returns the device and renderer used by raylib for logging""" 74 gl_renderer = 0x1F01 75 gl_get_string = rl.ffi.cast("unsigned char *(*)(unsigned int)", rl.rlGetProcAddress(b"glGetString")) 76 gstr = lambda e: rl.ffi.string(rl.ffi.cast("char*", gl_get_string(e))).decode() 77 renderer = gstr(gl_renderer) 78 software = ("llvmpipe", "softpipe", "swrast", "lavapipe") 79 device = "CPU (software rasterizer)" if any(s in renderer.lower() for s in software) else "GPU" 80 return device, renderer
RlTexture =
typing.Any
RlRenderTexture =
typing.Any
RlModel =
typing.Any
RlVector3 =
typing.Any
RlMesh =
typing.Any
@dataclass
class
RlModelWithTexture:
19@dataclass 20class RlModelWithTexture: 21 """basic raylib model with an optional texture applied to it""" 22 model: RlModel 23 texture: RlTexture | None 24 _bbox: tuple[np.ndarray, np.ndarray] | None = None 25 26 def __post_init__(self): 27 if self.texture is not None: 28 self.model.materials.maps[rl.MATERIAL_MAP_ALBEDO].texture = self.texture 29 30 @staticmethod 31 def from_path(model_path: Path | str, texture_path: Path | str | None = None) -> RlModelWithTexture: 32 """builds a RLModelWithTexture from a path""" 33 model_path = Path(model_path) 34 texture_path = texture_path and Path(texture_path) 35 assert not model_path.is_absolute(), model_path 36 assert texture_path is None or not texture_path.is_absolute(), texture_path 37 assert model_path.exists(), f"Model path: '{model_path}' doesn't exist." 38 logger.info(f"Loading model from '{model_path}'") 39 model = rl.LoadModel(str(model_path).encode()) 40 texture = rl.LoadTexture(str(texture_path).encode()) if texture_path is not None else None 41 return RlModelWithTexture(model=model, texture=texture) 42 43 @property 44 def bbox(self) -> tuple[np.ndarray, np.ndarray]: 45 """gets the top left and bottom right 3D points of this mesh. Used for collisions. Cahed once after 1st call""" 46 if self._bbox is None: 47 mins, maxs = np.float32([1<<31, 1<<31, 1<<31]), np.float32([-1<<31, -1<<31, -1<<31]) 48 for i in range(self.model.meshCount): 49 mesh: RlMesh = self.model.meshes[i] # of type struct Mesh & 50 buf = rl.ffi.buffer(mesh.vertices, mesh.vertexCount * 3 * 4) 51 vertices = np.frombuffer(buf, "float32").reshape(-1, 3) 52 mins = np.minimum(mins, np.min(vertices, axis=0)) 53 maxs = np.maximum(maxs, np.max(vertices, axis=0)) 54 self._bbox = mins, maxs 55 return self._bbox 56 57 def __del__(self): 58 if not rl.IsWindowReady(): # in case rl.CloseWindow() was called 59 return 60 if self.model is not None: 61 rl.UnloadModel(self.model) 62 if self.texture is not None: 63 rl.UnloadTexture(self.texture)
basic raylib model with an optional texture applied to it
RlModelWithTexture( model: Any, texture: typing.Any | None, _bbox: tuple[numpy.ndarray, numpy.ndarray] | None = None)
@staticmethod
def
from_path( model_path: pathlib.Path | str, texture_path: pathlib.Path | str | None = None) -> RlModelWithTexture:
30 @staticmethod 31 def from_path(model_path: Path | str, texture_path: Path | str | None = None) -> RlModelWithTexture: 32 """builds a RLModelWithTexture from a path""" 33 model_path = Path(model_path) 34 texture_path = texture_path and Path(texture_path) 35 assert not model_path.is_absolute(), model_path 36 assert texture_path is None or not texture_path.is_absolute(), texture_path 37 assert model_path.exists(), f"Model path: '{model_path}' doesn't exist." 38 logger.info(f"Loading model from '{model_path}'") 39 model = rl.LoadModel(str(model_path).encode()) 40 texture = rl.LoadTexture(str(texture_path).encode()) if texture_path is not None else None 41 return RlModelWithTexture(model=model, texture=texture)
builds a RLModelWithTexture from a path
bbox: tuple[numpy.ndarray, numpy.ndarray]
43 @property 44 def bbox(self) -> tuple[np.ndarray, np.ndarray]: 45 """gets the top left and bottom right 3D points of this mesh. Used for collisions. Cahed once after 1st call""" 46 if self._bbox is None: 47 mins, maxs = np.float32([1<<31, 1<<31, 1<<31]), np.float32([-1<<31, -1<<31, -1<<31]) 48 for i in range(self.model.meshCount): 49 mesh: RlMesh = self.model.meshes[i] # of type struct Mesh & 50 buf = rl.ffi.buffer(mesh.vertices, mesh.vertexCount * 3 * 4) 51 vertices = np.frombuffer(buf, "float32").reshape(-1, 3) 52 mins = np.minimum(mins, np.min(vertices, axis=0)) 53 maxs = np.maximum(maxs, np.max(vertices, axis=0)) 54 self._bbox = mins, maxs 55 return self._bbox
gets the top left and bottom right 3D points of this mesh. Used for collisions. Cahed once after 1st call
def
vec3_from(arr: numpy.ndarray) -> Any:
65def vec3_from(arr: np.ndarray) -> RlVector3: 66 """convers the numpy array to a raylib vector""" 67 return rl.ffi.from_buffer("Vector3 *", np.ascontiguousarray(arr))[0]
convers the numpy array to a raylib vector
def
rl_vec3_to_np(vec: Any) -> numpy.ndarray:
69def rl_vec3_to_np(vec: RlVector3) -> np.ndarray: 70 """returns a np array from a raylib vec3""" 71 return np.array([vec.x, vec.y, vec.z], "float32")
returns a np array from a raylib vec3
def
rl_get_device_and_renderer() -> tuple[str, str]:
73def rl_get_device_and_renderer() -> tuple[str, str]: 74 """returns the device and renderer used by raylib for logging""" 75 gl_renderer = 0x1F01 76 gl_get_string = rl.ffi.cast("unsigned char *(*)(unsigned int)", rl.rlGetProcAddress(b"glGetString")) 77 gstr = lambda e: rl.ffi.string(rl.ffi.cast("char*", gl_get_string(e))).decode() 78 renderer = gstr(gl_renderer) 79 software = ("llvmpipe", "softpipe", "swrast", "lavapipe") 80 device = "CPU (software rasterizer)" if any(s in renderer.lower() for s in software) else "GPU" 81 return device, renderer
returns the device and renderer used by raylib for logging