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_path: str
|
||||||
file_size: Optional[int]
|
file_size: Optional[int]
|
||||||
mime_type: Optional[str]
|
mime_type: Optional[str]
|
||||||
|
metadata_json: Optional[dict]
|
||||||
world_id: Optional[int]
|
world_id: Optional[int]
|
||||||
owner_id: int
|
owner_id: int
|
||||||
is_public: bool
|
is_public: bool
|
||||||
@@ -64,6 +65,75 @@ def _sanitize_filename(filename: str) -> str:
|
|||||||
return safe or "file"
|
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 ---
|
# --- Endpoints ---
|
||||||
|
|
||||||
@router.post("/", response_model=AssetResponse, status_code=201)
|
@router.post("/", response_model=AssetResponse, status_code=201)
|
||||||
@@ -204,6 +274,13 @@ async def upload_asset_file(
|
|||||||
asset.mime_type = file.content_type
|
asset.mime_type = file.content_type
|
||||||
asset.file_size = file_size
|
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.commit()
|
||||||
await db.refresh(asset)
|
await db.refresh(asset)
|
||||||
return asset
|
return asset
|
||||||
|
|||||||
Reference in New Issue
Block a user