WIP checkpoint: US-1: Asset Import Pipeline \u2014 glTF upload endpoint with metadata extraction
This commit is contained in:
@@ -37,6 +37,7 @@ class AssetResponse(BaseModel):
|
||||
file_path: str
|
||||
file_size: Optional[int]
|
||||
mime_type: Optional[str]
|
||||
metadata_json: Optional[dict]
|
||||
world_id: Optional[int]
|
||||
owner_id: int
|
||||
is_public: bool
|
||||
@@ -64,6 +65,75 @@ def _sanitize_filename(filename: str) -> str:
|
||||
return safe or "file"
|
||||
|
||||
|
||||
def _extract_gltf_metadata(content: bytes) -> Optional[dict]:
|
||||
"""Extract metadata from a glTF/GLB file.
|
||||
|
||||
Returns a dict with mesh_count, vertex_count, has_animation, texture_refs
|
||||
if the file is a valid glTF/GLB, or None otherwise.
|
||||
"""
|
||||
try:
|
||||
import gltflib
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
try:
|
||||
model = gltflib.GLFModel.from_json(content)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
# Count meshes
|
||||
meshes = model.meshes or []
|
||||
mesh_count = len(meshes)
|
||||
|
||||
# Count total vertices across all primitives
|
||||
vertex_count = 0
|
||||
for mesh in meshes:
|
||||
primitives = mesh.primitives or []
|
||||
for primitive in primitives:
|
||||
indices = primitive.indices
|
||||
if indices is not None:
|
||||
# Use index count
|
||||
accessor = model.accessors[indices] if indices < len(model.accessors or []) else None
|
||||
if accessor and accessor.count is not None:
|
||||
vertex_count += accessor.count
|
||||
else:
|
||||
vertex_count += 1 # fallback
|
||||
else:
|
||||
# No indices — count from POSITION attribute
|
||||
if primitive.attributes and primitive.attributes.POSITION is not None:
|
||||
accessor = model.accessors[primitive.attributes.POSITION] if primitive.attributes.POSITION < len(model.accessors or []) else None
|
||||
if accessor and accessor.count is not None:
|
||||
vertex_count += accessor.count
|
||||
else:
|
||||
vertex_count += 1 # fallback
|
||||
|
||||
# Check for animations
|
||||
animations = model.animations or []
|
||||
has_animation = len(animations) > 0
|
||||
|
||||
# Collect texture references
|
||||
texture_refs = []
|
||||
textures = model.textures or []
|
||||
for texture in textures:
|
||||
if texture.source is not None and texture.source < len(model.images or []):
|
||||
image = model.images[texture.source]
|
||||
ref = {}
|
||||
if image.name:
|
||||
ref["name"] = image.name
|
||||
if image.uri:
|
||||
ref["uri"] = image.uri
|
||||
if image.mimeType:
|
||||
ref["mime_type"] = image.mimeType
|
||||
texture_refs.append(ref)
|
||||
|
||||
return {
|
||||
"mesh_count": mesh_count,
|
||||
"vertex_count": vertex_count,
|
||||
"has_animation": has_animation,
|
||||
"texture_refs": texture_refs,
|
||||
}
|
||||
|
||||
|
||||
# --- Endpoints ---
|
||||
|
||||
@router.post("/", response_model=AssetResponse, status_code=201)
|
||||
@@ -204,6 +274,13 @@ async def upload_asset_file(
|
||||
asset.mime_type = file.content_type
|
||||
asset.file_size = file_size
|
||||
|
||||
# Extract glTF metadata for glTF/GLB files.
|
||||
filename_lower = (file.filename or "").lower()
|
||||
if filename_lower.endswith(".glb") or filename_lower.endswith(".gltf"):
|
||||
metadata = _extract_gltf_metadata(content)
|
||||
if metadata is not None:
|
||||
asset.metadata_json = str(metadata)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(asset)
|
||||
return asset
|
||||
|
||||
Reference in New Issue
Block a user