fix: smooth normals on non-indexed geometry + sync DB in gltf task
InlineCadViewer: STL-derived GLBs have non-indexed geometry (unique vertex per triangle face). computeVertexNormals() on non-indexed geometry produces per-face normals (faceted shading). Fix: mergeVertices() first to create shared/indexed geometry, then computeVertexNormals() averages across adjacent faces → smooth shading. Indexed Blender GLBs are unaffected. generate_gltf_geometry_task: asyncio.run() inside a Celery worker that already runs asyncpg causes 'Future attached to a different loop'. Replace async _store() with sync SQLAlchemy session (matching the rest of the task). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -530,38 +530,37 @@ def generate_gltf_geometry_task(self, cad_file_id: str):
|
||||
log_task_event(self.request.id, f"Blender GLB export completed: {output_path.name}", "done")
|
||||
|
||||
# --- Store MediaAsset (replace existing gltf_geometry for this cad_file) ---
|
||||
import asyncio
|
||||
# Use sync SQLAlchemy to avoid asyncio event-loop conflicts in Celery workers.
|
||||
import uuid as _uuid
|
||||
from sqlalchemy import create_engine as _ce, delete as _del
|
||||
from sqlalchemy.orm import Session as _Session
|
||||
from app.domains.media.models import MediaAsset, MediaAssetType
|
||||
|
||||
async def _store():
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.domains.media.models import MediaAsset, MediaAssetType
|
||||
from app.config import settings as _cfg
|
||||
import uuid
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Delete previous gltf_geometry assets for this cad_file to avoid stale records
|
||||
from sqlalchemy import delete as _delete
|
||||
await db.execute(
|
||||
_delete(MediaAsset).where(
|
||||
MediaAsset.cad_file_id == uuid.UUID(cad_file_id),
|
||||
MediaAsset.asset_type == MediaAssetType.gltf_geometry,
|
||||
)
|
||||
_sync_url = app_settings.database_url.replace("+asyncpg", "")
|
||||
_eng2 = _ce(_sync_url)
|
||||
with _Session(_eng2) as _sess:
|
||||
_sess.execute(
|
||||
_del(MediaAsset).where(
|
||||
MediaAsset.cad_file_id == _uuid.UUID(cad_file_id),
|
||||
MediaAsset.asset_type == MediaAssetType.gltf_geometry,
|
||||
)
|
||||
_key = str(output_path)
|
||||
_prefix = str(_cfg.upload_dir).rstrip("/") + "/"
|
||||
if _key.startswith(_prefix):
|
||||
_key = _key[len(_prefix):]
|
||||
asset = MediaAsset(
|
||||
cad_file_id=uuid.UUID(cad_file_id),
|
||||
asset_type=MediaAssetType.gltf_geometry,
|
||||
storage_key=_key,
|
||||
mime_type="model/gltf-binary",
|
||||
file_size_bytes=output_path.stat().st_size if output_path.exists() else None,
|
||||
)
|
||||
db.add(asset)
|
||||
await db.commit()
|
||||
return str(asset.id)
|
||||
)
|
||||
_key = str(output_path)
|
||||
_prefix = str(app_settings.upload_dir).rstrip("/") + "/"
|
||||
if _key.startswith(_prefix):
|
||||
_key = _key[len(_prefix):]
|
||||
asset = MediaAsset(
|
||||
cad_file_id=_uuid.UUID(cad_file_id),
|
||||
asset_type=MediaAssetType.gltf_geometry,
|
||||
storage_key=_key,
|
||||
mime_type="model/gltf-binary",
|
||||
file_size_bytes=output_path.stat().st_size if output_path.exists() else None,
|
||||
)
|
||||
_sess.add(asset)
|
||||
_sess.commit()
|
||||
asset_id = str(asset.id)
|
||||
_eng2.dispose()
|
||||
|
||||
asset_id = asyncio.run(_store())
|
||||
logger.info("generate_gltf_geometry_task: MediaAsset %s created for cad %s", asset_id, cad_file_id)
|
||||
return {"glb_path": str(output_path), "asset_id": asset_id}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user