updated versions
This commit is contained in:
Binary file not shown.
@@ -3,12 +3,15 @@ from kivy.uix.screenmanager import Screen
|
||||
import os
|
||||
import json
|
||||
import math
|
||||
from datetime import datetime
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import StringProperty, NumericProperty, AliasProperty
|
||||
from py_scripts.utils import (
|
||||
process_preview_util, optimize_route_entries_util
|
||||
)
|
||||
from py_scripts.video_3d_generator import generate_3d_video_animation
|
||||
from py_scripts.advanced_3d_generator import Advanced3DGenerator
|
||||
from py_scripts.blender_animator import BlenderGPSAnimator
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.label import Label
|
||||
@@ -238,89 +241,128 @@ class CreateAnimationScreen(Screen):
|
||||
)
|
||||
|
||||
def show_video_generation_options(self):
|
||||
"""Show popup with video generation mode options"""
|
||||
"""Show popup with video generation mode options including new advanced animations"""
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.label import Label
|
||||
|
||||
layout = BoxLayout(orientation='vertical', spacing=15, padding=15)
|
||||
layout = BoxLayout(orientation='vertical', spacing=12, padding=15)
|
||||
|
||||
# Title
|
||||
title_label = Label(
|
||||
text="Choose Video Generation Mode",
|
||||
font_size=18,
|
||||
text="Choose Animation Style & Quality",
|
||||
font_size=20,
|
||||
size_hint_y=None,
|
||||
height=40,
|
||||
color=(1, 1, 1, 1)
|
||||
)
|
||||
layout.add_widget(title_label)
|
||||
|
||||
# Test mode description
|
||||
test_layout = BoxLayout(orientation='vertical', spacing=5)
|
||||
test_title = Label(
|
||||
text="🏃♂️ 720p Test Mode (Fast)",
|
||||
# Classic 3D Mode
|
||||
classic_layout = BoxLayout(orientation='vertical', spacing=5)
|
||||
classic_title = Label(
|
||||
text="🏃♂️ Classic 3D (Original Pipeline)",
|
||||
font_size=16,
|
||||
size_hint_y=None,
|
||||
height=30,
|
||||
color=(0.2, 0.8, 0.2, 1)
|
||||
)
|
||||
test_desc = Label(
|
||||
text="• Resolution: 1280x720\n• Frame rate: 30 FPS\n• ~3x faster generation\n• Perfect for quick previews",
|
||||
font_size=12,
|
||||
classic_desc = Label(
|
||||
text="• Traditional OpenCV/PIL approach\n• Fast generation\n• Good for simple tracks\n• Test (720p) or Production (2K)",
|
||||
font_size=11,
|
||||
size_hint_y=None,
|
||||
height=80,
|
||||
height=70,
|
||||
color=(0.9, 0.9, 0.9, 1),
|
||||
halign="left",
|
||||
valign="middle"
|
||||
)
|
||||
test_desc.text_size = (None, None)
|
||||
test_layout.add_widget(test_title)
|
||||
test_layout.add_widget(test_desc)
|
||||
layout.add_widget(test_layout)
|
||||
classic_desc.text_size = (None, None)
|
||||
classic_layout.add_widget(classic_title)
|
||||
classic_layout.add_widget(classic_desc)
|
||||
layout.add_widget(classic_layout)
|
||||
|
||||
# Test mode button
|
||||
test_btn = Button(
|
||||
text="Generate 720p Test Video",
|
||||
# Classic buttons
|
||||
classic_btn_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint_y=None, height=45)
|
||||
classic_test_btn = Button(
|
||||
text="Classic 720p",
|
||||
background_color=(0.2, 0.8, 0.2, 1),
|
||||
size_hint_y=None,
|
||||
height=50,
|
||||
font_size=14
|
||||
font_size=12
|
||||
)
|
||||
layout.add_widget(test_btn)
|
||||
classic_prod_btn = Button(
|
||||
text="Classic 2K",
|
||||
background_color=(0.3, 0.6, 0.3, 1),
|
||||
font_size=12
|
||||
)
|
||||
classic_btn_layout.add_widget(classic_test_btn)
|
||||
classic_btn_layout.add_widget(classic_prod_btn)
|
||||
layout.add_widget(classic_btn_layout)
|
||||
|
||||
# Production mode description
|
||||
prod_layout = BoxLayout(orientation='vertical', spacing=5)
|
||||
prod_title = Label(
|
||||
text="🎯 2K Production Mode (High Quality)",
|
||||
# Advanced Pydeck/Plotly Mode
|
||||
advanced_layout = BoxLayout(orientation='vertical', spacing=5)
|
||||
advanced_title = Label(
|
||||
text="🚀 Advanced 3D (Pydeck + Plotly)",
|
||||
font_size=16,
|
||||
size_hint_y=None,
|
||||
height=30,
|
||||
color=(0.8, 0.2, 0.2, 1)
|
||||
color=(0.2, 0.6, 0.9, 1)
|
||||
)
|
||||
prod_desc = Label(
|
||||
text="• Resolution: 2560x1440\n• Frame rate: 60 FPS\n• Cinema-quality results\n• Ultra-detailed visuals",
|
||||
font_size=12,
|
||||
advanced_desc = Label(
|
||||
text="• Professional geospatial visualization\n• Interactive 3D terrain\n• Advanced camera movements\n• High-quality animations",
|
||||
font_size=11,
|
||||
size_hint_y=None,
|
||||
height=80,
|
||||
height=70,
|
||||
color=(0.9, 0.9, 0.9, 1),
|
||||
halign="left",
|
||||
valign="middle"
|
||||
)
|
||||
prod_desc.text_size = (None, None)
|
||||
prod_layout.add_widget(prod_title)
|
||||
prod_layout.add_widget(prod_desc)
|
||||
layout.add_widget(prod_layout)
|
||||
advanced_desc.text_size = (None, None)
|
||||
advanced_layout.add_widget(advanced_title)
|
||||
advanced_layout.add_widget(advanced_desc)
|
||||
layout.add_widget(advanced_layout)
|
||||
|
||||
# Production mode button
|
||||
prod_btn = Button(
|
||||
text="Generate 2K Production Video",
|
||||
background_color=(0.8, 0.2, 0.2, 1),
|
||||
# Advanced button
|
||||
advanced_btn = Button(
|
||||
text="Generate Advanced 3D Animation",
|
||||
background_color=(0.2, 0.6, 0.9, 1),
|
||||
size_hint_y=None,
|
||||
height=50,
|
||||
font_size=14
|
||||
height=45,
|
||||
font_size=13
|
||||
)
|
||||
layout.add_widget(prod_btn)
|
||||
layout.add_widget(advanced_btn)
|
||||
|
||||
# Blender Cinema Mode
|
||||
blender_layout = BoxLayout(orientation='vertical', spacing=5)
|
||||
blender_title = Label(
|
||||
text="<EFBFBD> Cinema Quality (Blender)",
|
||||
font_size=16,
|
||||
size_hint_y=None,
|
||||
height=30,
|
||||
color=(0.9, 0.6, 0.2, 1)
|
||||
)
|
||||
blender_desc = Label(
|
||||
text="• Professional 3D rendering\n• Photorealistic visuals\n• Cinema-grade quality\n• Longer processing time",
|
||||
font_size=11,
|
||||
size_hint_y=None,
|
||||
height=70,
|
||||
color=(0.9, 0.9, 0.9, 1),
|
||||
halign="left",
|
||||
valign="middle"
|
||||
)
|
||||
blender_desc.text_size = (None, None)
|
||||
blender_layout.add_widget(blender_title)
|
||||
blender_layout.add_widget(blender_desc)
|
||||
layout.add_widget(blender_layout)
|
||||
|
||||
# Blender button
|
||||
blender_btn = Button(
|
||||
text="Generate Blender Cinema Animation",
|
||||
background_color=(0.9, 0.6, 0.2, 1),
|
||||
size_hint_y=None,
|
||||
height=45,
|
||||
font_size=13
|
||||
)
|
||||
layout.add_widget(blender_btn)
|
||||
|
||||
# Cancel button
|
||||
cancel_btn = Button(
|
||||
@@ -333,23 +375,279 @@ class CreateAnimationScreen(Screen):
|
||||
layout.add_widget(cancel_btn)
|
||||
|
||||
popup = Popup(
|
||||
title="Select Video Generation Mode",
|
||||
title="Select Animation Style",
|
||||
content=layout,
|
||||
size_hint=(0.9, 0.8),
|
||||
size_hint=(0.95, 0.9),
|
||||
auto_dismiss=False
|
||||
)
|
||||
|
||||
def start_test_mode(instance):
|
||||
def start_classic_test(instance):
|
||||
popup.dismiss()
|
||||
self.generate_3d_video_test_mode()
|
||||
|
||||
def start_production_mode(instance):
|
||||
def start_classic_production(instance):
|
||||
popup.dismiss()
|
||||
self.generate_3d_video_production_mode()
|
||||
|
||||
def start_advanced_3d(instance):
|
||||
popup.dismiss()
|
||||
self.generate_advanced_3d_animation()
|
||||
|
||||
def start_blender_animation(instance):
|
||||
popup.dismiss()
|
||||
self.generate_blender_animation()
|
||||
|
||||
test_btn.bind(on_press=start_test_mode)
|
||||
prod_btn.bind(on_press=start_production_mode)
|
||||
classic_test_btn.bind(on_press=start_classic_test)
|
||||
classic_prod_btn.bind(on_press=start_classic_production)
|
||||
advanced_btn.bind(on_press=start_advanced_3d)
|
||||
blender_btn.bind(on_press=start_blender_animation)
|
||||
cancel_btn.bind(on_press=lambda x: popup.dismiss())
|
||||
|
||||
popup.open()
|
||||
|
||||
def generate_advanced_3d_animation(self):
|
||||
"""Generate advanced 3D animation using Pydeck and Plotly"""
|
||||
# Show processing popup
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Initializing advanced 3D animation...")
|
||||
progress = ProgressBar(max=100, value=0)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Generating Advanced 3D Animation",
|
||||
content=layout,
|
||||
size_hint=(0.9, None),
|
||||
size=(0, 200),
|
||||
auto_dismiss=False
|
||||
)
|
||||
popup.open()
|
||||
|
||||
def run_advanced_animation():
|
||||
try:
|
||||
# Update status
|
||||
def update_status(progress_val, status_text):
|
||||
def _update(dt):
|
||||
progress.value = progress_val
|
||||
label.text = status_text
|
||||
Clock.schedule_once(_update, 0)
|
||||
|
||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", self.project_name)
|
||||
positions_path = os.path.join(project_folder, "positions.json")
|
||||
|
||||
if not os.path.exists(positions_path):
|
||||
update_status(0, "Error: No GPS data found")
|
||||
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||
return
|
||||
|
||||
update_status(10, "Loading GPS data...")
|
||||
|
||||
# Check dependencies first
|
||||
generator = Advanced3DGenerator(project_folder)
|
||||
generator.check_dependencies()
|
||||
|
||||
update_status(20, "Processing GPS coordinates...")
|
||||
df = generator.load_gps_data(positions_path)
|
||||
|
||||
update_status(40, "Creating 3D visualization frames...")
|
||||
output_video_path = os.path.join(project_folder, f"{self.project_name}_advanced_3d_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||
|
||||
# Progress callback for the generator
|
||||
def generator_progress(progress, message):
|
||||
update_status(40 + (progress * 0.4), message) # Map 0-100% to 40-80%
|
||||
|
||||
update_status(80, "Rendering video...")
|
||||
success = generator.generate_3d_animation(
|
||||
positions_path,
|
||||
output_video_path,
|
||||
style='advanced',
|
||||
progress_callback=generator_progress
|
||||
)
|
||||
|
||||
if success:
|
||||
update_status(100, "Advanced 3D animation complete!")
|
||||
output_path = output_video_path
|
||||
else:
|
||||
raise Exception("Failed to generate video")
|
||||
|
||||
def show_success(dt):
|
||||
popup.dismiss()
|
||||
self.show_success_popup(
|
||||
"Advanced 3D Animation Complete!",
|
||||
f"Your high-quality 3D animation has been saved to:\n{output_path}",
|
||||
output_path
|
||||
)
|
||||
|
||||
Clock.schedule_once(show_success, 1)
|
||||
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
def show_error(dt):
|
||||
popup.dismiss()
|
||||
self.show_error_popup("Advanced Animation Error", error_message)
|
||||
|
||||
Clock.schedule_once(show_error, 0)
|
||||
|
||||
# Schedule the animation generation
|
||||
Clock.schedule_once(lambda dt: run_advanced_animation(), 0.5)
|
||||
|
||||
def generate_blender_animation(self):
|
||||
"""Generate cinema-quality animation using Blender"""
|
||||
# Show processing popup
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Initializing Blender rendering pipeline...")
|
||||
progress = ProgressBar(max=100, value=0)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Generating Blender Cinema Animation",
|
||||
content=layout,
|
||||
size_hint=(0.9, None),
|
||||
size=(0, 200),
|
||||
auto_dismiss=False
|
||||
)
|
||||
popup.open()
|
||||
|
||||
def run_blender_animation():
|
||||
try:
|
||||
# Update status
|
||||
def update_status(progress_val, status_text):
|
||||
def _update(dt):
|
||||
progress.value = progress_val
|
||||
label.text = status_text
|
||||
Clock.schedule_once(_update, 0)
|
||||
|
||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", self.project_name)
|
||||
positions_path = os.path.join(project_folder, "positions.json")
|
||||
|
||||
if not os.path.exists(positions_path):
|
||||
update_status(0, "Error: No GPS data found")
|
||||
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||
return
|
||||
|
||||
update_status(10, "Loading GPS data into Blender...")
|
||||
|
||||
# Check dependencies first
|
||||
animator = BlenderGPSAnimator(project_folder)
|
||||
animator.check_dependencies()
|
||||
|
||||
update_status(25, "Processing GPS coordinates...")
|
||||
gps_data = animator.load_gps_data(positions_path)
|
||||
|
||||
output_video_path = os.path.join(project_folder, f"{self.project_name}_blender_cinema_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||
|
||||
# Progress callback for the animator
|
||||
def animator_progress(progress, message):
|
||||
update_status(25 + (progress * 0.6), message) # Map 0-100% to 25-85%
|
||||
|
||||
update_status(85, "Rendering cinema-quality video...")
|
||||
success = animator.create_gps_animation(
|
||||
positions_path,
|
||||
output_video_path,
|
||||
progress_callback=animator_progress
|
||||
)
|
||||
|
||||
if success:
|
||||
update_status(100, "Blender cinema animation complete!")
|
||||
output_path = output_video_path
|
||||
else:
|
||||
raise Exception("Failed to render Blender animation")
|
||||
|
||||
def show_success(dt):
|
||||
popup.dismiss()
|
||||
self.show_success_popup(
|
||||
"Blender Cinema Animation Complete!",
|
||||
f"Your cinema-quality animation has been rendered to:\n{output_path}",
|
||||
output_path
|
||||
)
|
||||
|
||||
Clock.schedule_once(show_success, 1)
|
||||
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
def show_error(dt):
|
||||
popup.dismiss()
|
||||
self.show_error_popup("Blender Animation Error", error_message)
|
||||
|
||||
Clock.schedule_once(show_error, 0)
|
||||
|
||||
# Schedule the animation generation
|
||||
Clock.schedule_once(lambda dt: run_blender_animation(), 0.5)
|
||||
|
||||
def show_success_popup(self, title, message, file_path=None):
|
||||
"""Show success popup with option to open file location"""
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
|
||||
success_label = Label(
|
||||
text=message,
|
||||
text_size=(400, None),
|
||||
halign="center",
|
||||
valign="middle"
|
||||
)
|
||||
layout.add_widget(success_label)
|
||||
|
||||
button_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint_y=None, height=50)
|
||||
|
||||
if file_path:
|
||||
open_btn = Button(text="Open Folder", background_color=(0.2, 0.8, 0.2, 1))
|
||||
open_btn.bind(on_press=lambda x: self.open_file_location(file_path))
|
||||
button_layout.add_widget(open_btn)
|
||||
|
||||
ok_btn = Button(text="OK", background_color=(0.2, 0.6, 0.9, 1))
|
||||
button_layout.add_widget(ok_btn)
|
||||
layout.add_widget(button_layout)
|
||||
|
||||
popup = Popup(
|
||||
title=title,
|
||||
content=layout,
|
||||
size_hint=(0.8, None),
|
||||
size=(0, 250),
|
||||
auto_dismiss=False
|
||||
)
|
||||
|
||||
ok_btn.bind(on_press=lambda x: popup.dismiss())
|
||||
popup.open()
|
||||
|
||||
def show_error_popup(self, title, message):
|
||||
"""Show error popup"""
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
|
||||
error_label = Label(
|
||||
text=f"Error: {message}",
|
||||
text_size=(400, None),
|
||||
halign="center",
|
||||
valign="middle",
|
||||
color=(1, 0.3, 0.3, 1)
|
||||
)
|
||||
layout.add_widget(error_label)
|
||||
|
||||
ok_btn = Button(text="OK", background_color=(0.8, 0.2, 0.2, 1), size_hint_y=None, height=50)
|
||||
layout.add_widget(ok_btn)
|
||||
|
||||
popup = Popup(
|
||||
title=title,
|
||||
content=layout,
|
||||
size_hint=(0.8, None),
|
||||
size=(0, 200),
|
||||
auto_dismiss=False
|
||||
)
|
||||
|
||||
ok_btn.bind(on_press=lambda x: popup.dismiss())
|
||||
popup.open()
|
||||
|
||||
def open_file_location(self, file_path):
|
||||
"""Open file location in system file manager"""
|
||||
import subprocess
|
||||
import platform
|
||||
|
||||
folder_path = os.path.dirname(file_path)
|
||||
|
||||
try:
|
||||
if platform.system() == "Linux":
|
||||
subprocess.run(["xdg-open", folder_path])
|
||||
elif platform.system() == "Darwin": # macOS
|
||||
subprocess.run(["open", folder_path])
|
||||
elif platform.system() == "Windows":
|
||||
subprocess.run(["explorer", folder_path])
|
||||
except Exception as e:
|
||||
print(f"Could not open folder: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user