Carlexxx
feat: ✨ aBINC 2.2
fb56537
raw
history blame
4.08 kB
# aduc_framework/engineers/planner_3d.py
#
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
#
# Versão 4.1.0 (DNA Seguro por Cena)
#
# - Remove o acúmulo global de keyframes e a divisão por índices.
# - Atualiza cada cena diretamente com os keyframes gerados pelo Painter.
# - Garante que o DNA final nunca fique “bagunçado”.
# - Foco na simplicidade: Planner orquestra, Painter gera, DNA organiza.
import logging
import json
from typing import List, Dict, Any, Generator
from .deformes3D import deformes3d_engine_singleton
from ..types import KeyframeGenerationJob
logger = logging.getLogger(__name__)
class Planner3D:
"""
Gerente de projeto para a fase de keyframes.
Prepara e despacha ordens de serviço para o Painter (Deformes3D),
e injeta os keyframes diretamente no DNA cena a cena.
"""
def generate_keyframes_from_dna(
self,
generation_state: Dict[str, Any],
initial_chat_history: List[Dict[str, Any]]
) -> Generator[Dict[str, Any], None, None]:
painter = deformes3d_engine_singleton
chat_history = initial_chat_history.copy()
chat_history.append({
"role": "Planner3D",
"content": "Roteiro recebido. Iniciando criação de keyframes..."
})
yield {"chat": chat_history, "gallery": [], "dna": generation_state}
# 1. Extrai dados globais
scenes_list = generation_state.get("scenes", [])
ref_image_paths = [ref['caminho'] for ref in generation_state.get("midias_referencia", [])]
global_prompt = generation_state.get("prompt_geral", "")
pre_prod_params = generation_state.get("parametros_geracao", {}).get("pre_producao", {})
if not scenes_list or not ref_image_paths:
logger.warning("Planner3D: Nenhuma cena ou imagem de referência encontrada no DNA.")
yield {"chat": chat_history, "status": "keyframes_complete"}
return
# Mapeamento fixo de imagens de referência
ref_id_to_path_map = {i: path for i, path in enumerate(ref_image_paths)}
available_ref_ids = list(ref_id_to_path_map.keys())
# 2. Loop de cenas
for scene_idx, scene_data in enumerate(scenes_list):
scene_id = scene_data.get("id")
chat_history.append({
"role": "Planner3D",
"content": f"Desenhando a Cena {scene_id}/{len(scenes_list)}..."
})
yield {"chat": chat_history}
storyboard_da_cena = [ato.get("resumo_ato", "") for ato in scene_data.get("atos", [])]
if not storyboard_da_cena:
logger.info(f"Planner3D: Cena {scene_id} sem atos, pulando.")
continue
# 3. Cria ordem de serviço
job = KeyframeGenerationJob(
storyboard=storyboard_da_cena,
global_prompt=global_prompt,
ref_image_paths=ref_image_paths,
ref_id_to_path_map=ref_id_to_path_map,
available_ref_ids=available_ref_ids,
keyframe_prefix=f"scene_{scene_id:02d}",
params=pre_prod_params
)
# 4. Delega para o Painter
scene_keyframes = painter.generate_keyframes_from_job(job=job)
# 5. Injeta diretamente no DNA
scene_data["keyframes"] = scene_keyframes
if scene_keyframes:
chat_history.append({
"role": "Planner3D",
"content": f"Cena {scene_id} concluída ({len(scene_keyframes)} keyframes)."
})
yield {"chat": chat_history}
logger.info("Planner3D: Geração de todos os keyframes concluída.")
yield {
"chat": chat_history,
"gallery": [kf["caminho_pixel"] for scene in scenes_list for kf in scene.get("keyframes", [])],
"status": "keyframes_complete",
"dna": generation_state
}
planner_3d_singleton = Planner3D()