uploaded
This commit is contained in:
Binary file not shown.
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user