This commit is contained in:
2025-07-10 15:26:18 +03:00
parent 911143dfc5
commit 1d0dc05a7b
326 changed files with 10190 additions and 36 deletions

View File

@@ -10,7 +10,7 @@ from py_scripts.utils import (
process_preview_util, optimize_route_entries_util
)
from py_scripts.advanced_3d_generator import NavigationAnimationGenerator
from py_scripts.blender_animator import BlenderGPSAnimator
# BlenderGPSAnimator imported conditionally when needed
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.label import Label
@@ -250,15 +250,15 @@ class CreateAnimationScreen(Screen):
Clock.schedule_once(lambda dt: run_google_earth_animation(), 0.5)
def generate_blender_animation(self):
"""Generate cinema-quality animation using Blender"""
"""Generate cinema-quality animation using Blender (or fallback to advanced 3D)"""
# Show processing popup
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
label = Label(text="Initializing Blender rendering pipeline...")
label = Label(text="Initializing cinema rendering pipeline...")
progress = ProgressBar(max=100, value=0)
layout.add_widget(label)
layout.add_widget(progress)
popup = Popup(
title="Generating Blender Cinema Animation",
title="Generating Cinema Animation",
content=layout,
size_hint=(0.9, None),
size=(0, 200),
@@ -283,39 +283,244 @@ class CreateAnimationScreen(Screen):
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 generate Blender animation")
# Check if Blender is available
try:
from py_scripts.blender_animator import BLENDER_AVAILABLE, BlenderGPSAnimator
if BLENDER_AVAILABLE:
update_status(10, "Loading GPS data into Blender...")
# Use Blender for rendering
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 generate Blender animation")
else:
raise ImportError("Blender not available")
except ImportError:
# Fallback to advanced 3D animation with cinema-quality settings
update_status(10, "Blender not available - using advanced 3D cinema mode...")
# Import here to avoid startup delays
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import cv2
# Load GPS data
with open(positions_path, 'r') as f:
positions = json.load(f)
if len(positions) < 2:
update_status(0, "Error: Need at least 2 GPS points")
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
return
update_status(20, "Processing GPS coordinates for cinema rendering...")
# Extract coordinates
lats = np.array([pos['latitude'] for pos in positions])
lons = np.array([pos['longitude'] for pos in positions])
alts = np.array([pos.get('altitude', 0) for pos in positions])
timestamps = [pos.get('fixTime', '') for pos in positions]
# Convert to relative coordinates
lat_center = np.mean(lats)
lon_center = np.mean(lons)
alt_min = np.min(alts)
x = (lons - lon_center) * 111320 * np.cos(np.radians(lat_center))
y = (lats - lat_center) * 110540
z = alts - alt_min
update_status(30, "Creating cinema-quality frames...")
# Cinema settings - higher quality
frames_folder = os.path.join(project_folder, "cinema_frames")
os.makedirs(frames_folder, exist_ok=True)
fps = 24 # Cinema standard
total_frames = min(len(positions), 200) # Limit for reasonable processing time
points_per_frame = max(1, len(positions) // total_frames)
frame_files = []
# Generate cinema-quality frames
for frame_idx in range(total_frames):
current_progress = 30 + (frame_idx / total_frames) * 50
update_status(current_progress, f"Rendering cinema frame {frame_idx + 1}/{total_frames}...")
end_point = min((frame_idx + 1) * points_per_frame, len(positions))
# Create high-quality 3D plot
plt.style.use('dark_background') # Cinema-style dark theme
fig = plt.figure(figsize=(16, 12), dpi=150) # Higher resolution
ax = fig.add_subplot(111, projection='3d')
# Plot route with cinema styling
if end_point > 1:
# Gradient effect for completed route
colors = np.linspace(0, 1, end_point)
ax.scatter(x[:end_point], y[:end_point], z[:end_point],
c=colors, cmap='plasma', s=30, alpha=0.8)
ax.plot(x[:end_point], y[:end_point], z[:end_point],
color='cyan', linewidth=3, alpha=0.9)
# Current position with glow effect
if end_point > 0:
current_idx = end_point - 1
# Multiple layers for glow effect
for size, alpha in [(200, 0.3), (150, 0.5), (100, 0.8)]:
ax.scatter(x[current_idx], y[current_idx], z[current_idx],
c='yellow', s=size, alpha=alpha, marker='o')
# Trail effect
trail_start = max(0, current_idx - 10)
if current_idx > trail_start:
trail_alpha = np.linspace(0.3, 1.0, current_idx - trail_start + 1)
for i, alpha in enumerate(trail_alpha):
idx = trail_start + i
ax.scatter(x[idx], y[idx], z[idx],
c='orange', s=60, alpha=alpha)
# Remaining route preview
if end_point < len(positions):
ax.plot(x[end_point:], y[end_point:], z[end_point:],
color='gray', linewidth=1, alpha=0.4, linestyle='--')
# Cinema-style labels and styling
ax.set_xlabel('East-West (m)', color='white', fontsize=14)
ax.set_ylabel('North-South (m)', color='white', fontsize=14)
ax.set_zlabel('Elevation (m)', color='white', fontsize=14)
# Progress and time info
progress_percent = (end_point / len(positions)) * 100
timestamp_str = timestamps[end_point-1] if end_point > 0 else "Start"
ax.set_title(f'CINEMA GPS JOURNEY\nProgress: {progress_percent:.1f}% • Point {end_point}/{len(positions)}{timestamp_str}',
color='white', fontsize=16, pad=20, weight='bold')
# Consistent view with cinematic angle
margin = max(np.ptp(x), np.ptp(y)) * 0.15
ax.set_xlim(np.min(x) - margin, np.max(x) + margin)
ax.set_ylim(np.min(y) - margin, np.max(y) + margin)
ax.set_zlim(np.min(z) - 20, np.max(z) + 20)
# Dynamic camera movement for cinematic effect
azim = 45 + (frame_idx * 0.5) % 360 # Slowly rotating view
ax.view_init(elev=25, azim=azim)
# Cinema-style grid
ax.grid(True, alpha=0.2, color='white')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
# Make pane edges more subtle
ax.xaxis.pane.set_edgecolor('gray')
ax.yaxis.pane.set_edgecolor('gray')
ax.zaxis.pane.set_edgecolor('gray')
ax.xaxis.pane.set_alpha(0.1)
ax.yaxis.pane.set_alpha(0.1)
ax.zaxis.pane.set_alpha(0.1)
# Save high-quality frame
frame_path = os.path.join(frames_folder, f"cinema_frame_{frame_idx:04d}.png")
try:
plt.savefig(frame_path, dpi=150, bbox_inches='tight',
facecolor='black', edgecolor='none', format='png')
plt.close(fig)
if os.path.exists(frame_path) and os.path.getsize(frame_path) > 1024:
test_frame = cv2.imread(frame_path)
if test_frame is not None:
frame_files.append(frame_path)
if frame_idx == 0:
h, w, c = test_frame.shape
update_status(current_progress, f"Cinema quality: {w}x{h} at {fps} FPS")
except Exception as frame_error:
update_status(current_progress, f"Error creating frame {frame_idx}: {str(frame_error)}")
plt.close(fig)
continue
plt.style.use('default') # Reset style
# Create cinema video
if not frame_files:
raise Exception("No valid cinema frames were generated")
update_status(80, f"Creating cinema video from {len(frame_files)} frames...")
output_video_path = os.path.join(project_folder, f"{self.project_name}_cinema_3d_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
# Cinema video creation with higher quality
first_frame = cv2.imread(frame_files[0])
height, width, layers = first_frame.shape
# Try to create high-quality video
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
if video_writer.isOpened():
for i, frame_file in enumerate(frame_files):
frame = cv2.imread(frame_file)
if frame is not None:
video_writer.write(frame)
if i % 10 == 0:
progress = 80 + (i / len(frame_files)) * 8
update_status(progress, f"Encoding cinema frame {i+1}/{len(frame_files)}")
video_writer.release()
if os.path.exists(output_video_path) and os.path.getsize(output_video_path) > 1024:
update_status(90, "Cinema video created successfully")
output_path = output_video_path
else:
raise Exception("Cinema video creation failed")
else:
raise Exception("Could not initialize cinema video writer")
# Clean up frames
for frame_file in frame_files:
try:
os.remove(frame_file)
except:
pass
try:
os.rmdir(frames_folder)
except:
pass
update_status(100, "Cinema animation complete!")
def show_success(dt):
popup.dismiss()
self.show_success_popup(
"Blender Cinema Animation Complete!",
f"Your cinema-quality animation has been saved to:\n{output_path}",
"Cinema Animation Complete!",
f"Your cinema-quality animation has been saved to:\n{output_path}\n\nNote: Blender was not available, so advanced 3D cinema mode was used instead.",
output_path
)
@@ -323,9 +528,12 @@ class CreateAnimationScreen(Screen):
except Exception as e:
error_message = str(e)
print(f"DEBUG: Cinema animation error: {error_message}")
import traceback
traceback.print_exc()
def show_error(dt):
popup.dismiss()
self.show_error_popup("Blender Animation Error", error_message)
self.show_error_popup("Cinema Animation Error", error_message)
Clock.schedule_once(show_error, 0)