graphics.hatenablog.com

技術系テクニカルアーティストのあれこれ

USD Tutorials を C++ でやってみた。

だいたい Python だったから C++ でやってみた。
いろいろ調べながら頑張ってお勉強しようとするよりも、まずは何も考えず公式チュートリアルを読みながら写経するのがよさげ。あと C++ だと namespace とか型定義が Python と微妙に違うのと、include でファイルパス指定しなきゃいけないから、ソースコードは手許に用意して、VS Code でいいからインテリセンスと grep は必須。

graphics.pixar.com

ビルド環境

プリコンパイル済みヘッダー(stdafx.h)

#pragma once

#define NOMINMAX
#pragma warning(push)
#pragma warning(disable: 4244 4267 4305 6011 6319 26439 26451 26495 26812)
#include <pxr/usd/usd/stage.h>
#include <pxr/usd/usd/primRange.h>
#include <pxr/usd/usd/variantSets.h>
#include <pxr/usd/usd/editContext.h>
#include <pxr/usd/usd/modelApi.h>
#include <pxr/usd/sdf/types.h>
#include <pxr/usd/usdGeom/xform.h>
#include <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/sphere.h>
#include <pxr/usd/usdGeom/xformCommonAPI.h>
#include <pxr/usd/usdGeom/metrics.h>
#include <pxr/usd/usdGeom/tokens.h>
#include <pxr/usd/usdShade/material.h>
#include <pxr/usd/usdShade/materialBindingAPI.h>
#pragma warning(pop)
#undef NOMINMAX

#include <iostream>
#include <string>
#include <conio.h>

Hello World - Creating Your First USD Stage

f:id:hal1932:20200503192540p:plain

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

int main(int args, char* argv[]) {
    auto pStage = UsdStage::CreateNew("HelloWorld.usda");
    UsdGeomXform::Define(pStage, SdfPath("/hello"));
    UsdGeomSphere::Define(pStage, SdfPath("/hello/world"));
    pStage->GetRootLayer()->Save();
    return 0;
}

Inspecting and Authoring Properties

f:id:hal1932:20200503193043p:plain

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

int main(int args, char* argv[]) {
    auto pStage = UsdStage::CreateNew("HelloWorld.usda");
    UsdGeomXform::Define(pStage, SdfPath("/hello"));
    auto sphere = UsdGeomSphere::Define(pStage, SdfPath("/hello/world"));

    auto radius = sphere.GetRadiusAttr();
    radius.Set(2.0);

    auto extent = sphere.GetExtentAttr();
    extent.Set(VtArray<GfVec3f> { GfVec3f(-2.f, -2.f, -2.f), GfVec3f(2.f, 2.f, 2.f) });

    auto color = sphere.GetDisplayColorAttr();
    color.Set(VtArray<GfVec3f> { GfVec3f(0.f, 0.f, 1.f) });

    pStage->GetRootLayer()->Save();

    return 0;
}

Referencing Layers

f:id:hal1932:20200503194337p:plain

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

void PrintStage(const UsdStagePtr& pStage) {
    std::string str;
    pStage->ExportToString(&str);
    std::cout << str << std::endl;
}

int main(int args, char* argv[]) {
    auto pStage = UsdStage::Open("HelloWorld.usda");
    auto hello = pStage->GetPrimAtPath(SdfPath("/hello"));
    pStage->SetDefaultPrim(hello);
    UsdGeomXformCommonAPI(hello).SetTranslate(GfVec3f(4.f, 5.f, 6.f));
    pStage->GetRootLayer()->Save();

    auto pRefStage = UsdStage::CreateNew("RefExample.usda");
    auto refSphere = pRefStage->OverridePrim(SdfPath("/refSphere"));
    refSphere.GetReferences().AddReference("./HelloWorld.usda");
    auto refXform = UsdGeomXformable(refSphere);
    refXform.SetXformOpOrder({});

    auto refSphere2 = pRefStage->OverridePrim(SdfPath("/refSphere2"));
    refSphere2.GetReferences().AddReference("./HelloWorld.usda");

    auto overSphere = UsdGeomSphere::Get(pRefStage, SdfPath("/refSphere2/world"));
    overSphere.GetDisplayColorAttr().Set(VtArray<GfVec3f> { GfVec3f(1.f, 0.f, 0.f) });

    pRefStage->GetRootLayer()->Save();
    PrintStage(pRefStage);

    return 0;
}

Traversing a Stage

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

int main(int args, char* argv[]) {
    auto pStage = UsdStage::Open("RefExample.usda");
    for (auto prim : pStage->Traverse()) {
        std::cout << prim.GetName() << ": " << prim.GetTypeName() << std::endl;
    }
    auto iter = UsdPrimRange::PreAndPostVisit(pStage->GetPseudoRoot());
    for (auto i = iter.begin(); i != iter.end(); ++i) {
        std::cout << i->GetName() << " " << i.IsPostVisit() << std::endl;
    }
    return 0;
}

Authoring Variants

f:id:hal1932:20200503194351p:plain

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

int main(int args, char* argv[]) {
    auto pStage = UsdStage::Open("HelloWorld.usda");
    auto color = UsdGeomGprim::Get(pStage, SdfPath("/hello/world")).GetDisplayColorAttr();
    color.Clear();

    auto rootPrim = pStage->GetPrimAtPath(SdfPath("/hello"));
    auto vset = rootPrim.GetVariantSets().AddVariantSet("shadingVariant");
    vset.AddVariant("red");
    vset.AddVariant("blue");
    vset.AddVariant("green");

    vset.SetVariantSelection("red");
    {
        UsdEditContext ctxt(vset.GetVariantEditContext());
        color.Set(VtArray<GfVec3f> { GfVec3f(1.f, 0.f, 0.f) });
    }

    vset.SetVariantSelection("blue");
    {
        UsdEditContext ctxt(vset.GetVariantEditContext());
        color.Set(VtArray<GfVec3f> { GfVec3f(0.f, 0.f, 1.f) });
    }

    vset.SetVariantSelection("green");
    {
        UsdEditContext ctxt(vset.GetVariantEditContext());
        color.Set(VtArray<GfVec3f> { GfVec3f(0.f, 1.f, 0.f) });
    }

    pStage->GetRootLayer()->Export("HelloWorldWithVariants.usda");

    return 0;
}

Transformations, Time-sampled Animation, and Layer Offsets

f:id:hal1932:20200503195824p:plain

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

UsdStageRefPtr MakeInitialStage(const std::string& path) {
    auto pStage = UsdStage::CreateNew(path);
    UsdGeomSetStageUpAxis(pStage, UsdGeomTokens->z);
    pStage->SetStartTimeCode(0.0);
    pStage->SetEndTimeCode(192.0);
    return pStage;
}

UsdGeomXform AddReferenceToGeometry(UsdStageRefPtr pStage, const std::string& path) {
    auto geom = UsdGeomXform::Define(pStage, SdfPath(path));
    geom.GetPrim().GetReferences().AddReference("./top.geom.usd");
    return geom;
}

void AddSpin(UsdGeomXform top) {
    auto spin = top.AddRotateZOp(UsdGeomXformOp::PrecisionFloat, TfToken("spin"));
    spin.Set(0.f, UsdTimeCode(0.0));
    spin.Set(1440.f, UsdTimeCode(192.0));
}

void AddTilt(UsdGeomXform top) {
    auto tilt = top.AddRotateXOp(UsdGeomXformOp::PrecisionFloat, TfToken("tilt"));
    tilt.Set(12.f);
}

void AddOffset(UsdGeomXform top) {
    top.AddTranslateOp(UsdGeomXformOp::PrecisionFloat, TfToken("offset")).Set(GfVec3f(0.f, 0.1f, 0.f));
}

void AddPrecession(UsdGeomXform top) {
    auto precess = top.AddRotateZOp(UsdGeomXformOp::PrecisionFloat, TfToken("precess"));
    precess.Set(0.f, UsdTimeCode(0.0));
    precess.Set(360.f, UsdTimeCode(192.0));
}

void Step1() {
    auto pStage = MakeInitialStage("Step1.usda");
    pStage->SetMetadata(TfToken("comment"), "Step 1: Start and end time codes");
    pStage->Save();
}

void Step2() {
    auto pStage = MakeInitialStage("Step2.usda");
    pStage->SetMetadata(TfToken("comment"), "Step 2: Geometry reference");
    auto top = AddReferenceToGeometry(pStage, "/Top");
    pStage->Save();
}

void Step3() {
    auto pStage = MakeInitialStage("Step3.usda");
    pStage->SetMetadata(TfToken("comment"), "Step 3: Adding spin animation");
    auto top = AddReferenceToGeometry(pStage, "/Top");
    AddSpin(top);
    pStage->Save();
}

void Step4() {
    auto pStage = MakeInitialStage("Step4.usda");
    pStage->SetMetadata(TfToken("comment"), "Step 4: Adding tilt");
    auto top = AddReferenceToGeometry(pStage, "/Top");
    AddSpin(top);
    AddTilt(top);
    pStage->Save();
}

void Step5() {
    auto pStage = MakeInitialStage("Step5.usda");
    pStage->SetMetadata(TfToken("comment"), "Step 5: Adding precession and offset");
    auto top = AddReferenceToGeometry(pStage, "/Top");
    AddPrecession(top);
    AddOffset(top);
    AddTilt(top);
    AddSpin(top);
    pStage->Save();
}

void Step6() {
    const auto animLayerPath = "./Step5.usda";
    auto pStage = MakeInitialStage("Step6.usda");
    pStage->SetMetadata(TfToken("comment"), "Step 6: Layer offsets and animation");

    auto left = UsdGeomXform::Define(pStage, SdfPath("/Left"));
    auto leftTop = UsdGeomXform::Define(pStage, SdfPath("/Left/Top"));
    leftTop.GetPrim().GetReferences().AddReference(animLayerPath, SdfPath("/Top"));

    auto middle = UsdGeomXform::Define(pStage, SdfPath("/Middle"));
    middle.AddTranslateOp().Set(GfVec3d(2.0, 0.0, 0.0));
    auto middleTop = UsdGeomXform::Define(pStage, SdfPath("/Middle/Top"));
    middleTop.GetPrim().GetReferences().AddReference(animLayerPath, SdfPath("/Top"), SdfLayerOffset(96.0, 1.0));

    auto right = UsdGeomXform::Define(pStage, SdfPath("/Right"));
    right.AddTranslateOp().Set(GfVec3d(4.0, 0.0, 0.0));
    auto rightTop = UsdGeomXform::Define(pStage, SdfPath("/Right/Top"));
    rightTop.GetPrim().GetReferences().AddReference(animLayerPath, SdfPath("/Top"), SdfLayerOffset(0.0, 0.25));

    pStage->Save();
}

int main(int args, char* argv[]) {
    Step1();
    Step2();
    Step3();
    Step4();
    Step5();
    Step6();
    return 0;
}

Simple Shading in USD

f:id:hal1932:20200503202209p:plain

#include "stdafx.h"

using namespace PXR_INTERNAL_NS;

int main(int args, char* argv[]) {
    // Making a Model
    auto pStage = UsdStage::CreateNew("simpleShading.usda");
    UsdGeomSetStageUpAxis(pStage, UsdGeomTokens->y);

    auto modelRoot = UsdGeomXform::Define(pStage, SdfPath("/TexModel"));
    UsdModelAPI(modelRoot).SetKind(TfToken("component"));

    // Adding a Mesh "Billboard"
    auto billboard = UsdGeomMesh::Define(pStage, SdfPath("/TexModel/card"));
    billboard.CreatePointsAttr().Set(VtArray<GfVec3f> {
        GfVec3f(-430.f, -145.f, 0.f), GfVec3f(430.f, -145.f, 0.f), GfVec3f(430.f, 145.f, 0.f), GfVec3f(-430.f, 145.f, 0.f),
    });
    billboard.CreateFaceVertexCountsAttr().Set(VtArray<int> { 4 });
    billboard.CreateFaceVertexIndicesAttr().Set(VtArray<int> { 0, 1, 2, 3 });
    billboard.CreateExtentAttr().Set(VtArray<GfVec3f> { GfVec3f(-430.f, -145.f, 0.f), GfVec3f(430.f, 145.f, 0.f) });
    auto texCoords = billboard.CreatePrimvar(TfToken("st"), SdfValueTypeNames->TexCoord2fArray, UsdGeomTokens->varying);
    texCoords.Set(VtArray<GfVec2f> { GfVec2f(0.f, 0.f), GfVec2f(1.f, 0.f), GfVec2f(1.f, 1.f), GfVec2f(0.f, 1.f) });

    // Make a Material
    auto material = UsdShadeMaterial::Define(pStage, SdfPath("/TexModel/boardMat"));

    // Add a UsdPreviewSurface
    auto pbrShader = UsdShadeShader::Define(pStage, SdfPath("/TexModel/boardMat/PBRShader"));
    pbrShader.CreateIdAttr(VtValue(TfToken("UsdPreviewSurface")));
    pbrShader.CreateInput(TfToken("roughness"), SdfValueTypeNames->Float).Set(0.4f);
    pbrShader.CreateInput(TfToken("metallic"), SdfValueTypeNames->Float).Set(0.f);

    material.CreateSurfaceOutput().ConnectToSource(pbrShader, TfToken("surface"));

    // Add Texturing
    auto stReader = UsdShadeShader::Define(pStage, SdfPath("/TexModel/boardMat/stReader"));
    stReader.CreateIdAttr(VtValue(TfToken("UsdPrimvarReader_float2")));

    auto diffuseTextureSampler = UsdShadeShader::Define(pStage, SdfPath("/TexModel/boardMat/diffuseTexture"));
    diffuseTextureSampler.CreateIdAttr(VtValue(TfToken("UsdUVTexture")));
    diffuseTextureSampler.CreateInput(TfToken("file"), SdfValueTypeNames->Asset).Set(SdfAssetPath("USDLogoLrg.png"));
    diffuseTextureSampler.CreateInput(TfToken("st"), SdfValueTypeNames->Float2).ConnectToSource(stReader, TfToken("result"));
    diffuseTextureSampler.CreateOutput(TfToken("rgb"), SdfValueTypeNames->Float3);
    pbrShader.CreateInput(TfToken("diffuseColor"), SdfValueTypeNames->Color3f).ConnectToSource(diffuseTextureSampler, TfToken("rgb"));

    auto stInput = material.CreateInput(TfToken("frame:stPrimvarName"), SdfValueTypeNames->Token);
    stInput.Set(TfToken("st"));

    stReader.CreateInput(TfToken("varname"), SdfValueTypeNames->Token).ConnectToSource(stInput);

    UsdShadeMaterialBindingAPI(billboard).Bind(material);

    pStage->Save();
    return 0;
}