OpenGL Mathematics (GLM) library for Python providing comprehensive vector and matrix manipulation capabilities for graphics programming.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Utility functions for type conversion, memory access, testing, packing/unpacking, and interfacing with external libraries. These functions provide essential tools for working with graphics APIs, file formats, and performance-critical applications.
Functions for accessing the underlying memory of PyGLM types for interfacing with C libraries and graphics APIs.
def value_ptr(x):
"""
Get a ctypes pointer to the underlying data of a PyGLM object.
Args:
x: Any PyGLM vector, matrix, or quaternion
Returns:
ctypes pointer to the data (c_float, c_double, c_int, etc.)
Example:
vec = glm.vec3(1, 2, 3)
ptr = glm.value_ptr(vec) # c_float pointer
# Pass ptr to OpenGL functions like glUniform3fv()
"""
def sizeof(glm_type):
"""
Get the size in bytes of a PyGLM type.
Args:
glm_type: PyGLM type (vec3, mat4, quat, etc.)
Returns:
Size in bytes
Example:
size = glm.sizeof(glm.vec3) # 12 bytes (3 * 4-byte floats)
size = glm.sizeof(glm.mat4) # 64 bytes (16 * 4-byte floats)
size = glm.sizeof(glm.dvec3) # 24 bytes (3 * 8-byte doubles)
"""Functions for creating PyGLM objects from memory pointers, useful when interfacing with external libraries.
def make_vec2(ptr):
"""
Create a vec2 from a ctypes pointer.
Args:
ptr: ctypes pointer to float data
Returns:
vec2 constructed from the pointer data
Example:
# Create vec2 from existing float array
import ctypes
data = (ctypes.c_float * 2)(1.0, 2.0)
vec = glm.make_vec2(data)
"""
def make_vec3(ptr):
"""
Create a vec3 from a ctypes pointer.
Args:
ptr: ctypes pointer to float data
Returns:
vec3 constructed from the pointer data
"""
def make_vec4(ptr):
"""
Create a vec4 from a ctypes pointer.
Args:
ptr: ctypes pointer to float data
Returns:
vec4 constructed from the pointer data
"""
def make_mat2(ptr):
"""
Create a mat2 from a ctypes pointer.
Args:
ptr: ctypes pointer to float data (4 elements)
Returns:
mat2x2 constructed from the pointer data
"""
def make_mat3(ptr):
"""
Create a mat3 from a ctypes pointer.
Args:
ptr: ctypes pointer to float data (9 elements)
Returns:
mat3x3 constructed from the pointer data
"""
def make_mat4(ptr):
"""
Create a mat4 from a ctypes pointer.
Args:
ptr: ctypes pointer to float data (16 elements)
Returns:
mat4x4 constructed from the pointer data
"""
def make_quat(ptr):
"""
Create a quat from a ctypes pointer.
Args:
ptr: ctypes pointer to float data (4 elements: w, x, y, z)
Returns:
quat constructed from the pointer data
"""Functions for testing special floating-point values and performing epsilon-based comparisons.
def isinf(x):
"""
Test if value(s) are infinite.
Args:
x: Scalar or vector input
Returns:
Boolean or boolean vector indicating infinity
Example:
isinf(float('inf')) # True
isinf(glm.vec3(1, float('inf'), 3)) # bvec3(False, True, False)
"""
def isnan(x):
"""
Test if value(s) are NaN (Not a Number).
Args:
x: Scalar or vector input
Returns:
Boolean or boolean vector indicating NaN
Example:
isnan(float('nan')) # True
isnan(glm.vec2(1.0, float('nan'))) # bvec2(False, True)
"""
def epsilonEqual(x, y, epsilon):
"""
Compare values using epsilon tolerance.
Args:
x: First value (scalar or vector)
y: Second value (same type as x)
epsilon: Tolerance value (scalar or vector)
Returns:
Boolean or boolean vector indicating equality within epsilon
Example:
epsilonEqual(1.0001, 1.0002, 0.001) # True
epsilonEqual(glm.vec3(1, 2, 3), glm.vec3(1.01, 2.01, 3.01), 0.02) # bvec3(True, True, True)
"""
def epsilonNotEqual(x, y, epsilon):
"""
Compare values for inequality using epsilon tolerance.
Args:
x: First value (scalar or vector)
y: Second value (same type as x)
epsilon: Tolerance value (scalar or vector)
Returns:
Boolean or boolean vector indicating inequality beyond epsilon
Example:
epsilonNotEqual(1.0, 1.1, 0.05) # True (difference > epsilon)
"""Functions for packing and unpacking floating-point values and performing bit-level operations.
def floatBitsToInt(value):
"""
Reinterpret float bits as signed integer.
Args:
value: Float value (scalar or vector)
Returns:
Integer with same bit pattern as input float
Example:
int_bits = glm.floatBitsToInt(1.0)
# Useful for bit manipulation or hashing
"""
def intBitsToFloat(value):
"""
Reinterpret integer bits as float.
Args:
value: Integer value (scalar or vector)
Returns:
Float with same bit pattern as input integer
Example:
float_val = glm.intBitsToFloat(1065353216) # Should be 1.0
"""
def floatBitsToUint(value):
"""
Reinterpret float bits as unsigned integer.
Args:
value: Float value (scalar or vector)
Returns:
Unsigned integer with same bit pattern as input float
"""
def uintBitsToFloat(value):
"""
Reinterpret unsigned integer bits as float.
Args:
value: Unsigned integer value (scalar or vector)
Returns:
Float with same bit pattern as input unsigned integer
"""Functions for packing and unpacking 16-bit half-precision floating-point values.
def packHalf2x16(v):
"""
Pack two 32-bit floats into a single 32-bit unsigned integer as half-precision values.
Args:
v: vec2 containing two float values
Returns:
32-bit unsigned integer containing packed half-precision values
Example:
packed = glm.packHalf2x16(glm.vec2(1.0, -2.5))
# Useful for reducing memory usage in shaders
"""
def unpackHalf2x16(p):
"""
Unpack 32-bit unsigned integer into two half-precision floats.
Args:
p: 32-bit unsigned integer containing packed half-precision values
Returns:
vec2 containing the unpacked float values
Example:
unpacked = glm.unpackHalf2x16(packed_value) # Returns vec2
"""
def packHalf4x16(v):
"""
Pack four 32-bit floats into two 32-bit unsigned integers as half-precision values.
Args:
v: vec4 containing four float values
Returns:
uvec2 containing packed half-precision values
Example:
packed = glm.packHalf4x16(glm.vec4(1, 2, 3, 4))
"""
def unpackHalf4x16(p):
"""
Unpack two 32-bit unsigned integers into four half-precision floats.
Args:
p: uvec2 containing packed half-precision values
Returns:
vec4 containing the unpacked float values
"""Functions for packing normalized floating-point values into integer formats.
def packUnorm2x16(v):
"""
Pack two normalized floats [0,1] into 16-bit unsigned integers.
Args:
v: vec2 with values in range [0, 1]
Returns:
32-bit unsigned integer containing packed values
Example:
packed = glm.packUnorm2x16(glm.vec2(0.5, 1.0))
"""
def unpackUnorm2x16(p):
"""
Unpack 32-bit unsigned integer into two normalized floats.
Args:
p: 32-bit unsigned integer containing packed values
Returns:
vec2 with values in range [0, 1]
"""
def packSnorm2x16(v):
"""
Pack two signed normalized floats [-1,1] into 16-bit signed integers.
Args:
v: vec2 with values in range [-1, 1]
Returns:
32-bit unsigned integer containing packed values
Example:
packed = glm.packSnorm2x16(glm.vec2(-0.5, 1.0))
"""
def unpackSnorm2x16(p):
"""
Unpack 32-bit unsigned integer into two signed normalized floats.
Args:
p: 32-bit unsigned integer containing packed values
Returns:
vec2 with values in range [-1, 1]
"""
def packUnorm4x8(v):
"""
Pack four normalized floats [0,1] into 8-bit unsigned integers.
Args:
v: vec4 with values in range [0, 1]
Returns:
32-bit unsigned integer containing packed values
Example:
# Pack RGBA color
color = glm.vec4(1.0, 0.5, 0.25, 1.0) # Red, half green, quarter blue, full alpha
packed_color = glm.packUnorm4x8(color)
"""
def unpackUnorm4x8(p):
"""
Unpack 32-bit unsigned integer into four normalized floats.
Args:
p: 32-bit unsigned integer containing packed values
Returns:
vec4 with values in range [0, 1]
"""
def packSnorm4x8(v):
"""
Pack four signed normalized floats [-1,1] into 8-bit signed integers.
Args:
v: vec4 with values in range [-1, 1]
Returns:
32-bit unsigned integer containing packed values
"""
def unpackSnorm4x8(p):
"""
Unpack 32-bit unsigned integer into four signed normalized floats.
Args:
p: 32-bit unsigned integer containing packed values
Returns:
vec4 with values in range [-1, 1]
"""Functions for packing and unpacking double-precision floating-point values.
def packDouble2x32(v):
"""
Pack a double-precision float into two 32-bit unsigned integers.
Args:
v: Double-precision float value
Returns:
uvec2 containing the packed double
Example:
packed_double = glm.packDouble2x32(3.141592653589793)
"""
def unpackDouble2x32(p):
"""
Unpack two 32-bit unsigned integers into a double-precision float.
Args:
p: uvec2 containing packed double-precision value
Returns:
Double-precision float value
"""Advanced bit manipulation functions for integer operations.
def bitfieldExtract(value, offset, bits):
"""
Extract a range of bits from an integer.
Args:
value: Integer value (scalar or vector)
offset: Bit offset (least significant bit = 0)
bits: Number of bits to extract
Returns:
Extracted bits as integer
"""
def bitfieldInsert(base, insert, offset, bits):
"""
Insert bits into a range of an integer.
Args:
base: Base integer value
insert: Integer containing bits to insert
offset: Bit offset for insertion
bits: Number of bits to insert
Returns:
Integer with bits inserted
"""
def bitfieldReverse(value):
"""
Reverse the bits in an integer.
Args:
value: Integer value (scalar or vector)
Returns:
Integer with bits reversed
"""
def bitCount(value):
"""
Count the number of 1 bits in an integer.
Args:
value: Integer value (scalar or vector)
Returns:
Number of set bits
"""
def findLSB(value):
"""
Find the index of the least significant set bit.
Args:
value: Integer value (scalar or vector)
Returns:
Index of LSB, or -1 if no bits set
"""
def findMSB(value):
"""
Find the index of the most significant set bit.
Args:
value: Integer value (scalar or vector)
Returns:
Index of MSB, or -1 if no bits set
"""Functions for extended precision integer arithmetic.
def uaddCarry(x, y, carry):
"""
Add two unsigned integers with carry detection.
Args:
x: First unsigned integer
y: Second unsigned integer
carry: Output parameter for carry bit
Returns:
Sum of x and y
"""
def usubBorrow(x, y, borrow):
"""
Subtract two unsigned integers with borrow detection.
Args:
x: First unsigned integer (minuend)
y: Second unsigned integer (subtrahend)
borrow: Output parameter for borrow bit
Returns:
Difference of x and y
"""
def umulExtended(x, y, msb, lsb):
"""
Multiply two unsigned integers producing extended precision result.
Args:
x: First unsigned integer
y: Second unsigned integer
msb: Output parameter for most significant bits
lsb: Output parameter for least significant bits
Returns:
Extended precision product
"""
def imulExtended(x, y, msb, lsb):
"""
Multiply two signed integers producing extended precision result.
Args:
x: First signed integer
y: Second signed integer
msb: Output parameter for most significant bits
lsb: Output parameter for least significant bits
Returns:
Extended precision product
"""
def iround(x):
"""
Round float to nearest integer (signed).
Args:
x: Float value (scalar or vector)
Returns:
Rounded integer value
"""
def uround(x):
"""
Round float to nearest unsigned integer.
Args:
x: Float value (scalar or vector)
Returns:
Rounded unsigned integer value
"""Functions for controlling PyGLM behavior and warnings.
def silence(id):
"""
Silence specific PyGLM warnings.
Args:
id: Warning ID to silence (0 = silence all warnings)
Example:
glm.silence(1) # Silence warning ID 1
glm.silence(0) # Silence all warnings
"""from pyglm import glm
import ctypes
# === Memory Access for Graphics APIs ===
# Pass matrix to OpenGL uniform
model_matrix = glm.mat4()
model_matrix = glm.rotate(model_matrix, glm.radians(45), glm.vec3(0, 1, 0))
# Get pointer for OpenGL
matrix_ptr = glm.value_ptr(model_matrix)
# In OpenGL: glUniformMatrix4fv(location, 1, GL_FALSE, matrix_ptr)
# Get size information
matrix_size = glm.sizeof(glm.mat4) # 64 bytes
vector_size = glm.sizeof(glm.vec3) # 12 bytes
# === Creating Types from External Data ===
# Create PyGLM vector from C array
float_array = (ctypes.c_float * 3)(1.0, 2.0, 3.0)
vec3_from_array = glm.make_vec3(float_array)
# Create matrix from external data
matrix_data = (ctypes.c_float * 16)(*[1 if i == j else 0 for i in range(4) for j in range(4)])
identity_matrix = glm.make_mat4(matrix_data)
# === Numerical Testing ===
# Test for special values
values = glm.vec4(1.0, float('inf'), float('nan'), -5.0)
inf_test = glm.isinf(values) # bvec4(False, True, False, False)
nan_test = glm.isnan(values) # bvec4(False, False, True, False)
# Epsilon comparisons for floating-point equality
a = glm.vec3(1.0, 2.0, 3.0)
b = glm.vec3(1.0001, 1.9999, 3.0001)
epsilon = 0.001
equal_test = glm.epsilonEqual(a, b, epsilon) # bvec3(True, True, True)
not_equal_test = glm.epsilonNotEqual(a, b, epsilon) # bvec3(False, False, False)
# === Bit Manipulation ===
# Convert between float and integer bit representations
float_val = 1.0
int_bits = glm.floatBitsToInt(float_val) # 1065353216
back_to_float = glm.intBitsToFloat(int_bits) # 1.0
# Useful for bit manipulation or hashing
def simple_hash(vec):
# Convert vector components to integer bits for hashing
x_bits = glm.floatBitsToInt(vec.x)
y_bits = glm.floatBitsToInt(vec.y)
z_bits = glm.floatBitsToInt(vec.z)
return hash((x_bits, y_bits, z_bits))
position = glm.vec3(1.5, 2.7, 3.1)
position_hash = simple_hash(position)
# === Half-Precision Packing for Memory Efficiency ===
# Pack two floats into half-precision for storage
original_vec2 = glm.vec2(1.5, -2.25)
packed_half = glm.packHalf2x16(original_vec2) # Stores in 32 bits instead of 64
unpacked_vec2 = glm.unpackHalf2x16(packed_half) # Slight precision loss
# Pack four floats into half-precision
original_vec4 = glm.vec4(1.0, 2.0, 3.0, 4.0)
packed_half4 = glm.packHalf4x16(original_vec4) # Returns uvec2
unpacked_vec4 = glm.unpackHalf4x16(packed_half4)
# === Normalized Value Packing ===
# Pack RGBA color values efficiently
color = glm.vec4(1.0, 0.5, 0.25, 1.0) # Red, half green, quarter blue, full alpha
packed_color = glm.packUnorm4x8(color) # Pack into 32-bit integer
unpacked_color = glm.unpackUnorm4x8(packed_color) # Unpack back to vec4
# Pack normal vectors (commonly in range [-1, 1])
normal = glm.vec3(-0.5, 0.707, 0.5) # Normalized normal vector
# Extend to vec4 for packing
normal_vec4 = glm.vec4(normal.x, normal.y, normal.z, 0.0)
packed_normal = glm.packSnorm4x8(normal_vec4)
unpacked_normal = glm.unpackSnorm4x8(packed_normal)
# === Double-Precision Packing ===
# Pack double-precision value for storage or transmission
double_val = 3.141592653589793
packed_double = glm.packDouble2x32(double_val) # Returns uvec2
unpacked_double = glm.unpackDouble2x32(packed_double)
# === Practical Example: Vertex Data Compression ===
class CompressedVertex:
def __init__(self, position, normal, uv, color):
# Store position as-is (high precision needed)
self.position = position
# Pack normal into 32-bit integer (normals are in [-1,1] range)
normal_vec4 = glm.vec4(normal.x, normal.y, normal.z, 0.0)
self.packed_normal = glm.packSnorm4x8(normal_vec4)
# Pack UV coordinates into 32-bit integer (UVs are in [0,1] range)
uv_vec4 = glm.vec4(uv.x, uv.y, 0.0, 0.0) # Pad to vec4
self.packed_uv = glm.packUnorm4x8(uv_vec4)
# Pack color into 32-bit integer
self.packed_color = glm.packUnorm4x8(color)
def get_normal(self):
unpacked = glm.unpackSnorm4x8(self.packed_normal)
return glm.vec3(unpacked.x, unpacked.y, unpacked.z)
def get_uv(self):
unpacked = glm.unpackUnorm4x8(self.packed_uv)
return glm.vec2(unpacked.x, unpacked.y)
def get_color(self):
return glm.unpackUnorm4x8(self.packed_color)
# Create compressed vertex
original_position = glm.vec3(1.0, 2.0, 3.0)
original_normal = glm.vec3(0.0, 1.0, 0.0)
original_uv = glm.vec2(0.5, 0.75)
original_color = glm.vec4(1.0, 0.5, 0.25, 1.0)
compressed = CompressedVertex(original_position, original_normal, original_uv, original_color)
# Retrieve data (with some precision loss from compression)
retrieved_normal = compressed.get_normal()
retrieved_uv = compressed.get_uv()
retrieved_color = compressed.get_color()
# === OpenGL Integration Example ===
def upload_matrix_to_gpu(matrix, uniform_location):
"""Upload matrix to OpenGL uniform."""
# Get pointer to matrix data
matrix_ptr = glm.value_ptr(matrix)
matrix_size = glm.sizeof(type(matrix))
# Determine matrix dimensions and upload
if matrix_size == 64: # mat4
# glUniformMatrix4fv(uniform_location, 1, GL_FALSE, matrix_ptr)
pass
elif matrix_size == 36: # mat3
# glUniformMatrix3fv(uniform_location, 1, GL_FALSE, matrix_ptr)
pass
# etc.
# === Performance Monitoring ===
def compare_storage_sizes():
"""Compare storage sizes of different representations."""
# Original vec4
original = glm.vec4(1.0, 0.5, 0.25, 1.0)
original_size = glm.sizeof(glm.vec4) # 16 bytes
# Half-precision packed
packed_half = glm.packHalf4x16(original)
packed_half_size = glm.sizeof(glm.uvec2) # 8 bytes
# Normalized packed
packed_norm = glm.packUnorm4x8(original)
packed_norm_size = 4 # 4 bytes (single uint32)
print(f"Original: {original_size} bytes")
print(f"Half-precision: {packed_half_size} bytes ({packed_half_size/original_size:.1%} of original)")
print(f"Normalized: {packed_norm_size} bytes ({packed_norm_size/original_size:.1%} of original)")
compare_storage_sizes()
# === Error Handling and Warnings ===
# Silence specific warnings
glm.silence(1) # Silence warning with ID 1
# Silence all warnings
glm.silence(0)
# Example function that might generate warnings
def risky_operation():
# Some operation that might trigger frexp warning
result = glm.frexp(glm.vec3(1.5, 2.0, 3.0))
return result
# Call without warnings (if silenced)
result = risky_operation()make_* functionsvalue_ptr() to pass data directly to OpenGL, Vulkan, and other graphics APIsisinf, isnan) when working with computed valuesInstall with Tessl CLI
npx tessl i tessl/pypi-pyglm