Skip to content

Commit

Permalink
Native instance picking. (#107)
Browse files Browse the repository at this point in the history
* Native instance picking.

* Fixed gcc 11.2 Linux compilation.
  • Loading branch information
ppt-adsk authored Mar 26, 2024
1 parent 7fd0fbc commit 9490586
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 0 deletions.
20 changes: 20 additions & 0 deletions lib/mayaHydra/mayaPlugin/renderOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,26 @@ class UsdPickHandler : public MtohRenderOverride::PickHandlerBase {
return {primOrigin.GetFullPath(), -1};
}

// If there is a Hydra instancer, distinguish between native instancing
// (implicit USD prototype created by USD itself) and point instancing
// (explicitly authored USD prototypes). As per HdxInstancerContext
// documentation:
//
// [...] "exactly one of instancePrimOrigin or instancerPrimOrigin will
// contain data depending on whether the instancing at the current
// level was implicit or not, respectively."
const auto& instancerContext = primOrigin.instancerContexts.front();

if (instancerContext.instancePrimOrigin) {
// Implicit prototype instancing (i.e. USD native instancing).
auto schema = HdPrimOriginSchema(instancerContext.instancePrimOrigin);
if (!TF_VERIFY(schema, "Cannot build prim origin schema for USD native instance.")) {
return {SdfPath(), -1};
}
return {schema.GetOriginPath(HdPrimOriginSchemaTokens->scenePath), -1};
}

// Explicit prototype instancing (i.e. USD point instancing).
std::function<HitPath(const HdxPrimOriginInfo& primOrigin, const HdxPickHit& hit)> pickFn[] = {pickInstancer, pickInstance, pickPrototype};

// Retrieve pick mode from mayaUsd optionVar, to see if we're picking
Expand Down
1 change: 1 addition & 0 deletions test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ set(INTERACTIVE_TEST_SCRIPT_FILES
cpp/testPrimInstancing.py
cpp/testPicking.py
cpp/testPointInstancePicking.py
cpp/testUsdNativeInstancePicking.py
)

#Add this test only if the MayaUsd_FOUND (so also MAYAUSDAPI_LIBRARY) has been found during compile time.
Expand Down
1 change: 1 addition & 0 deletions test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ target_sources(${TARGET_NAME}
testPrimInstancing.cpp
testPicking.cpp
testPointInstancePicking.cpp
testUsdNativeInstancePicking.cpp
)

# -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2024 Autodesk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "testUtils.h"

#include <maya/M3dView.h>
#include <maya/MPoint.h>

#include <pxr/imaging/hd/selectionSchema.h>
#include <pxr/imaging/hd/selectionsSchema.h>

#include <ufe/path.h>
#include <ufe/pathString.h>
#include <ufe/observableSelection.h>
#include <ufe/globalSelection.h>

#include <gtest/gtest.h>

PXR_NAMESPACE_USING_DIRECTIVE

using namespace MayaHydra;

TEST(TestUsdNativeInstancePicking, pickInstance)
{
const auto& sceneIndices = GetTerminalSceneIndices();
ASSERT_GT(sceneIndices.size(), 0u);
auto siRoot = sceneIndices.front();

auto [argc, argv] = getTestingArgs();
ASSERT_EQ(argc, 1);
const Ufe::Path selected(Ufe::PathString::path(argv[0]));

// Maya selection API doesn't understand USD data, which can only be
// represented through UFE, so use UFE API to interact with Maya selection.
const auto sn = Ufe::GlobalSelection::get();
sn->clear();

// Translate the application path into a scene index path using the
// selection scene index.
// The Flow Viewport selection scene index is in the scene index tree.
const auto snSi = findSelectionSceneIndexInTree(siRoot);
ASSERT_TRUE(snSi);

const auto sceneIndexPath = snSi->SceneIndexPath(selected);

ASSERT_FALSE(sceneIndexPath.IsEmpty());

const auto prim = siRoot->GetPrim(sceneIndexPath);
ASSERT_TRUE(prim.dataSource);

// There is no selections data source on the prim.
auto dataSourceNames = prim.dataSource->GetNames();
ASSERT_EQ(std::find(dataSourceNames.begin(), dataSourceNames.end(), HdSelectionsSchemaTokens->selections), dataSourceNames.end());

// Selection scene index says the prim is not selected.
ASSERT_FALSE(snSi->IsFullySelected(sceneIndexPath));

//======================================================================
// Perform a pick
//======================================================================

M3dView active3dView = M3dView::active3dView();

const auto primMouseCoords = getPrimMouseCoords(prim, active3dView);

mouseClick(Qt::MouseButton::LeftButton, active3dView.widget(), primMouseCoords);
active3dView.refresh();

//======================================================================
// Test that the pick changed the Maya selection
//======================================================================

// When picking on the boundary of multiple objects, one Hydra pick hit per
// object is returned. Therefore test that the expected selected path is
// in the selection.
ASSERT_GE(sn->size(), 1u);
ASSERT_TRUE(sn->contains(selected));

//======================================================================
// Test that the pick changed the Hydra selection
//======================================================================

// On selection, the prim is given a selections data source.
dataSourceNames = prim.dataSource->GetNames();
ASSERT_NE(std::find(dataSourceNames.begin(), dataSourceNames.end(), HdSelectionsSchemaTokens->selections), dataSourceNames.end());

auto snDataSource = prim.dataSource->Get(HdSelectionsSchemaTokens->selections);
ASSERT_TRUE(snDataSource);
auto selectionsSchema = HdSelectionsSchema::GetFromParent(prim.dataSource);
ASSERT_TRUE(selectionsSchema);

// Only one selection in the selections schema.
ASSERT_EQ(selectionsSchema.GetNumElements(), 1u);
auto selectionSchema = selectionsSchema.GetElement(0);

// Prim is fully selected.
auto ds = selectionSchema.GetFullySelected();
ASSERT_TRUE(ds);
ASSERT_TRUE(ds->GetTypedValue(0.0f));

// Selection scene index says the prim is selected.
ASSERT_TRUE(snSi->IsFullySelected(sceneIndexPath));
ASSERT_TRUE(snSi->HasFullySelectedAncestorInclusive(sceneIndexPath));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2024 Autodesk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import maya.cmds as cmds
import fixturesUtils
import mtohUtils
import unittest
import usdUtils

import testUtils
from testUtils import PluginLoaded

class TestUsdNativeInstancePicking(mtohUtils.MtohTestCase):
# MayaHydraBaseTestCase.setUpClass requirement.
_file = __file__

PICK_PATH = "|instancedCubeHierarchies|instancedCubeHierarchiesShape,/cubeHierarchies"

def loadUsdScene(self):
usdScenePath = testUtils.getTestScene('testUsdNativeInstances', 'instancedCubeHierarchies.usda')
usdUtils.createStageFromFile(usdScenePath)

def setUp(self):
super(TestUsdNativeInstancePicking, self).setUp()
self.loadUsdScene()
cmds.refresh()

@unittest.skipUnless(mtohUtils.checkForMayaUsdPlugin(), "Requires Maya USD Plugin.")
def test_NativeInstances(self):
with PluginLoaded('mayaHydraCppTests'):
instances = ["/cubes_1", "/cubes_2"]
for instance in instances:
cmds.mayaHydraCppTest(
self.PICK_PATH + instance,
f="TestUsdNativeInstancePicking.pickInstance")

if __name__ == '__main__':
fixturesUtils.runTests(globals())
33 changes: 33 additions & 0 deletions test/testSamples/testUsdNativeInstances/cubesHierarchy.usda
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#usda 1.0
(
defaultPrim = "parent"
metersPerUnit = 0.01
upAxis = "Y"
)

def Xform "parent" (
kind = "component"
)
{
def Mesh "topCube"
{
uniform bool doubleSided = 1
float3[] extent = [(-0.25, -0.25, -0.25), (0.25, 0.25, 0.25)]
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]
int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4]
point3f[] points = [(-0.25, -0.25, 0.25), (0.25, -0.25, 0.25), (-0.25, 0.25, 0.25), (0.25, 0.25, 0.25), (-0.25, 0.25, -0.25), (0.25, 0.25, -0.25), (-0.25, -0.25, -0.25), (0.25, -0.25, -0.25)]
double3 xformOp:translate = (0, 1.25, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
}

def Mesh "baseCube"
{
uniform bool doubleSided = 1
float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)]
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]
int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4]
point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)]
double3 xformOp:translate = (0, 0.5, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#usda 1.0

def "cubeHierarchies"
{
def Xform "cubes_1" (
instanceable = true
references = @./cubesHierarchy.usda@
)
{
double3 xformOp:translate = (-1.0, 0, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
}

def Xform "cubes_2" (
instanceable = true
references = @./cubesHierarchy.usda@
)
{
double3 xformOp:translate = (1.0, 0, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
}
}

0 comments on commit 9490586

Please sign in to comment.