#!/usr/bin/env python3 """ Generate simple GLTF primitive models for Starworld entity rendering. Requires: pip install pygltflib numpy """ import numpy as np from pygltflib import * import struct import os from pathlib import Path def create_cube_gltf(output_path): """Create a 1x1x1 cube centered at origin""" # Cube vertices (8 vertices) vertices = np.array([ [-0.5, -0.5, -0.5], # 0 [ 0.5, -0.5, -0.5], # 1 [ 0.5, 0.5, -0.5], # 2 [-0.5, 0.5, -0.5], # 3 [-0.5, -0.5, 0.5], # 4 [ 0.5, -0.5, 0.5], # 5 [ 0.5, 0.5, 0.5], # 6 [-0.5, 0.5, 0.5], # 7 ], dtype=np.float32) # Cube indices (12 triangles, 36 indices) indices = np.array([ # Front face 0, 1, 2, 0, 2, 3, # Back face 5, 4, 7, 5, 7, 6, # Top face 3, 2, 6, 3, 6, 7, # Bottom face 4, 5, 1, 4, 1, 0, # Right face 1, 5, 6, 1, 6, 2, # Left face 4, 0, 3, 4, 3, 7, ], dtype=np.uint16) # Normals (per-face normals repeated for each vertex of triangle) normals = np.array([ # Front [0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1], # Back [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], # Top [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], # Bottom [0, -1, 0], [0, -1, 0], [0, -1, 0], [0, -1, 0], [0, -1, 0], [0, -1, 0], # Right [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], # Left [-1, 0, 0], [-1, 0, 0], [-1, 0, 0], [-1, 0, 0], [-1, 0, 0], [-1, 0, 0], ], dtype=np.float32) # Create binary data vertices_binary = vertices.tobytes() indices_binary = indices.tobytes() # Build GLTF gltf = GLTF2( scene=0, scenes=[Scene(nodes=[0])], nodes=[Node(mesh=0)], meshes=[ Mesh(primitives=[ Primitive( attributes=Attributes(POSITION=0), indices=1, ) ]) ], accessors=[ Accessor( bufferView=0, componentType=FLOAT, count=len(vertices), type=VEC3, max=vertices.max(axis=0).tolist(), min=vertices.min(axis=0).tolist(), ), Accessor( bufferView=1, componentType=UNSIGNED_SHORT, count=len(indices), type=SCALAR, ), ], bufferViews=[ BufferView( buffer=0, byteOffset=0, byteLength=len(vertices_binary), target=ARRAY_BUFFER, ), BufferView( buffer=0, byteOffset=len(vertices_binary), byteLength=len(indices_binary), target=ELEMENT_ARRAY_BUFFER, ), ], buffers=[ Buffer(byteLength=len(vertices_binary) + len(indices_binary)) ], ) gltf.set_binary_blob(vertices_binary + indices_binary) gltf.save(output_path) print(f"Created cube: {output_path}") def create_sphere_gltf(output_path, segments=32, rings=16): """Create a UV sphere""" vertices = [] normals = [] indices = [] # Generate vertices for ring in range(rings + 1): theta = ring * np.pi / rings sin_theta = np.sin(theta) cos_theta = np.cos(theta) for seg in range(segments + 1): phi = seg * 2 * np.pi / segments sin_phi = np.sin(phi) cos_phi = np.cos(phi) x = cos_phi * sin_theta y = cos_theta z = sin_phi * sin_theta vertices.append([x * 0.5, y * 0.5, z * 0.5]) # radius 0.5 normals.append([x, y, z]) # Generate indices for ring in range(rings): for seg in range(segments): first = ring * (segments + 1) + seg second = first + segments + 1 indices.extend([first, second, first + 1]) indices.extend([second, second + 1, first + 1]) vertices = np.array(vertices, dtype=np.float32) normals = np.array(normals, dtype=np.float32) indices = np.array(indices, dtype=np.uint16) vertices_binary = vertices.tobytes() normals_binary = normals.tobytes() indices_binary = indices.tobytes() gltf = GLTF2( scene=0, scenes=[Scene(nodes=[0])], nodes=[Node(mesh=0)], meshes=[ Mesh(primitives=[ Primitive( attributes=Attributes(POSITION=0, NORMAL=1), indices=2, ) ]) ], accessors=[ Accessor( bufferView=0, componentType=FLOAT, count=len(vertices), type=VEC3, max=vertices.max(axis=0).tolist(), min=vertices.min(axis=0).tolist(), ), Accessor( bufferView=1, componentType=FLOAT, count=len(normals), type=VEC3, ), Accessor( bufferView=2, componentType=UNSIGNED_SHORT, count=len(indices), type=SCALAR, ), ], bufferViews=[ BufferView( buffer=0, byteOffset=0, byteLength=len(vertices_binary), target=ARRAY_BUFFER, ), BufferView( buffer=0, byteOffset=len(vertices_binary), byteLength=len(normals_binary), target=ARRAY_BUFFER, ), BufferView( buffer=0, byteOffset=len(vertices_binary) + len(normals_binary), byteLength=len(indices_binary), target=ELEMENT_ARRAY_BUFFER, ), ], buffers=[ Buffer(byteLength=len(vertices_binary) + len(normals_binary) + len(indices_binary)) ], ) gltf.set_binary_blob(vertices_binary + normals_binary + indices_binary) gltf.save(output_path) print(f"Created sphere: {output_path}") if __name__ == "__main__": # Create output directory cache_dir = Path.home() / ".cache" / "starworld" / "primitives" cache_dir.mkdir(parents=True, exist_ok=True) # Generate primitives create_cube_gltf(str(cache_dir / "cube.glb")) create_sphere_gltf(str(cache_dir / "sphere.glb")) print(f"\n✓ Primitive models generated in: {cache_dir}") print(f" - cube.glb") print(f" - sphere.glb")