forked from AcademySoftwareFoundation/OpenRV
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add annotation in OTIO reader for Live Review (AcademySoftwareFoundat…
…ion#612) ### Add annotation in OTIO reader for Live Review ### Summarize your change. - [X] Add the paint and point schemas used to defined an annotation drawn by a presenter - [X] Add an annotation hook to handle the display of annotations sent from an OTIO string - [X] Handle the exception where a source is not active by manually setting the aspect ratio - [X] Add a parameter to set the node to use in findAnnotatedFrames ### Describe the reason for the change. A participant to a Live Review session is now able to see annotations drawn by the presenter. ### Describe what you have tested and on which operating system. The changes were tested on MacOS arm64. --------- Signed-off-by: Éloïse Brosseau <[email protected]>
- Loading branch information
1 parent
e055db3
commit db17195
Showing
8 changed files
with
326 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,10 @@ package: OTIO Reader | |
author: Contributors to the OpenTimelineIO project | ||
organization: OpenTimelineIO project | ||
contact: [email protected] | ||
version: 1.1 | ||
version: 1.2 | ||
url: http://opentimeline.io | ||
rv: 4.0.8 | ||
openrv: 1.0.0 | ||
rv: 2024.2.0 | ||
openrv: 2.1.0 | ||
|
||
modes: | ||
- file: otio_reader_plugin.py | ||
|
@@ -20,6 +20,8 @@ files: | |
location: SupportFiles/$PACKAGE | ||
- file: annotation_schema.py | ||
location: SupportFiles/$PACKAGE | ||
- file: annotation_hook.py | ||
location: SupportFiles/$PACKAGE | ||
- file: cdlSchema.py | ||
location: SupportFiles/$PACKAGE | ||
- file: cdlHook.py | ||
|
@@ -42,6 +44,10 @@ files: | |
location: SupportFiles/$PACKAGE | ||
- file: multiRepPostExportHook.py | ||
location: SupportFiles/$PACKAGE | ||
- file: paint_schema.py | ||
location: SupportFiles/$PACKAGE | ||
- file: point_schema.py | ||
location: SupportFiles/$PACKAGE | ||
|
||
description: | | ||
<p> | ||
|
@@ -134,6 +140,20 @@ description: | | |
</p> | ||
</li> | ||
|
||
<li> | ||
<p> | ||
paint_schema.py | ||
An example schema for a paint annotation. | ||
</p> | ||
</li> | ||
|
||
<li> | ||
<p> | ||
point_schema.py | ||
An example schema for a paint annotation point. | ||
</p> | ||
</li> | ||
|
||
<li> | ||
<p> | ||
timeWarpHook.py | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# ***************************************************************************** | ||
# Copyright 2024 Autodesk, Inc. All rights reserved. | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
# ***************************************************************************** | ||
|
||
|
||
import effectHook | ||
import opentimelineio as otio | ||
from rv import commands, extra_commands | ||
|
||
|
||
def hook_function(in_timeline, argument_map=None) -> None: | ||
for layer in in_timeline.layers: | ||
if layer.name == "Paint": | ||
if type(layer.layer_range) is otio._opentime.TimeRange: | ||
range = layer.layer_range | ||
else: | ||
range = otio.opentime.TimeRange( | ||
layer.layer_range["start_time"], layer.layer_range["duration"] | ||
) | ||
|
||
relative_time = range.end_time_inclusive() | ||
frame = relative_time.to_frames() | ||
|
||
source_node = argument_map.get("source_group") | ||
paint_node = extra_commands.nodesInGroupOfType(source_node, "RVPaint")[0] | ||
paint_component = f"{paint_node}.paint" | ||
stroke_id = commands.getIntProperty(f"{paint_component}.nextId")[0] | ||
pen_component = f"{paint_node}.pen:{stroke_id}:{frame}:annotation" | ||
frame_component = f"{paint_node}.frame:{frame}" | ||
|
||
# Set properties on the paint component of the RVPaint node | ||
effectHook.set_rv_effect_props( | ||
paint_component, {"nextId": stroke_id + 1, "show": True} | ||
) | ||
|
||
# Add and set properties on the pen component of the RVPaint node | ||
effectHook.add_rv_effect_props( | ||
pen_component, | ||
{ | ||
"color": [float(x) for x in layer.rgba], | ||
"brush": layer.brush, | ||
"debug": 1, | ||
"join": 3, | ||
"cap": 2, | ||
"splat": 1, | ||
"mode": 0 if layer.type.lower() == "color" else 1, | ||
}, | ||
) | ||
|
||
if not commands.propertyExists(f"{frame_component}.order"): | ||
commands.newProperty(f"{frame_component}.order", commands.StringType, 1) | ||
|
||
commands.insertStringProperty( | ||
f"{frame_component}.order", [f"pen:{stroke_id}:{frame}:annotation"] | ||
) | ||
|
||
global_scale = argument_map.get("global_scale") | ||
points_property = f"{pen_component}.points" | ||
width_property = f"{pen_component}.width" | ||
|
||
if not commands.propertyExists(points_property): | ||
commands.newProperty(points_property, commands.FloatType, 2) | ||
if not commands.propertyExists(width_property): | ||
commands.newProperty(width_property, commands.FloatType, 1) | ||
|
||
global_width = 2 / 15 # 0.133333... | ||
for point in layer.points: | ||
commands.insertFloatProperty( | ||
points_property, | ||
[point.x * global_scale.x, point.y * global_scale.y], | ||
) | ||
commands.insertFloatProperty( | ||
width_property, [point.width * global_width] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# ***************************************************************************** | ||
# Copyright 2024 Autodesk, Inc. All rights reserved. | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
# ***************************************************************************** | ||
|
||
""" | ||
For our OTIO output to effectively interface with other programs | ||
using the OpenTimelineIO Python API, our custom schema need to be | ||
specified and registered with the API. | ||
As per OTIO documentation, a class such as this one must be created, | ||
the schema must be registered with a PluginManifest, and the path to that | ||
manifest must be added to $OTIO_PLUGIN_MANIFEST_PATH; then the schema | ||
is ready to be used. | ||
```python | ||
Example: | ||
myObject = otio.schemadef.Paint.Paint( | ||
name, points, rgba, type, brush, layer_range, hold, ghost | ||
) | ||
""" | ||
|
||
import opentimelineio as otio | ||
|
||
|
||
@otio.core.register_type | ||
class Paint(otio.core.SerializableObject): | ||
"""A schema for the start of an annotation""" | ||
|
||
_serializable_label = "Paint.1" | ||
_name = "Paint" | ||
|
||
def __init__( | ||
self, | ||
name: str = "", | ||
points: list | None = None, | ||
rgba: list | None = None, | ||
type: str = "", | ||
brush: str = "", | ||
layer_range: dict | None = None, | ||
hold: bool = False, | ||
ghost: bool = False, | ||
) -> None: | ||
super().__init__() | ||
self.name = name | ||
self.points = points | ||
self.rgba = rgba | ||
self.type = type | ||
self.brush = brush | ||
self.layer_range = layer_range | ||
self.hold = hold | ||
self.ghost = ghost | ||
|
||
name = otio.core.serializable_field( | ||
"name", required_type=str, doc=("Name: expects a string") | ||
) | ||
|
||
_points = otio.core.serializable_field( | ||
"points", required_type=list, doc=("Points: expects a list of point objects") | ||
) | ||
|
||
@property | ||
def points(self) -> list: | ||
return self._points | ||
|
||
@points.setter | ||
def points(self, val: list) -> None: | ||
self._points = val | ||
|
||
_rgba = otio.core.serializable_field( | ||
"rgba", required_type=list, doc=("RGBA: expects a list of four floats") | ||
) | ||
|
||
@property | ||
def rgba(self) -> list: | ||
return self._rgba | ||
|
||
@rgba.setter | ||
def rgba(self, val: list) -> list: | ||
self._rgba = val | ||
|
||
type = otio.core.serializable_field( | ||
"type", required_type=str, doc=("Type: expects a string") | ||
) | ||
|
||
brush = otio.core.serializable_field( | ||
"brush", required_type=str, doc=("Brush: expects a string") | ||
) | ||
|
||
_layer_range = otio.core.serializable_field( | ||
"layer_range", | ||
required_type=otio.opentime.TimeRange, | ||
doc=("Layer_range: expects a TimeRange object"), | ||
) | ||
|
||
@property | ||
def layer_range(self) -> otio.opentime.TimeRange: | ||
return self._layer_range | ||
|
||
@layer_range.setter | ||
def layer_range(self, val) -> otio.opentime.TimeRange: | ||
self._layer_range = val | ||
|
||
_hold = otio.core.serializable_field( | ||
"hold", required_type=bool, doc=("Hold: expects either true or false") | ||
) | ||
|
||
_ghost = otio.core.serializable_field( | ||
"ghost", required_type=bool, doc=("Ghost: expects either true or false") | ||
) | ||
|
||
def __str__(self) -> str: | ||
return ( | ||
f"Paint({self.name}, {self.points}, {self.rgba}, {self.type}, " | ||
f"{self.brush}, {self.layer_range}, {self.hold}, {self.ghost})" | ||
) | ||
|
||
def __repr__(self) -> str: | ||
return ( | ||
f"otio.schema.Paint(name={self.name!r}, points={self.points!r}, " | ||
f"rgba={self.rgba!r}, type={self.type!r}, brush={self.brush!r}, " | ||
f"layer_range={self.layer_range!r}, hold={self.hold!r}, " | ||
f"ghost={self.ghost!r})" | ||
) |
Oops, something went wrong.