## Copyright (c) 2021, NVIDIA CORPORATION.  All rights reserved.
##
## NVIDIA CORPORATION and its licensors retain all intellectual property
## and proprietary rights in and to this software, related documentation
## and any modifications thereto.  Any use, reproduction, disclosure or
## distribution of this software and related documentation without an express
## license agreement from NVIDIA CORPORATION is strictly prohibited.
##
import omni.kit.test
import omni.timeline
from .test_hydra_common import RtxHydraTest, RtxHydraMaterialsTest, RtxHydraInstancerTest
from .test_common import wait_for_update, postLoadTestSettings
from pxr import Sdf, Vt, UsdGeom, UsdShade

simplePts = [(0, -100, 0), (50, -50, 0), (-50, 50, 0), (0, 100, 0)]
simpleCnt = len(simplePts)
linearPts = [(0, -100, 0), (50, 0, 0), (0, 100, 0)]
linearCnt = len(linearPts)
bezierPts = [(0, -100, 0), (25, -25, 0), (50, 0, 0), (0, 25, 0), (-50, 50, 0), (-25, 75, 0), (0, 100, 0)]
bezierCnt = len(bezierPts)
bsplinePts = [(0, -200, 0), (25, -25, 0), (-25, 0, 0), (50, 25, 0), (0, 200, 0)]
bsplineCnt = len(bsplinePts)
catromPts = [(0, -200, 0), (25, -50, 0), (-25, -15, 0), (-25, 15, 0), (50, 50, 0), (0, 200, 0)]
catromCnt = len(catromPts)
periodicPts = [(-200, -200, 0), (200, -200, 0), (200, 200, 0), (-200, 200, 0)]
periodicCnt = len(periodicPts)
bezierPeriodicPts = [(0, -200, 0), (-100, -200, 0), (-200, -100, 0), (-200, 0, 0), (-200, 100, 0), (-100, 200, 0), (0, 200, 0), (100, 200, 0), (200, 100, 0)]
bezierPeriodicCnt = len(bezierPeriodicPts)


class TestRtxHydraBasisCurves(RtxHydraTest):
    TEST_PATH = "hydra/basisCurves"
    PRIM_PATH = "/World/curve"

    async def setUp(self):
        await super().setUp()
        timeline = omni.timeline.get_timeline_interface()
        self.set_settings({"/rtx/hydra/curves/enabled": True,
                           "/rtx/hydra/curves/splits": 1})

    def _create_geometry(self, cnt, v, widthInterpolation="constant", width=None, basis="bezier", type="cubic", wrap="nonperiodic", name=PRIM_PATH):
        curve = UsdGeom.BasisCurves.Define(self.ctx.get_stage(), name)
        curve.CreateCurveVertexCountsAttr(cnt)
        curve.CreatePointsAttr(v)
        curve.SetWidthsInterpolation(widthInterpolation)
        curve.CreateWidthsAttr(width if width else [20])
        curve.CreateBasisAttr(basis)
        curve.CreateTypeAttr(type)
        curve.CreateWrapAttr(wrap)
        return curve

    async def create_geometry_and_test(self, golden, *args, **kwargs):
        self._create_geometry(*args, **kwargs)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, golden)
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)

    async def create_geometry_and_test_primvar(self, golden, pvName, pvType, pvInterp, pvData, *args, **kwargs):
        self._create_geometry(*args, **kwargs)
        prim = self.ctx.get_stage().GetPrimAtPath(self.PRIM_PATH)
        attr = UsdGeom.PrimvarsAPI(prim).CreatePrimvar(pvName, pvType, pvInterp)
        attr.Set(pvData)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, golden)
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)

    def create_geometry(self, name=PRIM_PATH):
        curve = self._create_geometry([simpleCnt], simplePts, name=name)
        return curve

    async def test_visibility(self):
        """
        Test hydra basis curves - visibility
        """
        await self.visibility()

    async def test_doNotCastShadow(self):
        """
        Test hydra basis curves - doNotCastShadow toggle
        """
        self.create_geometry_with_floor()
        await self.toggle_primvar(self.PRIM_PATH, "doNotCastShadows")

    async def test_matteObject(self):
        """
        Test hydra basis curves - isMatteObject toggle
        """
        self.create_geometry_with_floor()
        await self.matte()

    async def test_hideForCamera(self):
        """
        Test hydra basis curves - hideForCamera toggle
        """
        self.create_geometry_with_floor()
        await self.toggle_primvar(self.PRIM_PATH, "hideForCamera")

    async def test_transform(self):
        """
        Test hydra basis curves - tranform
        """
        await self.transform_all()

    async def test_display_color(self):
        """
        Test hydra basis curves - change display color
        """
        # Constant
        geom = self.create_geometry()
        geom.CreateDisplayColorPrimvar("constant").Set([(0, 1, 0)])
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "displayColorConstant.png")
        return geom

    async def test_widths(self):
        """
        Test hydra basis curves - change width
        """
        await self.create_geometry_and_test("bezier.png", [bezierCnt], bezierPts, "constant", [20, 19])
        await self.create_geometry_and_test("widthDefault.png", [bezierCnt], bezierPts, "constant", [])

        await self.create_geometry_and_test("widthLinear.png", [linearCnt], linearPts, "vertex", [0, 30, 0], type="linear")
        await self.create_geometry_and_test("widthVertex.png", [bezierCnt], bezierPts, "vertex", [0, 10, 20, 30, 20, 10, 0])
        await self.create_geometry_and_test("widthBsplineVertex.png", [bsplineCnt], bsplinePts, "vertex", [0, 10, 30, 10, 0], "bspline")

        await self.create_geometry_and_test("widthLinear.png", [linearCnt], linearPts, "varying", [0, 30, 0], type="linear")
        await self.create_geometry_and_test("widthVarying.png", [bezierCnt], bezierPts, "varying", [10, 30, 50])
        await self.create_geometry_and_test("widthBsplineVarying.png", [bsplineCnt], bsplinePts, "varying", [10, 30, 10], "bspline")

        pts2 = bezierPts + [(p[0], p[1], 50) for p in bezierPts]
        await self.create_geometry_and_test("widthConstant.png", [bezierCnt, bezierCnt], pts2, "constant", [30])
        await self.create_geometry_and_test("widthUniform.png", [bezierCnt, bezierCnt], pts2, "uniform", [10, 30])

        lPts2 = linearPts + [(p[0], p[1], 50) for p in linearPts]
        await self.create_geometry_and_test("widthLinear2.png", [linearCnt, linearCnt], lPts2, "varying", [10, 30, 50, 5, 35, 5], type="linear")
        await self.create_geometry_and_test("widthVarying2.png", [bezierCnt, bezierCnt], pts2, "varying", [10, 30, 50, 5, 35, 5])
        bsPts2 = bsplinePts + [(p[0], p[1], 50) for p in bsplinePts]
        await self.create_geometry_and_test("widthBsplineVarying2.png", [bsplineCnt, bsplineCnt], bsPts2, "varying", [10, 30, 50, 5, 35, 5], "bspline")

    async def test_points(self):
        """
        Test hydra basis curves - update points
        """
        pts = [simplePts, simplePts.copy()]
        pts[1][1] = (-50, -50, 0)
        pts[1][2] = (50, 50, 0)
        await self.attribute_test_case(self.create_geometry, "points", Sdf.ValueTypeNames.Point3f, pts, ["geom.png", "points.png"])
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)

        # TODO fix curves for delayed points assignments
        # curve = UsdGeom.BasisCurves.Define(self.ctx.get_stage(), self.PRIM_PATH)
        # curve.CreateWidthsAttr([20])
        # await wait_for_update()
        # await self.capture_and_compare(self.TEST_PATH, "empty.png")

        # curve.CreateCurveVertexCountsAttr([simpleCnt])
        # curve.CreatePointsAttr(simplePts)
        # await wait_for_update()
        # await self.capture_and_compare(self.TEST_PATH, "empty.png")

    async def test_skip_processing(self):
        """
        Test hydra basis curves - skipProcessing attribute
        """
        geom = self.create_geometry()
        geom.GetPrim().CreateAttribute("omni:rtx:skip", Sdf.ValueTypeNames.Bool).Set(True)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "empty.png")


    async def test_topology_wrap(self):
        """
        Test hydra basis curves - wrap
        """
        # bspline:
        await self.create_geometry_and_test("bsplinePinned.png", [bsplineCnt], bsplinePts, "constant", [20], "bspline", "cubic", "pinned")
        await self.create_geometry_and_test("bsplinePinned2.png", [bsplineCnt, bsplineCnt],
            bsplinePts + [(p[0], p[1], 50) for p in bsplinePts], "constant", [20], "bspline", "cubic", "pinned")
        # catmullRom
        await self.create_geometry_and_test("catmullRomPinned.png", [catromCnt], catromPts, "constant", [20], "catmullRom", "cubic", "pinned")
        await self.create_geometry_and_test("catmullRomPinned2.png", [catromCnt, catromCnt],
            catromPts + [(p[0], p[1], 50) for p in catromPts], "constant", [20], "catmullRom", "cubic", "pinned")

        # bspline:
        await self.create_geometry_and_test("bsplinePeriodic.png", [periodicCnt], periodicPts, "constant", [20], "bspline", "cubic", "periodic")
        await self.create_geometry_and_test("bsplinePeriodic2.png", [periodicCnt, periodicCnt],
            periodicPts + [(p[0], p[1], 50) for p in periodicPts], "constant", [20], "bspline", "cubic", "periodic")
        # catmullRom
        await self.create_geometry_and_test("catmullRomPeriodic.png", [periodicCnt], periodicPts, "constant", [20], "catmullRom", "cubic", "periodic")
        await self.create_geometry_and_test("catmullRomPeriodic2.png", [periodicCnt, periodicCnt],
            periodicPts + [(p[0], p[1], 50) for p in periodicPts], "constant", [20], "catmullRom", "cubic", "periodic")
        # bezier
        await self.create_geometry_and_test("bezierPeriodic.png", [bezierPeriodicCnt], bezierPeriodicPts, "constant", [20], "bezier", "cubic", "periodic")

        # linear
        await self.create_geometry_and_test("linearPeriodic.png", [periodicCnt], periodicPts, type="linear", wrap="periodic")

    async def test_topology_basis(self):
        """
        Test hydra basis curves - topology basis
        """
        # linear:  2 + (n - 1)
        await self.create_geometry_and_test("linear.png", [linearCnt], linearPts, type="linear")
        await self.create_geometry_and_test("linear2.png", [linearCnt, linearCnt], linearPts + [(p[0], p[1], 50) for p in linearPts], type="linear", wrap="nonperiodic")

        # bezier: 4 + 3 * (n - 1) pts
        await self.create_geometry_and_test("bezier.png", [bezierCnt], bezierPts, "constant", [20], "bezier", "cubic", "nonperiodic")

        # # more points than needed, but this is ok
        await self.create_geometry_and_test("bezier.png", [bezierCnt], bezierPts + [(0, 0, 0)])

        # # indices - ibychkov: is this supported? How it can be set?

        # bspline: 4 + (n - 1) pts
        await self.create_geometry_and_test("bspline.png", [bsplineCnt], bsplinePts, "constant", [20], "bspline", "cubic", "nonperiodic")

        # catmullRom
        await self.create_geometry_and_test("catmullRom.png", [catromCnt], catromPts, "constant", [20], "catmullRom", "cubic", "nonperiodic")

        # Ribbons - Not supported - OM-36882

    async def test_topology_incorrect(self):
        """
        Test hydra basis curves - incorrect topology
        """
        efn = "empty.png"

        # test incorrect number of points
        # linear
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0)], type="linear")
        await self.create_geometry_and_test(efn, [2], [(0, -100, 0), (-100, 0, 0)], type="linear", wrap="periodic")

        # bezier
        await self.create_geometry_and_test(efn, [0], [])
        await self.create_geometry_and_test(efn, [4], [])
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0), (50, -50, 0), (0, 0, 0), (-50, 50, 0), (0, 100, 0)])
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0)])
        await self.create_geometry_and_test(efn, [2], [(0, -100, 0), (50, -50, 0)])
        await self.create_geometry_and_test(efn, [3], [(0, -100, 0), (50, -50, 0), (0, 0, 0)])
        await self.create_geometry_and_test(efn, [4], [(0, -100, 0)])
        await self.create_geometry_and_test(efn, [5], [(0, -100, 0), (50, -50, 0), (0, 0, 0), (-50, 50, 0), (0, 100, 0), (50, 150, 0)])
        await self.create_geometry_and_test(efn, [7], [(0, -100, 0), (25, -25, 0), (50, 0, 0), (0, 25, 0), (-50, 50, 0), (-25, 75, 0), (0, 100, 0)], "constant", [20], "bezier", "cubic", "periodic")
        await self.create_geometry_and_test(efn, [7, 7], [(0, -100, 0), (25, -25, 0), (50, 0, 0), (0, 25, 0), (-50, 50, 0), (-25, 75, 0), (0, 100, 0)])

        # bspline
        await self.create_geometry_and_test(efn, [3], [(0, -100, 0), (50, -50, 0), (0, 0, 0)], basis="bspline")
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0)], "constant", [20], "bspline", "cubic", "periodic")
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0)], "constant", [20], "bspline", "cubic", "pinned")

        # catmullRom
        await self.create_geometry_and_test(efn, [3], [(0, -100, 0), (50, -50, 0), (0, 0, 0)], basis="catmullRom")
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0)], "constant", [20], "catmullRom", "cubic", "periodic")
        await self.create_geometry_and_test(efn, [1], [(0, -100, 0)], "constant", [20], "catmullRom", "cubic", "pinned")

        # pinned wrap, bezier
        await self.create_geometry_and_test("empty.png", [bezierCnt], bezierPts, "constant", [20], "bezier", "cubic", "pinned")

        # test incorrect number of widths
        pts2 = bezierPts + [(p[0], p[1], 50) for p in bezierPts]
        await self.create_geometry_and_test(efn, [bezierCnt], bezierPts, "vertex", [0, 10, 20, 30, 20, 10])
        await self.create_geometry_and_test(efn, [bezierCnt], bezierPts, "varying", [20])
        await self.create_geometry_and_test(efn, [bezierCnt, bezierCnt], pts2, "uniform", [20])

        # test incorrect number of display color

    async def test_normal_and_tangents(self):
        """
        Test hydra basis curves - normal and tangents
        """
        self.set_settings({"/rtx/debugView/target": "normal"})
        await self.create_geometry_and_test("normalLinear.png", [linearCnt], linearPts, "constant", [40], type="linear")
        await self.create_geometry_and_test("normal.png", [bezierCnt], bezierPts, "constant", [40])
        await self.create_geometry_and_test("normalBspline.png", [bsplineCnt], bsplinePts, "constant", [40], "bspline")
        await self.create_geometry_and_test("normalCatrom.png", [catromCnt], catromPts, "constant", [40], "catmullRom")
        self.set_settings({"/rtx/debugView/target": "tangentu"})
        await self.create_geometry_and_test("tangentuLinear.png", [linearCnt], linearPts, "constant", [40], type="linear")
        await self.create_geometry_and_test("tangentu.png", [bezierCnt], bezierPts, "constant", [40])
        await self.create_geometry_and_test("tangentuBspline.png", [bsplineCnt], bsplinePts, "constant", [40], "bspline")
        await self.create_geometry_and_test("tangentuCatrom.png", [catromCnt], catromPts, "constant", [40], "catmullRom")
        self.set_settings({"/rtx/debugView/target": "tangentv"})
        await self.create_geometry_and_test("tangentvLinear.png", [linearCnt], linearPts, "constant", [40], type="linear")
        await self.create_geometry_and_test("tangentv.png", [bezierCnt], bezierPts, "constant", [40])
        await self.create_geometry_and_test("tangentvBspline.png", [bsplineCnt], bsplinePts, "constant", [40], "bspline")
        await self.create_geometry_and_test("tangentvCatrom.png", [catromCnt], catromPts, "constant", [40], "catmullRom")

    async def test_texCoords(self):
        """
        Test hydra basis curves - texCoords (UVs)
        """
        self.set_settings({"/rtx/debugView/target": "texcoord0"})
        await self.create_geometry_and_test("texCoordsLinear.png", [linearCnt], linearPts, "vertex", [10, 30, 50], type="linear")
        await self.create_geometry_and_test("texCoords.png", [bezierCnt], bezierPts, "varying", [10, 30, 50])
        await self.create_geometry_and_test("texCoordsBspline.png", [bsplineCnt], bsplinePts, "constant", [40], "bspline")
        await self.create_geometry_and_test("texCoordsCatrom.png", [catromCnt], catromPts, "constant", [40], "catmullRom")

    async def test_texCoords2(self):
        """
        Test hydra basis curves - texCoords (UVs) set 2. Defined by asset.
        """
        self.set_settings({"/rtx/debugView/target": "texcoord1"})
        await self.create_geometry_and_test_primvar("texCoords2Vertex.png",
            "uv1", Sdf.ValueTypeNames.Float2Array, UsdGeom.Tokens.vertex,
            [(0, 0), (0.15, 0), (0.3, 0), (0.5, 0), (0.7, 0), (0.85, 0), (1, 0)],
            [bezierCnt], bezierPts, "varying", [10, 30, 50])
        await self.create_geometry_and_test_primvar("texCoords2Uniform.png",
            "uv1", Sdf.ValueTypeNames.Float2Array, UsdGeom.Tokens.uniform,
            [(0, 0.5), (1, 0)],
            [bezierCnt, bezierCnt], bezierPts + [(p[0], p[1], 50) for p in bezierPts])
        # Pinned wrap
        await self.create_geometry_and_test_primvar("texCoords2VertexPinned.png",
            "uv1", Sdf.ValueTypeNames.Float2Array, UsdGeom.Tokens.vertex,
            [(0, 0), (0.3, 0), (0.5, 0), (0.7, 0), (1, 0)],
            [bsplineCnt], bsplinePts, "constant", [30], "bspline", "cubic", "pinned")
        await self.create_geometry_and_test_primvar("texCoords2UniformPinned.png",
            "uv1", Sdf.ValueTypeNames.Float2Array, UsdGeom.Tokens.uniform,
            [(0, 0.5), (1, 0)],
            [bsplineCnt, bsplineCnt], bsplinePts + [(p[0], p[1], 50) for p in bsplinePts], "constant", [20], "bspline", "cubic", "pinned")
        # Peropdic wrap
        await self.create_geometry_and_test_primvar("texCoords2VertexPeriodic.png",
            "uv1", Sdf.ValueTypeNames.Float2Array, UsdGeom.Tokens.vertex,
            [(0, 0), (0.3, 0), (0.5, 0), (1, 0)],
            [periodicCnt], periodicPts, "constant", [30], "bspline", "cubic", "periodic")
        await self.create_geometry_and_test_primvar("texCoords2UniformPeriodic.png",
            "uv1", Sdf.ValueTypeNames.Float2Array, UsdGeom.Tokens.uniform,
            [(0, 0.5), (1, 0)],
            [periodicCnt, periodicCnt], periodicPts + [(p[0], p[1], 50) for p in periodicPts], "constant", [20], "bspline", "cubic", "periodic")

    async def test_endcaps(self):
        """
        Test hydra basis curves - endcaps.
        For cubic curves - flat and open
        For linear curves - only round
        """
        self._create_geometry([bezierCnt], bezierPts, "varying", [30, 30, 50])
        prim = self.ctx.get_stage().GetPrimAtPath(self.PRIM_PATH)
        pv = UsdGeom.PrimvarsAPI(prim).CreatePrimvar("endcaps", Sdf.ValueTypeNames.Int)
        # Flat
        pv.Set(1)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "endcapFlat.png")

        # Open
        pv.Set(0)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "endcapOpen.png")

        # OM-55978: Discontinuity along curves or hair fibers when the endcaps option set to open
        self.set_camera((1.25, 1.25, 1.25), (0, 0, 0))
        self.set_settings({"/rtx/debugView/target": "normal"})
        self._create_geometry([4], [(0, -10, 0), (0, 0, 0), (0, 10, 0), (0, 20, 0)], "constant", [1], "bspline", "cubic", "pinned")
        prim = self.ctx.get_stage().GetPrimAtPath(self.PRIM_PATH)
        pv = UsdGeom.PrimvarsAPI(prim).CreatePrimvar("endcaps", Sdf.ValueTypeNames.Int)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "om55978.png")

    async def test_numsplits(self):
        """
        Test hydra basis curves - numSplits.
        For cubic curves only
        """
        self.set_settings({"/rtx/wireframe/enabled": True})
        self.set_settings({"/rtx/wireframe/wireframeThickness": 5})
        self.set_settings({"/rtx/wireframe/mode": 2})

        # Global setting
        self._create_geometry([bezierCnt], bezierPts, "constant", [20])
        self.set_settings({"/rtx/hydra/curves/splits": 4})
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "bezier4Splits.png")

        # Update global setting
        self.set_settings({"/rtx/hydra/curves/splits": 1})
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "bezier1Splits.png")
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)

        # Primvar with overrided
        self._create_geometry([bezierCnt], bezierPts, "constant", [20])
        prim = self.ctx.get_stage().GetPrimAtPath(self.PRIM_PATH)
        UsdGeom.PrimvarsAPI(prim).CreatePrimvar("numSplitsOverride", Sdf.ValueTypeNames.Bool).Set(True)
        UsdGeom.PrimvarsAPI(prim).CreatePrimvar("numSplits", Sdf.ValueTypeNames.Int).Set(2)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "bezier2Splits.png")
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)

        # Primvar without override (it should get num splits from global setting == 1)
        self._create_geometry([bezierCnt], bezierPts, "constant", [20])
        prim = self.ctx.get_stage().GetPrimAtPath(self.PRIM_PATH)
        UsdGeom.PrimvarsAPI(prim).CreatePrimvar("numSplits", Sdf.ValueTypeNames.Int).Set(2)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "bezier1Splits.png")
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)

        # Bspline
        self._create_geometry([bsplineCnt], bsplinePts, "constant", [20], "bspline")
        prim = self.ctx.get_stage().GetPrimAtPath(self.PRIM_PATH)
        UsdGeom.PrimvarsAPI(prim).CreatePrimvar("numSplits", Sdf.ValueTypeNames.Int).Set(2)
        UsdGeom.PrimvarsAPI(prim).CreatePrimvar("numSplitsOverride", Sdf.ValueTypeNames.Bool).Set(True)
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "bspline2Splits.png")

    async def test_timeSampled(self):
        """
        Test hydra basis curves - verify time sample animation works.
        """
        super().open_usd("hydra/curves_timeSampled_001.usda")
        await omni.kit.app.get_app().next_update_async()
        self.set_settings(postLoadTestSettings)

        timeline = omni.timeline.get_timeline_interface()

        # Regression test for OM-96462, where time-sampled xform was impairing update of time-sampled points
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "curves_timeSampled_frame0.png")

        # Advance a few frames and capture another frame
        for t in range(12):
            timeline.forward_one_frame()
            await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "curves_timeSampled_frame12.png")


class TestRtxHydraBasisCurvesInstancer(RtxHydraInstancerTest):
    # TODO full cover scene instancer with tests
    TEST_PATH = "hydra/basisCurves/instancer"
    GEOM_PATH = RtxHydraInstancerTest.PRIM_PATH + "/curve"

    def create_geometry(self, name=GEOM_PATH):
        curve = UsdGeom.BasisCurves.Define(self.ctx.get_stage(), name)
        curve.CreateCurveVertexCountsAttr([simpleCnt])
        curve.CreatePointsAttr(simplePts)
        curve.CreateWidthsAttr([0, 20, 20, 0])
        return curve

    async def test_sceneInstancer(self):
        """
        Test hydra basis curves - scene instancer
        """
        await self.si_all()

    async def test_pointInstancer(self):
        """
        Test hydra basis curves - point instancer
        """
        await self.pi_all()


class TestRtxHydraBasisCurvesMaterials(RtxHydraMaterialsTest):
    TEST_PATH = "hydra/basisCurves/material"
    PRIM_PATH = "/World/curve"

    def create_geometry(self, name=PRIM_PATH):
        curve = UsdGeom.BasisCurves.Define(self.ctx.get_stage(), name)
        curve.CreateCurveVertexCountsAttr([simpleCnt])
        curve.CreatePointsAttr(simplePts)
        curve.CreateWidthsAttr([10, 30, 30, 10])
        return curve

    # Unstable via OM-53604
    async def test_UNSTABLE_materials_UNSTABLE(self):
        """
        Test hydra basis curves - materials
        """
        materials = ["Green", "Red"]
        looksPath = "/World/Looks/"
        # Set and change material
        geomPrim = self.create_geometry().GetPrim()
        for m in materials:
            self.bind_material(geomPrim, looksPath + m)
            await wait_for_update()
            await self.capture_and_compare(self.TEST_PATH, "mat_{}.png".format(m))

        # Unbind material it should fallback to basic material
        UsdShade.MaterialBindingAPI(geomPrim).UnbindAllBindings()
        await wait_for_update()
        await self.capture_and_compare(self.TEST_PATH, "mat_base.png")
        self.ctx.get_stage().RemovePrim(self.PRIM_PATH)
