Skip to content

Commit 9fd74ad

Browse files
committed
Merge branch 'dev/nkendallbar/logic-properties-panel' into 'main'
REMIX-4815: Logic properties panel Improvements See merge request lightspeedrtx/lightspeed-kit!1067
2 parents 009d217 + 66a250f commit 9fd74ad

File tree

15 files changed

+276
-58
lines changed

15 files changed

+276
-58
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6868
- REMIX-4766: Added input validation to ensure a valid graph prim name is provided for new graphs
6969
- REMIX-4804: Fixed Remix Logic Graph Creation Logic Inconstancy Across Editor, Property Panel, and Right Click Menus
7070
- Cleanup Github runners before packaging Toolkit
71+
- REMIX-4815: Fixed default values, tooltips, and flexible type handling in the Remix Logic properties panel
7172

7273
### Removed
7374
- Removed Waypoint support based on deprecated kit sample waypoint extension

source/extensions/lightspeed.trex.logic.core/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
# Semantic Versioning is used: https://semver.org/
3-
version = "1.0.0"
3+
version = "1.1.0"
44

55
# Lists people or organizations that are considered the "authors" of the package.
66
authors = ["Nicolas Kendall-Bar <[email protected]>"]

source/extensions/lightspeed.trex.logic.core/docs/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Changelog
22
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
33

4+
## [1.1.0]
5+
### Added
6+
- Added `get_ogn_default_value()` utility for retrieving OGN attribute defaults as USD types
7+
8+
### Fixed
9+
- Fixed duplicate graph entries when multiple paths contain the same graph
10+
411
## [1.0.0]
512
### Added
613
- Created extension for managing logic graphs

source/extensions/lightspeed.trex.logic.core/lightspeed/trex/logic/core/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
"""
1717

18+
from .attributes import get_ogn_default_value
1819
from .graphs import LogicGraphCore
1920

20-
__all__ = ["LogicGraphCore"]
21+
__all__ = ["LogicGraphCore", "get_ogn_default_value"]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
"""
17+
18+
import json
19+
from typing import Any
20+
21+
import omni.graph.core as og
22+
import omni.graph.tools.ogn as ogn
23+
24+
25+
def _get_type_default(og_type: og.AttributeType) -> Any:
26+
"""Get the default value for an OGN type when no explicit default is defined."""
27+
# Arrays default to empty list (except string/path which are uchar[])
28+
if og_type.array_depth > 0:
29+
if og_type.base_type == og.BaseDataType.UCHAR and og_type.role in (
30+
og.AttributeRole.TEXT,
31+
og.AttributeRole.PATH,
32+
):
33+
return ""
34+
return []
35+
36+
# Determine base value for the type
37+
if og_type.base_type == og.BaseDataType.BOOL:
38+
val_base = False
39+
elif og_type.base_type == og.BaseDataType.TOKEN:
40+
val_base = ""
41+
else:
42+
val_base = 0
43+
44+
# Vectors/matrices: expand base value
45+
if og_type.tuple_count > 1:
46+
if og_type.role in (
47+
og.AttributeRole.FRAME,
48+
og.AttributeRole.MATRIX,
49+
og.AttributeRole.TRANSFORM,
50+
):
51+
# Identity matrix
52+
dim = 2 if og_type.tuple_count == 4 else 3 if og_type.tuple_count == 9 else 4
53+
return [[1 if i == j else 0 for j in range(dim)] for i in range(dim)]
54+
return [val_base] * og_type.tuple_count
55+
56+
return val_base
57+
58+
59+
def get_ogn_default_value(attr: og.Attribute) -> Any:
60+
"""Get an OGN attribute's default value as a USD-compatible Python type.
61+
62+
Uses og.python_value_as_usd() for proper type conversion (Gf.Vec3f, etc.)
63+
"""
64+
og_type = attr.get_resolved_type()
65+
default_str = attr.get_metadata(ogn.MetadataKeys.DEFAULT)
66+
67+
if default_str is not None:
68+
try:
69+
py_val = json.loads(default_str)
70+
return og.python_value_as_usd(og_type, py_val)
71+
except (json.JSONDecodeError, TypeError, ValueError):
72+
return default_str
73+
74+
# No explicit default - use type's default value
75+
py_val = _get_type_default(og_type)
76+
return og.python_value_as_usd(og_type, py_val)

source/extensions/lightspeed.trex.logic.core/lightspeed/trex/logic/core/graphs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ def create_graph_at_path(
8181
@staticmethod
8282
def get_existing_logic_graphs(stage: Usd.Stage, paths: list[Sdf.Path]) -> list[Usd.Prim]:
8383
"""Get the names of the existing logic graphs under provided paths"""
84-
existing_graphs: list[Usd.Prim] = []
84+
existing_graphs: set[Usd.Prim] = set()
8585
for path in paths:
8686
root_prim = stage.GetPrimAtPath(path)
8787
for prim in Usd.PrimRange(root_prim):
8888
if prim.GetTypeName() == OMNI_GRAPH_TYPE:
89-
existing_graphs.append(prim)
89+
existing_graphs.add(prim)
9090
return sorted(existing_graphs, key=lambda x: x.GetPath())

source/extensions/lightspeed.trex.properties_pane.logic.widget/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
version = "1.2.5"
2+
version = "1.3.0"
33
authors = ["Nicolas Kendall-Bar <[email protected]>"]
44
title = "Remix Logic Properties Widget"
55
description = "Properties panel for RemixLogic prim types"

source/extensions/lightspeed.trex.properties_pane.logic.widget/docs/CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
# Changelog
22
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
33

4+
## [1.3.0]
5+
### Added
6+
- Added better support for flexible OGN types (union/any) with read-only display and guidance text
7+
8+
### Changed
9+
- Improved OGN attribute default value handling with proper USD type conversion
10+
- Disabled override deletion for OGN node attributes to prevent type removal
11+
12+
### Fixed
13+
- Fixed panel rebuild targeting correct dynamic content frame
14+
415
## [1.2.5]
516
### Changed
617
- Replaced text buttons with icon buttons for edit and delete actions
@@ -44,4 +55,3 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4455
## [1.0.0]
4556
### Added
4657
- Created
47-

source/extensions/lightspeed.trex.properties_pane.logic.widget/lightspeed/trex/properties_pane/logic/widget/setup_ui.py

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import omni.usd
3131
from lightspeed.common.constants import OMNI_GRAPH_NODE_TYPE, REGEX_MESH_TO_INSTANCE_SUB, GlobalEventNames
3232
from lightspeed.events_manager import get_instance as _get_event_manager_instance
33+
from lightspeed.trex.logic.core.attributes import get_ogn_default_value
3334
from lightspeed.trex.logic.core.graphs import LogicGraphCore
3435
from omni.flux.info_icon.widget import InfoIconWidget
3536
from omni.flux.property_widget_builder.delegates.string_value.file_picker import FilePicker
@@ -329,7 +330,7 @@ def _build_dynamic_content(self) -> None:
329330
ui.Label("Existing Graphs", name="PropertiesWidgetLabel", height=0)
330331
ui.Spacer(height=ui.Pixel(_SPACING_SM))
331332

332-
for graph in set(existing_graphs):
333+
for graph in existing_graphs:
333334
with ui.HStack(height=ui.Pixel(_ROW_HEIGHT), spacing=ui.Pixel(_SPACING_SM)):
334335
relative_path = self._get_relative_path(graph.GetPath(), self._valid_target_paths)
335336
ui.Label(
@@ -526,19 +527,19 @@ async def _deferred_refresh(
526527
if not tooltip:
527528
tooltip = attr.get_metadata(ogn.MetadataKeys.DESCRIPTION) or ""
528529

529-
default_value: Any = attr.get_metadata(ogn.MetadataKeys.DEFAULT)
530+
default_value: Any = get_ogn_default_value(attr)
530531

531532
options: list[str] | None = None
532533
allowed_tokens = attr.get_metadata(ogn.MetadataKeys.ALLOWED_TOKENS)
533534
if allowed_tokens:
534535
options = allowed_tokens.split(",")
535536

537+
is_input = attr_name.startswith(OGN_ATTR_PREFIX_INPUTS)
536538
read_only = True
537-
if attr_name.startswith(OGN_ATTR_PREFIX_INPUTS):
539+
if is_input:
538540
read_only = False
539541

540542
is_relationship = is_property_relationship(stage, attribute_paths[0])
541-
542543
if is_relationship:
543544
# Build prim picker configuration (filters, path patterns, pagination, etc.)
544545
node_path = str(prim.GetPath())
@@ -552,42 +553,63 @@ async def _deferred_refresh(
552553
read_only=read_only,
553554
ui_metadata=ui_metadata,
554555
)
555-
elif options:
556-
value_type_name = None
557-
ogn_type: og.AttributeType = attr.get_attribute_data().get_type()
558-
value_type_name_str: str = og.AttributeType.sdf_type_name_from_type(ogn_type)
559-
if value_type_name_str:
560-
value_type_name = Sdf.ValueTypeNames.Find(value_type_name_str)
561-
if not value_type_name:
562-
value_type_name = Sdf.ValueTypeNames.String
563-
564-
attr_item = USDAttrListItem(
565-
self._context_name,
566-
attribute_paths,
567-
default_value,
568-
options,
569-
read_only=read_only,
570-
value_type_name=value_type_name,
571-
display_attr_names=[display_name],
572-
display_attr_names_tooltip=[tooltip],
573-
)
574556
else:
575557
value_type_name = None
576-
ogn_type: og.AttributeType = attr.get_attribute_data().get_type()
558+
ogn_type: og.AttributeType = attr.get_resolved_type()
559+
if ogn_type.base_type == og.BaseDataType.UNKNOWN:
560+
# Fall back to attribute data type for unresolved flexible types
561+
ogn_type = attr.get_attribute_data().get_type()
577562
value_type_name_str: str = og.AttributeType.sdf_type_name_from_type(ogn_type)
578563
if value_type_name_str:
579564
value_type_name = Sdf.ValueTypeNames.Find(value_type_name_str)
580565
if not value_type_name:
581566
value_type_name = Sdf.ValueTypeNames.String
567+
default_value = str(default_value)
582568

583-
attr_item = USDAttributeItem(
584-
self._context_name,
585-
attribute_paths,
586-
read_only=read_only,
587-
value_type_name=value_type_name,
588-
display_attr_names=[display_name],
589-
display_attr_names_tooltip=[tooltip],
569+
extended_type = attr.get_extended_type()
570+
is_flexible_type = extended_type in (
571+
og.ExtendedAttributeType.EXTENDED_ATTR_TYPE_UNION,
572+
og.ExtendedAttributeType.EXTENDED_ATTR_TYPE_ANY,
590573
)
574+
if is_flexible_type:
575+
read_only = True
576+
if is_input:
577+
tooltip += "(Flexible Input - Set value and resolve type by connecting to a node's output)"
578+
else:
579+
tooltip += "(Flexible Output Type - Determined by connected input types)"
580+
if ogn_type.base_type == og.BaseDataType.UNKNOWN:
581+
if is_input:
582+
default_value = "Connect to a node output."
583+
else:
584+
default_value = "Unresolved: Connect flexible inputs"
585+
else:
586+
# If type is resolved, get the value from the connected port
587+
upstream_connections = attr.get_upstream_connections()
588+
if upstream_connections:
589+
default_value = upstream_connections[0].get()
590+
else:
591+
default_value = None
592+
if options:
593+
attr_item = USDAttrListItem(
594+
self._context_name,
595+
attribute_paths,
596+
default_value,
597+
options,
598+
read_only=read_only,
599+
value_type_name=value_type_name,
600+
display_attr_names=[display_name],
601+
display_attr_names_tooltip=[tooltip],
602+
)
603+
else:
604+
attr_item = USDAttributeItem(
605+
self._context_name,
606+
attribute_paths,
607+
default_value=default_value,
608+
read_only=read_only,
609+
value_type_name=value_type_name,
610+
display_attr_names=[display_name],
611+
display_attr_names_tooltip=[tooltip],
612+
)
591613

592614
# Collect items by group (but don't add to items list yet)
593615
if group_name not in group_items:
@@ -671,8 +693,8 @@ def _delete_logic_graph(self, graph: Usd.Prim, x, y, b, m) -> None:
671693
return
672694
omni.kit.commands.execute("DeletePrimsCommand", paths=[str(graph.GetPath())])
673695
# Rebuild the UI to reflect the changes in the existing logic graphs
674-
if self._root_frame:
675-
self._root_frame.rebuild()
696+
if self._dynamic_content_frame:
697+
self._dynamic_content_frame.rebuild()
676698

677699
@property
678700
def property_model(self):

source/extensions/omni.flux.property_widget_builder.model.usd/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
# Semantic Versionning is used: https://semver.org/
3-
version = "2.21.2"
3+
version = "2.22.0"
44

55
# Lists people or organizations that are considered the "authors" of the package.
66
authors = ["Damien Bataille <[email protected]>"]

0 commit comments

Comments
 (0)