Added the base
This commit is contained in:
5
scrips/README.txt
Normal file
5
scrips/README.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# Flight Animation sample
|
||||
This sample shows my flight from BER over HEL to NRT. The frame count needs to be done by hand for now for a clean look
|
||||
|
||||
## Name of the objects
|
||||
The name of the Objects need to match the Name given in Blender on the right
|
||||
572
scrips/flight_animation.py
Normal file
572
scrips/flight_animation.py
Normal file
@@ -0,0 +1,572 @@
|
||||
import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import re
|
||||
from mathutils.bvhtree import BVHTree
|
||||
from mathutils import Vector, Euler, Matrix
|
||||
from typing import List, Tuple
|
||||
|
||||
import time
|
||||
|
||||
print("\n\n\n\n\n\n\n\n", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "Starting script execution")
|
||||
|
||||
class Airport:
|
||||
name: str
|
||||
longitude: float
|
||||
latitude: float
|
||||
x: float
|
||||
y: float
|
||||
z: float
|
||||
object: bpy.types.Object | None
|
||||
|
||||
def __init__(self, name: str, lon_lat: str, bvh: BVHTree, origin: Vector, radius: float, shader: bpy.types.Material | None):
|
||||
self.name = name
|
||||
lat, lon = parse_coordinates(lon_lat)
|
||||
print(f"Parsed coordinates: {lat}, {lon}")
|
||||
self.latitude = lat
|
||||
self.longitude = lon
|
||||
# print(self.longitude, self.latitude)
|
||||
direction = direction_from_lat_lon(self.latitude, self.longitude)
|
||||
# print("direction:", direction)
|
||||
distance = distance_to_surface_from_latlon(bvh, origin, self.latitude, self.longitude)
|
||||
# print("distance:", distance)
|
||||
if distance is None:
|
||||
raise ValueError(f"No surface found at latitude {self.latitude} and longitude {self.longitude}")
|
||||
pos_vector = vector_to_coordinates(direction, distance, origin)
|
||||
# print("position vector:", pos_vector)
|
||||
# pos_vector = vector_to_coordinates(direction, distance, origin)
|
||||
|
||||
self.x = pos_vector.x
|
||||
self.y = pos_vector.y
|
||||
self.z = pos_vector.z
|
||||
try:
|
||||
sphere = bpy.data.objects.get(name)
|
||||
if not sphere is None:
|
||||
# print(f"Sphere with name '{name}' already exists.")
|
||||
if not pos_vector == sphere.location:
|
||||
print("Sphere already exists, but location is different. Updating location.")
|
||||
sphere.location.x = pos_vector.x
|
||||
sphere.location.y = pos_vector.y
|
||||
sphere.location.z = pos_vector.z
|
||||
else:
|
||||
print(f"Sphere with name '{name}' does not exist, creating a new one.")
|
||||
sphere = create_sphere(pos_vector, radius=radius, name=name, shader=shader)
|
||||
except:
|
||||
print("Creating new sphere object.")
|
||||
sphere = create_sphere(pos_vector, radius=radius, name=name, shader=shader)
|
||||
self.object = sphere
|
||||
|
||||
def __str__(self):
|
||||
return f"Airport(name={self.name}, longitude={self.longitude}, latitude={self.latitude}, x={self.x}, y={self.y}, z={self.z})"
|
||||
|
||||
def get_coordinates(self):
|
||||
return Vector((self.x, self.y, self.z))
|
||||
|
||||
def get_location(self):
|
||||
return Vector((self.phi, self.theta, self.height))
|
||||
|
||||
### World Building Functions
|
||||
|
||||
def parse_coordinates(coord_str: str) -> Tuple[float, float]:
|
||||
"""
|
||||
Parses a coordinate string in the format '15° 52′ 16″ S, 47° 55′ 7″ W'
|
||||
and returns (latitude, longitude) in decimal degrees.
|
||||
|
||||
Args:
|
||||
coord_str (str): Coordinate string.
|
||||
|
||||
Returns:
|
||||
tuple: (latitude, longitude) in decimal degrees.
|
||||
"""
|
||||
# Regular expression to match DMS components and direction
|
||||
pattern = r"(\d+)[°º]\s*(\d+)[′']\s*(\d+)[″\"]?\s*([NSEOW])"
|
||||
matches = re.findall(pattern, coord_str)
|
||||
|
||||
if len(matches) != 2:
|
||||
raise ValueError("Invalid coordinate format. Expecting two sets of DMS with direction.")
|
||||
|
||||
def dms_to_decimal(degree, minute, second, direction):
|
||||
degree = int(degree)
|
||||
minute = int(minute)
|
||||
second = int(second)
|
||||
decimal = degree + minute / 60 + second / 3600
|
||||
dir = direction.upper()
|
||||
|
||||
# Normalize 'O' (German "Ost") to 'E' (East)
|
||||
if dir == 'O':
|
||||
dir = 'E'
|
||||
|
||||
if dir in ('S', 'W'):
|
||||
return -decimal
|
||||
return decimal
|
||||
|
||||
|
||||
lat = dms_to_decimal(*matches[0])
|
||||
lon = dms_to_decimal(*matches[1])
|
||||
|
||||
return lat, lon
|
||||
|
||||
def direction_from_lat_lon(latitude_deg: float, longitude_deg: float) -> Vector:
|
||||
"""Convert latitude and longitude (in degrees) to a unit direction vector."""
|
||||
lat = math.radians(latitude_deg)
|
||||
lon = math.radians(longitude_deg)
|
||||
|
||||
x = math.cos(lat) * math.cos(lon)
|
||||
y = math.cos(lat) * math.sin(lon)
|
||||
z = math.sin(lat)
|
||||
|
||||
return Vector((x, y, z))
|
||||
|
||||
def distance_to_surface_from_latlon(
|
||||
bvh: BVHTree,
|
||||
origin: Vector,
|
||||
latitude_deg: float,
|
||||
longitude_deg: float,
|
||||
max_dist=100.0
|
||||
) -> float | None:
|
||||
"""Cast a ray from origin in a direction defined by lat/lon and return the hit distance."""
|
||||
direction = direction_from_lat_lon(latitude_deg, longitude_deg)
|
||||
|
||||
hit = bvh.ray_cast(origin, direction, max_dist)
|
||||
|
||||
if hit[0]: # hit[0] is hit location
|
||||
hit_point = hit[0]
|
||||
return (hit_point - origin).length
|
||||
else:
|
||||
return None # No hit found
|
||||
|
||||
def vector_to_coordinates(direction: Vector, length: float, origin: Vector = Vector((0, 0, 0))) -> Vector:
|
||||
"""
|
||||
Returns a 3D coordinate in space by extending a direction vector from an origin point.
|
||||
|
||||
Args:
|
||||
direction (Vector): The direction vector (will be normalized).
|
||||
length (float): Distance from the origin along the direction.
|
||||
origin (Vector): Starting point (default is the origin (0, 0, 0)).
|
||||
|
||||
Returns:
|
||||
Vector: The resulting 3D point.
|
||||
"""
|
||||
direction = direction.normalized()
|
||||
return origin + direction * length
|
||||
|
||||
def create_sphere(location: Vector, radius: float, name: str, shader: bpy.types.Material | None) -> bpy.types.Object: # Create a red UV sphere at the specified location
|
||||
"""
|
||||
Creates a red UV sphere at the given location in the scene.
|
||||
|
||||
Args:
|
||||
location (Vector): The location to place the sphere.
|
||||
radius (float): Radius of the sphere.
|
||||
name (str): Name of the sphere object and its mesh data.
|
||||
"""
|
||||
# Create the UV sphere
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(radius=radius, location=location)
|
||||
sphere = bpy.context.active_object
|
||||
sphere.name = name
|
||||
sphere.data.name = f"{name}_Mesh"
|
||||
|
||||
# Create a red material if it doesn't exist
|
||||
if shader == None:
|
||||
mat_name = "RedMaterial"
|
||||
if mat_name in bpy.data.materials:
|
||||
shader = bpy.data.materials[mat_name]
|
||||
else:
|
||||
shader = bpy.data.materials.new(name=mat_name)
|
||||
shader.diffuse_color = (1.0, 0.0, 0.0, 1.0) # RGBA red
|
||||
shader.use_nodes = False
|
||||
|
||||
# Assign the material
|
||||
if len(sphere.data.materials) == 0:
|
||||
sphere.data.materials.append(shader)
|
||||
else:
|
||||
sphere.data.materials[0] = shader
|
||||
return sphere
|
||||
|
||||
def create_sphere_from_coordinates(
|
||||
lat_long_str: str,
|
||||
name: str,
|
||||
bvh: BVHTree,
|
||||
origin: Vector,
|
||||
radius: float,
|
||||
shader: bpy.types.Material | None
|
||||
) -> Tuple[Vector, bpy.types.Object]:
|
||||
"""
|
||||
Creates a sphere at the specified latitude and longitude on the surface of the sphere.
|
||||
|
||||
Args:
|
||||
lat_long_str (str): Latitude and longitude in the format '15° 52′ 16″ S, 47° 55′ 7″ W'.
|
||||
name (str): Name of the sphere object.
|
||||
bvh (BVHTree): The BVH tree for ray casting.
|
||||
origin (Vector): The origin point from which to calculate the sphere's position.
|
||||
radius (float): Radius of the sphere.
|
||||
shader (str | None): The shader to apply to the sphere, if any.
|
||||
Returns:
|
||||
bpy.types.Object: The created sphere object.
|
||||
"""
|
||||
lat_deg, lon_deg = parse_coordinates(lat_long_str)
|
||||
print(lat_deg, lon_deg)
|
||||
direction = direction_from_lat_lon(lat_deg, lon_deg)
|
||||
print("direction:", direction)
|
||||
distance = distance_to_surface_from_latlon(bvh, origin, lat_deg, lon_deg)
|
||||
print("distance:", distance)
|
||||
|
||||
if distance is None:
|
||||
raise ValueError(f"No surface found at latitude {lat_deg} and longitude {lon_deg}")
|
||||
point = vector_to_coordinates(direction, distance, origin)
|
||||
print("point:", point)
|
||||
sphere = None
|
||||
try:
|
||||
sphere = bpy.data.objects.get(name)
|
||||
if not sphere is None:
|
||||
# print(f"Sphere with name '{name}' already exists.")
|
||||
if not point == sphere.location:
|
||||
print("Sphere already exists, but location is different. Updating location.")
|
||||
sphere.location.x = point.x
|
||||
sphere.location.y = point.y
|
||||
sphere.location.z = point.z
|
||||
else:
|
||||
print(f"Sphere with name '{name}' does not exist, creating a new one.")
|
||||
sphere = create_sphere(point, radius=radius, name=name, shader=shader)
|
||||
except:
|
||||
print("Creating new sphere object.")
|
||||
sphere = create_sphere(point, radius=radius, name=name, shader=shader)
|
||||
return point, sphere
|
||||
|
||||
def create_bvh_from_object(Object: bpy.types.Object) -> BVHTree:
|
||||
"""Creates a new BVH from the main mesh.
|
||||
Args:
|
||||
Object (bpy.types.Object): The Object to create the BVH from.
|
||||
Returns:
|
||||
BVHTree: The created BVH object.
|
||||
"""
|
||||
main_mesh = Object.data
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(main_mesh)
|
||||
bm.transform(Object.matrix_world)
|
||||
bm.normal_update()
|
||||
|
||||
# Create the BVH tree from the bmesh
|
||||
bvh = BVHTree.FromBMesh(bm)
|
||||
|
||||
# Free the bmesh to avoid memory leaks
|
||||
bm.free()
|
||||
return bvh
|
||||
|
||||
def reset_object(object: bpy.types.Object):
|
||||
"""Resets the location and rotation of the main objects in the scene."""
|
||||
if object is not None:
|
||||
object.location = Vector((0, 0, 0))
|
||||
object.rotation_euler = Euler((0, 0, 0), 'XYZ')
|
||||
else:
|
||||
print(f"Object {object} is None, cannot reset location and rotation.")
|
||||
|
||||
### Animation Functions
|
||||
def align_vectors_via_xz_rotation(source: Vector, target: Vector) -> Tuple[float, float]:
|
||||
"""
|
||||
Aligns 'source' vector to 'target' vector using rotation around X and Z axes only.
|
||||
|
||||
Args:
|
||||
source (Vector): The source direction vector (should be normalized).
|
||||
target (Vector): The target direction vector (should be normalized).
|
||||
|
||||
Returns:
|
||||
Tuple[float, float]: (rot_x, rot_z) in radians to align source to target.
|
||||
"""
|
||||
# Ensure both vectors are normalized
|
||||
source = source.normalized()
|
||||
target = target.normalized()
|
||||
|
||||
# Rotate source vector around X-axis to match target's Z component
|
||||
# Compute angle between projected vectors in YZ plane
|
||||
source_yz = Vector((source.y, source.z))
|
||||
target_yz = Vector((target.y, target.z))
|
||||
angle_x = source_yz.angle_signed(target_yz)
|
||||
|
||||
# Apply X rotation to source vector
|
||||
temp_vec = source.copy()
|
||||
temp_vec.rotate(Euler((angle_x, 0, 0), 'XYZ'))
|
||||
|
||||
# Rotate adjusted vector around Z-axis to match target in XY plane
|
||||
temp_xy = Vector((temp_vec.x, temp_vec.y))
|
||||
target_xy = Vector((target.x, target.y))
|
||||
angle_z = temp_xy.angle_signed(target_xy)
|
||||
|
||||
return angle_x, angle_z
|
||||
|
||||
def apply_rotation_to_vector(
|
||||
vector: Vector,
|
||||
rot_x: float,
|
||||
rot_z: float
|
||||
) -> Vector:
|
||||
"""
|
||||
Applies rotation around X and Z axes to a vector.
|
||||
|
||||
Args:
|
||||
vector (Vector): The original vector.
|
||||
rot_x (float): Rotation angle around X axis in radians.
|
||||
rot_z (float): Rotation angle around Z axis in radians.
|
||||
|
||||
Returns:
|
||||
Vector: The rotated vector.
|
||||
"""
|
||||
vector = Vector(vector)
|
||||
# Copy to avoid modifying original vector
|
||||
rotated_vector = vector.copy()
|
||||
|
||||
# Apply rotations in XYZ order: first X, then Z
|
||||
rotation = Euler((rot_x, 0.0, rot_z), 'XYZ')
|
||||
rotated_vector.rotate(rotation)
|
||||
|
||||
return rotated_vector
|
||||
|
||||
def local_z_rotation_to_align(vec1: Vector, vec2: Vector, degrees=False) -> float:
|
||||
"""
|
||||
Calculates the rotation angle around the local Z axis of vec1 to align it with vec2.
|
||||
|
||||
Args:
|
||||
vec1 (Vector): The source vector (must be normalized).
|
||||
vec2 (Vector): The target vector (must be normalized).
|
||||
degrees (bool): If True, return angle in degrees. Otherwise, radians.
|
||||
|
||||
Returns:
|
||||
float: Angle to rotate vec1 around its local Z axis to align best with vec2.
|
||||
"""
|
||||
# Normalize input
|
||||
v1 = vec1.normalized()
|
||||
v2 = vec2.normalized()
|
||||
|
||||
# Project both vectors onto the XY plane (Z rotation only affects XY)
|
||||
v1_xy = Vector((v1.x, v1.y)).normalized()
|
||||
v2_xy = Vector((v2.x, v2.y)).normalized()
|
||||
|
||||
# Compute angle between projections
|
||||
dot = max(-1.0, min(1.0, v1_xy.dot(v2_xy))) # Clamp for safety
|
||||
angle = math.acos(dot)
|
||||
|
||||
# Determine rotation direction (sign) using 2D cross product
|
||||
cross = v1_xy.x * v2_xy.y - v1_xy.y * v2_xy.x
|
||||
if cross < 0:
|
||||
angle = -angle
|
||||
|
||||
return math.degrees(angle) if degrees else angle
|
||||
|
||||
def get_point_of_triangle(v1: Vector, v2: Vector, vector1: Vector) -> Vector:
|
||||
"""
|
||||
Returns the point of intersection of the triangle formed by v1, v2, and vector with the XY plane.
|
||||
|
||||
Args:
|
||||
v1 (Vector): First vertex of the triangle (with 90 degrees).
|
||||
v2 (Vector): Second vertex of the triangle.
|
||||
vector (Vector): Vector to 3rd vertex of the triangle.
|
||||
|
||||
Returns:
|
||||
Vector: The point where the 3rd vertex would be.
|
||||
"""
|
||||
direction = vector1.normalized()
|
||||
cos_alpha = v1.normalized().dot(direction)
|
||||
cos_alpha = max(min(cos_alpha, 1.0), -1.0)
|
||||
|
||||
if abs(cos_alpha) < 1e-6:
|
||||
raise ValueError("Angle too close to 90°, cannot compute third point reliably.")
|
||||
|
||||
v3_length = v1.length / cos_alpha
|
||||
v3 = direction * v3_length
|
||||
|
||||
return v3
|
||||
|
||||
def clear_timeline(objects: List[bpy.types.Object]) -> None:
|
||||
"""
|
||||
Clears keyframes for 'rotation_euler' and 'location.z' from the given objects,
|
||||
and resets those values to 0.0.
|
||||
|
||||
Args:
|
||||
objects (List[bpy.types.Object]): List of objects to clear keyframes from.
|
||||
"""
|
||||
for obj in objects:
|
||||
if obj.animation_data and obj.animation_data.action:
|
||||
action = obj.animation_data.action
|
||||
fcurves_to_remove = [
|
||||
fc for fc in action.fcurves
|
||||
if fc.data_path == "rotation_euler"
|
||||
or (fc.data_path == "location" and fc.array_index == 2)
|
||||
]
|
||||
for fc in fcurves_to_remove:
|
||||
action.fcurves.remove(fc)
|
||||
|
||||
# Reset rotation_euler (X, Y, Z) to 0
|
||||
obj.rotation_euler = (0.0, 0.0, 0.0)
|
||||
|
||||
# Reset location.z to 0, keep x and y unchanged
|
||||
loc = obj.location
|
||||
obj.location = (loc.x, loc.y, 0.0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
### Initialization
|
||||
|
||||
main_sphere = bpy.data.objects["Sphere.002"]
|
||||
|
||||
origin: Vector = main_sphere.matrix_world.translation
|
||||
|
||||
bvh=create_bvh_from_object(main_sphere)
|
||||
|
||||
### Setting up world
|
||||
|
||||
airport_shader: bpy.types.Material | None = None # Placeholder for shader, can be set to a specific material if needed
|
||||
|
||||
# flughafen1_vec, flughafen1_obj = create_sphere_from_coordinates(
|
||||
# lat_long_str="52° 21′ 44″ N, 13° 30′ 2″ O",
|
||||
# name="BER1",
|
||||
# bvh=bvh,
|
||||
# origin=origin,
|
||||
# radius=0.1,
|
||||
# shader=airport_shader
|
||||
# )
|
||||
|
||||
flughafen1 = Airport(
|
||||
name="BER2",
|
||||
lon_lat="52° 21′ 44″ N, 13° 30′ 2″ O",
|
||||
bvh=bvh,
|
||||
origin=origin,
|
||||
radius=0.1,
|
||||
shader=airport_shader
|
||||
)
|
||||
|
||||
flughafen2 = Airport(
|
||||
name="HEL",
|
||||
lon_lat="60° 19′ 2″ N, 24° 57′ 48″ O",
|
||||
bvh=bvh,
|
||||
origin=origin,
|
||||
radius=0.1,
|
||||
shader=airport_shader
|
||||
)
|
||||
|
||||
flughafen3 = Airport(
|
||||
name="NRT",
|
||||
lon_lat="35° 45′ 53″ N, 140° 23′ 11″ O",
|
||||
bvh=bvh,
|
||||
origin=origin,
|
||||
radius=0.1,
|
||||
shader=airport_shader
|
||||
)
|
||||
|
||||
### Initialize Animation
|
||||
|
||||
airplane = bpy.data.objects["Airplane"]
|
||||
# airplane_vector = Vector((0, 1, 0)) # Setting the airplane default vector (x, y, z)
|
||||
# reset_object(airplane) # Resetting the airplane location and rotation
|
||||
|
||||
centerjoint = bpy.data.objects["Center_Joint"]
|
||||
# centerjoint_vectors = Vector((0, 0, 1)) # Setting the center joint default vector (x, y, z)
|
||||
# reset_object(centerjoint) # Resetting the center joint location and rotation
|
||||
|
||||
|
||||
### Animation Logic
|
||||
# Way between airport1 and airport2
|
||||
for airport in [flughafen1, flughafen2, flughafen3]:
|
||||
print("lat: ", airport.latitude, "lon: ", airport.longitude)
|
||||
lat=[
|
||||
math.radians(flughafen1.latitude - 90),
|
||||
math.radians(flughafen2.latitude - 90),
|
||||
math.radians(flughafen3.latitude - 90)
|
||||
]
|
||||
print("lat:", lat)
|
||||
|
||||
lon=[
|
||||
math.radians(flughafen1.longitude-90),
|
||||
math.radians(flughafen2.longitude-90),
|
||||
math.radians(flughafen3.longitude-90)
|
||||
]
|
||||
print("lon:", lon)
|
||||
|
||||
airplane_height = [
|
||||
flughafen1.get_coordinates().length + 0.1,
|
||||
flughafen2.get_coordinates().length + 0.1,
|
||||
flughafen3.get_coordinates().length + 0.1
|
||||
]
|
||||
print("airplane_height:", airplane_height)
|
||||
|
||||
travel_height = airplane_height[0] * 1.1
|
||||
print("travel_height:", travel_height)
|
||||
|
||||
airplane_rotation = [
|
||||
math.radians(-35),
|
||||
math.radians(-42),
|
||||
math.radians(-111),
|
||||
math.radians(-102)
|
||||
]
|
||||
|
||||
frame_count = 720
|
||||
|
||||
clear_timeline([airplane, centerjoint])
|
||||
|
||||
airplane.rotation_euler.x = math.radians(90)
|
||||
|
||||
### Start BER
|
||||
bpy.context.scene.frame_set(0)
|
||||
airplane.location.z = airplane_height[0]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[0]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[0]
|
||||
centerjoint.rotation_euler.z = lon[0]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### flight height start
|
||||
bpy.context.scene.frame_set(32)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### End of high flight
|
||||
bpy.context.scene.frame_set(49)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### Landing HEL
|
||||
bpy.context.scene.frame_set(81)
|
||||
airplane.location.z = airplane_height[1]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[1]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[1]
|
||||
centerjoint.rotation_euler.z = lon[1]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### Start HEL
|
||||
bpy.context.scene.frame_set(127)
|
||||
airplane.location.z = airplane_height[1]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[2]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[1]
|
||||
centerjoint.rotation_euler.z = lon[1]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### flight height start
|
||||
bpy.context.scene.frame_set(255)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### End of high flight
|
||||
bpy.context.scene.frame_set(592)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### Landing NRT
|
||||
bpy.context.scene.frame_set(720)
|
||||
airplane.location.z = airplane_height[2]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[3]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[2]
|
||||
centerjoint.rotation_euler.z = lon[2]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### reset
|
||||
bpy.context.scene.frame_set(0)
|
||||
|
||||
print("END")
|
||||
173
scrips/main.py
Normal file
173
scrips/main.py
Normal file
@@ -0,0 +1,173 @@
|
||||
import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import re
|
||||
from mathutils.bvhtree import BVHTree
|
||||
from mathutils import Vector, Euler, Matrix
|
||||
import time
|
||||
|
||||
import flight_animation
|
||||
|
||||
|
||||
### Setting the globe (the object of which the surface will be used as the height)
|
||||
|
||||
main_sphere = bpy.data.objects["Sphere.002"]
|
||||
|
||||
### Making the globe useful
|
||||
|
||||
origin: Vector = main_sphere.matrix_world.translation
|
||||
|
||||
bvh=flight_animation.create_bvh_from_object(main_sphere)
|
||||
|
||||
### Setting up world
|
||||
|
||||
airport_shader: bpy.types.Material | None = None # Placeholder for shader, can be set to a specific material if needed
|
||||
|
||||
flughafen1 = flight_animation.Airport( # Change to Place you want
|
||||
name="BER2",
|
||||
lon_lat="52° 21′ 44″ N, 13° 30′ 2″ O",
|
||||
bvh=bvh,
|
||||
origin=origin,
|
||||
radius=0.1,
|
||||
shader=airport_shader
|
||||
)
|
||||
|
||||
flughafen2 = flight_animation.Airport(
|
||||
name="HEL",
|
||||
lon_lat="60° 19′ 2″ N, 24° 57′ 48″ O",
|
||||
bvh=bvh,
|
||||
origin=origin,
|
||||
radius=0.1,
|
||||
shader=airport_shader
|
||||
)
|
||||
|
||||
flughafen3 = flight_animation.Airport(
|
||||
name="NRT",
|
||||
lon_lat="35° 45′ 53″ N, 140° 23′ 11″ O",
|
||||
bvh=bvh,
|
||||
origin=origin,
|
||||
radius=0.1,
|
||||
shader=airport_shader
|
||||
)
|
||||
|
||||
### Initialize Animation
|
||||
|
||||
airplane = bpy.data.objects["Airplane"]
|
||||
# airplane_vector = Vector((0, 1, 0)) # Setting the airplane default vector (x, y, z)
|
||||
# reset_object(airplane) # Resetting the airplane location and rotation
|
||||
|
||||
centerjoint = bpy.data.objects["Center_Joint"]
|
||||
# centerjoint_vectors = Vector((0, 0, 1)) # Setting the center joint default vector (x, y, z)
|
||||
# reset_object(centerjoint) # Resetting the center joint location and rotation
|
||||
|
||||
|
||||
### Animation Logic
|
||||
# Way between airport1 and airport2
|
||||
for airport in [flughafen1, flughafen2, flughafen3]:
|
||||
print("lat: ", airport.latitude, "lon: ", airport.longitude)
|
||||
lat=[
|
||||
math.radians(flughafen1.latitude - 90),
|
||||
math.radians(flughafen2.latitude - 90),
|
||||
math.radians(flughafen3.latitude - 90)
|
||||
]
|
||||
print("lat:", lat)
|
||||
|
||||
lon=[
|
||||
math.radians(flughafen1.longitude-90),
|
||||
math.radians(flughafen2.longitude-90),
|
||||
math.radians(flughafen3.longitude-90)
|
||||
]
|
||||
print("lon:", lon)
|
||||
|
||||
airplane_height = [
|
||||
flughafen1.get_coordinates().length + 0.1,
|
||||
flughafen2.get_coordinates().length + 0.1,
|
||||
flughafen3.get_coordinates().length + 0.1
|
||||
]
|
||||
print("airplane_height:", airplane_height)
|
||||
|
||||
travel_height = airplane_height[0] * 1.1 # for 10% extra height in relation to the origin
|
||||
print("travel_height:", travel_height)
|
||||
|
||||
# Set to the Rotation the plane needs to have for the correct rotation at the airport
|
||||
airplane_rotation = [ # rotate the airplane at the timestamps of the airports !!! Only rotate the z achses in the plane propertys
|
||||
math.radians(-35), # rotate so the plane on the first airport so that the front points to the 2nd
|
||||
math.radians(-42), # rotate the plane at the second airport so that the back points to the last airport
|
||||
math.radians(-111), # like first rotation from 2nd to third
|
||||
math.radians(-102) # like 2nd rotation
|
||||
] #! You do not need more rotation points the rest is done by the interpolation in blender. Maybe you need to set the motion to a constant instead of accelerating and decelerating
|
||||
|
||||
frame_count = 720
|
||||
|
||||
clear_timeline([airplane, centerjoint])
|
||||
|
||||
airplane.rotation_euler.x = math.radians(90)
|
||||
|
||||
### Start BER
|
||||
bpy.context.scene.frame_set(0)
|
||||
airplane.location.z = airplane_height[0]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[0]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[0]
|
||||
centerjoint.rotation_euler.z = lon[0]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### flight height start
|
||||
bpy.context.scene.frame_set(32)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### End of high flight
|
||||
bpy.context.scene.frame_set(49)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### Landing HEL
|
||||
bpy.context.scene.frame_set(81)
|
||||
airplane.location.z = airplane_height[1]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[1]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[1]
|
||||
centerjoint.rotation_euler.z = lon[1]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### Start HEL
|
||||
bpy.context.scene.frame_set(127)
|
||||
airplane.location.z = airplane_height[1]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[2]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[1]
|
||||
centerjoint.rotation_euler.z = lon[1]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### flight height start
|
||||
bpy.context.scene.frame_set(255)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### End of high flight
|
||||
bpy.context.scene.frame_set(592)
|
||||
airplane.location.z = travel_height
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
|
||||
### Landing NRT
|
||||
bpy.context.scene.frame_set(720)
|
||||
airplane.location.z = airplane_height[2]
|
||||
airplane.keyframe_insert(data_path="location", index=2)
|
||||
airplane.rotation_euler.z = airplane_rotation[3]
|
||||
airplane.keyframe_insert(data_path="rotation_euler", index=2)
|
||||
|
||||
centerjoint.rotation_euler.x = lat[2]
|
||||
centerjoint.rotation_euler.z = lon[2]
|
||||
centerjoint.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
### reset
|
||||
bpy.context.scene.frame_set(0)
|
||||
|
||||
print("END")
|
||||
Reference in New Issue
Block a user