Timeline Example
This example depicts how to create animations and integrate into a newly created timeline. To use this script, open a new database, create a plane and a cube, then execute the script : the script will create automatically kinematics parent for the cube so that it can be animated.
1from os import path
2from sys import stdout
3from typing import List
4
5from p3dsdk import CurrentP3DSession, Database, P3DFile, Model
6from p3dsdk import CameraBezierPathAnimation, Vector3, CameraBookmarkAnimation
7from p3dsdk import Null, CameraBezierPathAnimationPositionType, CameraBezierPathAnimationTargetType
8from p3dsdk import ChannelsSimpleAnimation, ChannelsCurveAnimation, Surface
9
10
11def find_kinematic_object(db: Database, name: str) -> Null:
12 for obj in db.list_models()[0].list_kinematic_objects():
13 if obj.name == name:
14 return obj
15 raise Exception(f"No kinemtaics object named '{name}' found in the database")
16
17
18def find_surface(db: Database, names: List[str]) -> Surface:
19 geometry_layers = db.list_models()[0].list_geometry_layers()
20 for layer in geometry_layers:
21 for surface in layer.list_surfaces():
22 for name in names:
23 if surface.name.startswith(name):
24 return surface
25 raise Exception(f"No surface starting by '{' or '.join(names)}' found in the database")
26
27
28def clean_empty_timelines(db: Database):
29 timelines_to_remove = []
30 for timeline in db.list_timelines():
31 all_is_empty = timeline.product_animation_track().empty
32 all_is_empty &= timeline.camera_animation_track().empty
33 all_is_empty &= timeline.configuration_animation_track().empty
34 all_is_empty &= timeline.empty_channels_track().empty
35 all_is_empty &= timeline.empty_texture_track().empty
36 for channel_track in timeline.channels_animation_tracks():
37 all_is_empty &= channel_track.empty
38 for texture_track in timeline.texture_animation_tracks():
39 all_is_empty &= texture_track.empty
40 if all_is_empty:
41 timelines_to_remove.append(timeline)
42 for timeline_to_remove in timelines_to_remove:
43 timeline_to_remove.remove()
44
45
46def create_bezier_path_camera_anim(db: Database) -> CameraBezierPathAnimation :
47 anim = db.create_camera_bezier_path_animation('rotate_camera')
48 anim.duration = 3
49 anim.position_type = CameraBezierPathAnimationPositionType.FOLLOW_BEZIER_PATH
50 bezier_path = db.list_models()[0].create_bezier_path('camera_bezier_path', [], [])
51 bezier_path.translation = Vector3(0, 0.8, 0)
52 anim.bezier_path_used_for_position = bezier_path
53 anim.position_bezier_path_inverted = True
54 anim.target_type = CameraBezierPathAnimationTargetType.FOLLOW_NULL
55 anim.target_null = db.list_models()[0].create_null('camera_target_null')
56 return anim
57
58
59def create_bookmark_camera_anim(db: Database) -> CameraBookmarkAnimation :
60 anim = db.create_camera_bookmark_animation('zoom_camera')
61 group: CameraGroup = db.create_camera_group('CameraGroup For Animation')
62 camera_1 = db.create_camera(group)
63 camera_1.name = 'bookmarkCamera1'
64 camera_1.view_from = Vector3(1,0.8,0)
65 camera_2 = db.create_camera(group)
66 camera_2.name = 'bookmarkCamera2'
67 camera_2.view_from = Vector3(0.5, 0.4, 0.0)
68 camera_1 = db.create_camera(group)
69 camera_1.name = 'bookmarkCamera3'
70 camera_1.view_from = Vector3(1, 0.8, 0)
71 anim.add_camera_from_camera_group_to_animation(group=group)
72 for camera_bookmark in anim.list_camera_bookmarks():
73 camera_bookmark.duration = 1
74 anim.list_camera_bookmarks()[-1].duration = 0
75 return anim
76
77
78def create_simple_channel_animation(db: Database) -> ChannelsSimpleAnimation :
79 anim = db.create_channels_simple_animation('rotate_box')
80 axis = find_kinematic_object(db, 'animated_axis')
81 anim.set_axis_channel_animation(object=axis, start=0, end=360)
82 anim.duration = 2
83 return anim
84
85
86def create_curve_channel_animation(db: Database) -> ChannelsCurveAnimation :
87 anim = db.create_channels_curve_animation('translate_box')
88 anim.duration = 3
89
90 null_obj = find_kinematic_object(db, 'animated_null')
91 null_try_curve = anim.add_null_channel_animation(object=null_obj, channel='Translation/X')
92 null_try_curve.insert_key_frame(1, 0.3)
93 null_try_curve.insert_key_frame(2, 0.3)
94 null_try_curve.insert_key_frame(3, 0.0)
95 null_try_curve = anim.add_null_channel_animation(object=null_obj, channel='Translation/Z')
96 null_try_curve.insert_key_frame(1, 0.3)
97 null_try_curve.insert_key_frame(2, -0.3)
98 null_try_curve.insert_key_frame(3, 0.0)
99
100 return anim
101
102
103def prepare_product(db: Database):
104 product = db.list_models()[0].create_product('Product')
105 aspect_layer = product.create_aspect_layer("materials")
106 mat_group = db.create_material_group("gbuffer materials")
107
108 geometry_layers = db.list_models()[0].list_geometry_layers()
109 for layer in geometry_layers:
110 for surface in layer.list_surfaces():
111 color = surface.color
112 material = mat_group.create_standard_material("mat-" + surface.name)
113 material.diffuse_color = color;
114 aspect_layer.assign_material(surface, material)
115
116
117def prepare_kinematics(db: Database):
118 null_obj = db.list_models()[0].create_null('animated_null')
119 axis = null_obj.create_axis('animated_axis')
120 axis.origin_point = (0, -0.35, 0)
121 axis.end_point = (0, 0.35, 0)
122 box = find_surface(db, ["Box","Cube"])
123 axis.add_surface(box)
124
125
126def timeline_example(db: Database):
127 prepare_product(db)
128 prepare_kinematics(db)
129
130 timeline = db.create_timeline('Timeline Example')
131 timeline.range_end = 10
132
133 start_time = 0
134 # create a curve channel animation and insert as a clip to the timeline in the empty track
135 advanced_channel_anim = create_curve_channel_animation(db)
136 timeline.empty_channels_track().insert_animation(start_time, advanced_channel_anim)
137
138 start_time += int(advanced_channel_anim.duration * 1000)
139 # create a simple channel animation and insert as a clip to the timeline
140 simple_channel_anim = create_simple_channel_animation(db)
141 timeline.empty_channels_track().insert_animation(start_time, simple_channel_anim)
142
143 start_time += int(simple_channel_anim.duration * 1000)
144 # create BezierPath Camera Animation and insert as a clip to the timeline at 0ms
145 bp_camera_anim = create_bezier_path_camera_anim(db)
146 timeline.camera_animation_track().insert_animation(start_time, bp_camera_anim)
147
148 start_time += int(bp_camera_anim.duration * 1000)
149 # create Camera Bookmark Animation and insert as a clip to the timeline after the previous animation
150 # N.B. the duration of the animation is in seconds and the insert clip method takes time in ms
151 bookmark_camera_anim = create_bookmark_camera_anim(db)
152 timeline.camera_animation_track().insert_animation(start_time, bookmark_camera_anim)
153
154 # create a product and insert a product key animation to the timeline
155 timeline.product_animation_track().insert_animation(0, db.list_product_key_animations()[0])
156
157 clean_empty_timelines(db)
158
159
160if __name__ == "__main__":
161 use_current_session = True
162 if use_current_session:
163 with CurrentP3DSession(fallback_port=33900) as p3d:
164 timeline_example(p3d.data)
165 else:
166 with P3DFile.create() as database:
167 timeline_example(database)
168 test_file_name = 'database_' + path.splitext(path.basename(__file__))[0] + '.p3d'
169 database.save_as(path.join(path.dirname(path.abspath(__file__)), test_file_name), True)
ChannelsCurveAnimation
A "ChannelsCurveAnimation"
is created to animate the translation of the newly created kinematic object "Null"
.
First, the animation is created using create_channels_curve_animation()
and the total duration of the animation is set to 3 seconds using the duration
attribute.
Then, the channels to animate the translation along the X axis is added to the previously created animation using add_null_channel_animation()
with the channel set as 'Translation/X'. Then the translated X position is defined for each keyframe using insert_key_frame()
.
Finally, the translation along the Y axis is created and defined similarly using add_null_channel_animation()
with the channel set as 'Translation/Y' and the translated Y position for each keyframe.
In this example, the null will translate in the following way : (X=0.3, Y=0.3) → (X=0.3, Y=-0.3) → (X=0.0, Y=0.0).
ChannelsSimpleAnimation
A "ChannelsSimpleAnimation"
is created to animate the rotation of the newly created kinematic object "Axis"
.
First, the animation is created using create_channels_simple_animation()
.
Then, the animation channel setting the start/end angle and duration for axis rotation is added to the previously created animation
using set_axis_channel_animation()
.
In this example, the axis will rotate from 0° to 360° during 2 seconds.
CameraBezierPathAnimation
A "CameraBezierPathAnimation"
is created to animate the "Camera"
position and target.
First, the animation is created using create_camera_bezier_path_animation()
and the total duration of the animation is set to 3 seconds using the duration
attribute.
Then, in order to make the camera's position move along a "BezierPath"
, the position_type
attribute is set to
"CameraBezierPathAnimationPositionType.FOLLOW_BEZIER_PATH"
, the py:attr:bezier_path_used_for_position
attribute
is set to the desired "BezierPath"
and the the py:attr:position_bezier_path_inverted
attribute is set to True
to make the camera move the inverted curve.
Finally, to set the camera's target to the "Null"
object, he target_type
attribute is set to
"CameraBezierPathAnimationTargetType.FOLLOW_NULL"
, the py:attr:target_null
attribute
is set to the desired "Null"
.
CameraBookmarkAnimation
A "CameraBookmarkAnimation"
is created to animate the "Camera"
position.
First, the animation is created using create_camera_bookmark_animation()
,
here the total duration of the animation is set implicitely by computing the total sum of each "CameraBookmark"
's duration.
In this example, the list of "CameraBookmark"
is defined by creating a "CameraGroup"
containing as much as "Camera"
as the animation need bookmarks.
Each camera's position will be used to define each bookmark's position.
Then, by using add_camera_from_camera_group_to_animation()
with the previously created "CameraGroup"
,
an animation containing as many bookmarks as the cameras in the group is created.
Finally, a for loop is used to define each bookmarj's duration, thus defining the total duration of the whole animation.
Insert to timeline
To insert the created animation into the timeline, the corresponding "TimelineTrack"
needs to be found :
"ChannelsCurveAnimation"
and "ChannelsSimpleAnimation"
will be inserted in empty_channels_track()
,
"CameraBezierPathAnimation"
and "CameraBookmarkAnimation"
will be inserted in camera_animation_track()
,
"ConfigurationKeyAnimation"
will be inserted in configurationAnimationTrack()
,
"TextureAnimation"
will be inserted in textureAnimationTracks()
,
"ProductKeyAnimation"
will be inserted in list_product_key_animations()
.
Then the animation is inserted to the "TimelineTrack"
as a clip by using insert_animation()
with the starting time in milliseconds.
In the example a "Product"
is inserted in py:meth:product_animation_track()
to show how it can be done simply :
the animation does not need to be created as list_product_key_animations()
contains every existing product.
Clean empty Timelines
At the end of the script, timelines are looped through to check for empty tracks : is a timeline does not have a track with animation inserted, it is deleted.