Poly Toolkit for Unity

Poly Toolkit for Unity is a plugin that allows you to import assets from Poly at edit time and at runtime.

For information on how to install the toolkit and get started, see the Unity Quickstart. This page contains information about the toolkit, which has two main features:

  • Poly Asset Browser: a window that allows you to browse assets from Poly and import them into your project at edit time. The imported assets are statically included in your project. The browser window appears after the toolkit is installed. You can also open the window by selecting Poly > Browse Assets in the Unity menu.

  • Poly Toolkit API: a library that you can call from your code to find and import assets from Poly at runtime. This allow you to build apps that, for example, get assets from Poly in response to user actions. The Poly Toolkit API works by wrapping the Poly API and provides a function for importing assets as Unity game objects.

Importing assets at edit time

To import assets into your project at edit time, open the Poly Asset Browser by selecting Poly > Browse Assets in the menu. You can browse assets by category and type, or search for assets by keyword using the Search button.

When you have found an asset you wish to import, select it to view its details, review its import options, and click the Import into Project button.

Import options

  • Import Location: Indicates the file in your project where the asset will be saved. This asset file stores the assets's geometry, materials and textures.

    • Replace Existing: lets you choose an existing asset to replace. The imported asset will replace all instances of the selected existing asset.

      All existing instances of that asset (including prefab instances) will be automatically replaced by instances of the new asset. This is a convenient way to iterate on assets with an art team, since it allows you to update art assets in-place with one click.

  • Recenter: If enabled (recommended), the imported asset will be translated such that the pivot (origin of the local coordinate system) coincides with the object's centroid. If not enabled, the pivot will be kept as-is from the original file. Only disable this option if you know the file has a meaningful pivot point that you would like to preserve.

  • Also Instantiate: If enabled (recommended), the asset will be added to your scene (instantiated) in addition to being stored as a prefab. If this is not enabled, the asset will only be stored as a prefab and you must manually add it to your scene later.

  • Rescale Mode: Indicates how the asset will be rescaled from its original dimensions to your project's coordinate system. There are two possible values:

    • CONVERT: the asset will be converted from its original units (assumed to be meters) to your scene's measurement units (which you can configure in Poly > Poly Toolkit Settings). Note that depending on the asset's original dimensions, it may be too large or too small to fit on your scene. You can rescale it later, or use the FIT option instead if you want it to have an exact size. In addition to conversion, you can specify an additional scale factor to apply (in the box labeled Scale factor).

    • FIT: the asset will be rescaled to fit in the indicated size. The box entitled Desired size controls what the desired size of the asset is, in your scene's units. So, for example, if you set this to 10, this means that the asset will be scaled such that it fits inside of a 10x10x10 cube.

You can set the default values for these import options in the Editor tab of the Poly Toolkit settings panel.

Using the runtime API

This section describes basic concepts of the API and provides an overview of its functions.

Enter credentials

The Poly Toolkit API requires an API key or OAuth credentials for the Poly API. These credentials are used to identify your app and enforce usage limits. For more information, see Poly API credentials.

Once you have credentials for your app, enter them in the Runtime tab of the Poly Toolkit settings panel (shown below). To access this window, select Poly > Poly Toolkit Settings from the menu.

To access publicly available assets, you just need to enter an API key; you don't need to enter OAuth Client Id and Secret. If you need to access a user's private assets, then you must enter Client Id and Secret. The toolkit includes an implementation of OAuth 2.0 that prompts the user to authenticate and grant access. For more information, see Authentication.

Initialize the Toolkit

To initialize the toolkit, add the PolyToolkitManager prefab (located in Assets/PolyToolkit/Prefabs) to your scene. Make sure that you add the prefab to every scene that uses the Poly Toolkit API.

After adding the prefab, you can use the PolyApi class, which is the main entry point of the API.

API overview

The API allows you to:

List assets

To obtain a listing of available assets, call either the PolyApi.ListAssets or PolyApi.ListMyAssets method. Here's an example that lists featured assets:

PolyApi.ListAssets(PolyListAssetsRequest.Featured(), MyCallback);

When the response is ready, the system calls MyCallback which includes a result parameter that indicates whether the query was successful, and what the results were. Here's an example:

void MyCallback(PolyStatusOr<PolyListAssetsResult> result) {
  if (!result.Ok) {
    // Handle error.
    return;
  }
  // Success. result.Value is a PolyListAssetsResult and
  // result.Value.assets is a list of PolyAssets.
  foreach (PolyAsset asset in result.Value.assets) {
    // Do something with the asset here.
  }
}

PolyAsset do not hold any model data or resources—they contain metadata and pointers to asset resources. To obtain model data, you must import.

Custom requests

In the previous example, we used the built-in PolyListAssetsRequest.Featured() request. You can also build your own request with PolyListAssetsRequest or PolyListMyAssetsRequest. Here's an example:

PolyListAssetsRequest req = new PolyListAssetsRequest();
// Search by keyword:
req.keywords = "tree";
// Only curated assets:
req.curated = true;
// Limit complexity to medium.
req.maxComplexity = PolyMaxComplexityFilter.MEDIUM;
// Only Blocks objects.
req.formatFilter = PolyFormatFilter.BLOCKS,
// Order from best to worst.
req.orderBy = PolyOrderBy.BEST;
// Up to 20 results per page.
req.pageSize = 20;
// Send the request.
PolyApi.ListMyAssets(req, MyCallback).

Get an asset

Alternatively, if you know an asset's ID, you can obtain it directly instead of listing assets. To do that, use PolyApi.GetAsset:

PolyApi.GetAsset("assets/8nMC2GZProF", MyCallback);

When the response is ready, the system calls MyCallback which includes a result parameter that indicates whether the query was successful, and what the results were. Here's an example:

void MyCallback(PolyStatusOr<PolyAsset> result) {
  if (!result.Ok) {
    // Handle error.
    return;
  }
  // Success. result.Value is a PolyAsset
  // Do something with the asset here.
}

PolyAsset objects do not hold model data or resources—they contain metadata and pointers to asset resources. To obtain model data, you must either import.

Import an asset

To import a PolyAsset as a GameObject, call the PolyApi.Import method:

PolyApi.Import(asset, PolyImportOptions.Defaults(), MyCallback);

After the asset is downloaded and imported, the system executes the callback:

void MyCallback(PolyStatusOr<PolyImportResult> result) {
  if (!result.Ok) {
    // Handle error.
    return;
  }
  // Success. Place the result.Value.gameObject in your scene.
}

Fetch a thumbnail

Displaying thumbnails for assets is an effective and economical way to let the user preview an asset without having to import or download resources. To fetch a thumbnail, call PolyApi.FetchThumbnail:

PolyApi.FetchThumbnail(asset, MyCallback);

After the thumbnail is fetched, the system executes the callback:

void MyCallback(PolyAsset asset, PolyStatus status) {
  if (!status.ok) {
    // Handle error;
    return;
  }
  // Display the asset.thumbnailTexture.
}

Attributions at Runtime

If your app loads and displays third-party assets at runtime, you are required to give proper attribution to the authors of those assets. You can generate an attributions text for all your imported assets through the GenerateAttributions method of PolyApi.

List<PolyAsset> assets = ...;  // List of assets you are using.
string attribs = PolyApi.GenerateAttributions(
    includeStatic: true, runtimeAssets: assets);

Display this string in an appropriate UI in your app. This text will include the attributions for your static assets (includeStatic: true) and the given list of runtime assets (runtimeAssets: assets).

Authentication

The toolkit includes an implementation of OAuth 2.0, which is required for access to private assets. This section shows you how to use the toolkit's authentication.

First ensure that you have entered credentials including your OAuth 2.0 client ID and client secret in Poly Toolkit settings.

There are two types of authentication: interactive and silent.

Interactive authentication launches a browser and prompts the user to sign in to the Google Authorization server and grant access to your app. This is the type of authentication your app should invoke when the user clicks a Sign In button in your app.

Silent authentication attempts to authenticate using an existing token (from a previous interactive session) and is invisible to the user.

For an optimal user experience, we recommend that apps use both interaction and silent authencation as described below:

  1. Attempt silent authentication.
  2. If silent authentication succeeds:
    1. Indicate that the user is signed in. For example, show their name and profile picture and name in the toolbar.
  3. Else (silent authentication fails):
    1. Remain unauthenticated and show a Sign In button.
    2. When the user clicks Sign In:
      1. Attempt interactive authentication.
      2. If successful, welcome the user.
      3. If failed, display an error.

Note that a failure during silent authentication is not considered an error; a failure is expected if the user has not signed in before or if the token from a previous sign-in has expired. A failure during interactive authentication is always considered an error; the app should display an appropriate error message.

To implement this authentication flow with the Toolkit API, invoke PolyApi.Authenticate with interactive set to false in your the scene's start() method:

PolyApi.Authenticate(/* interactive */ false, SilentAuthCallback);

// "Sign in" button on your UI. Initially not shown.
Button signInButton = ....;
// Profile pic widget on your UI:
Image profilePic = ....;
// Text field on your UI where you show the user's name:
Text userNameText = ....;

From your callback, decide whether or not to show a Sign In button:

private void SilentAuthCallback(PolyStatus status) {
  if (status.ok) {
    WelcomeUser();
  } else {
    // Silent sign in failed, so show the Sign In button.
    // This is NOT an error.
    signInButton.gameObject.SetActive(true);
  }
}

private void WelcomeUser() {
  // Sign in successful. Show user's profile pic and user name.
  profilePic.sprite = PolyApi.UserIcon;
  userNameText.text = PolyApi.UserName;
}

Now add an event handler to your Sign In button that invokes interactive authentication:

private void Awake() {
  // ...
  signInButton.onClick.AddHandler(OnSignInClicked);
  // ...
}

private void OnSignInClicked() {
  // Sign in button was clicked. Invoke interactive authentication
  // (which may or may not involve launching a browser window).
  PolyApi.Authenticate(/* interactive */ true, InteractiveAuthCallback);
}

private void InteractiveAuthCallback(PolyStatus status) {
  if (status.ok) {
    // Success!
    WelcomeUser();
  } else {
    // Error: something went wrong during sign in.
    // ... (show error message to user) ...
  }
}

Reuse existing authentication

If your app already includes an implementation of OAuth for another Google API, you can reuse the implementation with Poly Toolkit.

First, ensure that your authentication includes the following OAuth scopes:

  • https://www.googleapis.com/auth/peopleapi.readonly - View and edit Google people information

  • https://www.googleapis.com/auth/vrassetdata.readonly - View your 3D creations and their metadata

Next, call a special overload of PolyApi.Authenticate that takes an access token and refresh token:

string accessToken = ....;  // access token
string refreshToken = ....;  // refresh token
PolyApi.Authenticate(accessToken, refreshToken, MyCallback);

Toolkit settings

To access the Poly Toolkit settings panel, click Poly > Poly Toolkit Settings on the menu. If the settings panel doesn't show, check that the Inspector window is visible (Window > Inspector in the menu).

General settings

  • Scene unit: lets you configure what unit of measurement you use in your scene. This helps with automatic conversion of asset scale when importing an asset. For example, if your scene is in centimeters and you import an asset that's given in meters, it will be scaled up by 100x to adapt to your scene.

  • Surface shader materials, Pbr materials: these specify the materials that will be used for imported assets. For more information about this, consult the Material System section below.

Editor settings

These settings control how Poly Toolkit behaves at edit-time.

  • Asset Objects Path: indicates where in your project Poly Toolkit will save asset and prefab files.

  • Asset Sources Path: indicates where in your project Poly Toolkit will save asset source files (GLTF files, textures, etc).

  • Resources Path: indicates where in your project Poly Toolkit will save resource files (files to load at runtime).

  • Default import options: specifies default values for the import options in the Poly Asset Browser.

  • Send Editor Analytics: indicates whether or not to send anonymous usage data via Google Analytics. This data doesn't contain any personal information and helps us understand how Poly Toolkit is used in order to prioritize bug fixes and improvements. Usage data is sent while in the editor only, not at runtime in your app.

Runtime settings

These settings control how Poly Toolkit behaves at runtime in your app.

  • Auth Config: authentication information.

    • Api Key (required): this is your project's API key used to access the Poly API. The API key is required for all Poly Toolkit API calls, regardless of whether or not they are authenticated.
    • Client ID (optional): this is your project's OAuth2 Client ID, used for authenticated API calls. If you don't use authenticated API calls, you can leave this field blank.
    • Client Secret (optional): this is your project's OAuth2 Client secret, used for authenticated API calls. If you don't use authenticated API calls, you can leave this field blank.
    • Additional Scopes (optional): if you need any additional Google API scopes as a result of using other Google APIs together with the Poly API, you can include the extra scopes here. If not, leave this field blank.
  • Cache config: controls how to cache web requests and assets.

    • Cache enabled: indicates whether to use a client-side cache when making requests to the Poly API. This cache will hold API responses and asset data. Using the cache will help you conserve download bandwidth and API call quota, since repeated requests to the same assets will be served from the offline cache.
    • Max Cache Size MB: maximum size of the cache, in megabytes. Recommended: 512MB or larger.
    • Max Cache Entries: maximum number of cache entries. Recommended: 2048 or larger.
    • Cache path override: if set, the cache will be kept at this (absolute) path instead of at the default disk location. If unset, the default location will be used.

Toolkit folder layout

  • Assets/PolyToolkit: this folder is created when you add the Poly Toolkit to your project. This folder contains the internal files needed by the toolkit. You shouldn't need to make any modifications to files inside of this folder.

    • Assets/PolyToolkit/ExampleScenes: contains example scenes that you can run to try out Poly Toolkit. For more information on running samples, see the Unity Quickstart.

    • Assets/PolyToolkit/Scripts: contains the files for the Poly Toolkit API.

  • Assets/Poly: this folder holds the assets that you import at edit-time using the Poly Browser window.

    • Assets/Poly/Assets: contains the asset files and prefabs that correspond to the objects you have imported. Use the prefabs from this folder to instantiate assets into your scene.

    • Assets/Poly/Sources: contains the original files downloaded from the Poly API that were used to create the assets.

Material system

When Poly Toolkit imports an asset, it assigns materials to the asset according to the rules below.

  • Blocks objects: Blocks objects have a very small set of possible materials (most of the Blocks look and feel is accomplished through vertex colors). These materials are listed under Surface Shader Materials in Poly Toolkit Settings. You can override these materials with your own if you want to customize how Blocks objects are rendered. Note that your replacement to the "Blocks Paper" shader will have to support vertex colors in order to render Blocks objects correctly.

  • Tilt Brush sketches: Tilt Brush sketches use built-in materials. These materials are highly customized to produce the Tilt Brush look and feel and can't normally be overridden.

  • Custom objects: Objects directly uploaded to Poly via the web interface are normally specified in terms of PBR parameters. For each of those objects Poly Toolkit will generate materials based on the material templates specified in Poly Toolkit Settings (Base PBR material settings). You can override these with your own materials to customize how they are rendered.

Attribution

Assets in Poly are normally available through the Creative Commons license, which allows them to be remixed subject to certain conditions.

If you are using any third-party assets in your app, you are required to give proper attribution to the authors. To help you do so, Poly Toolkit automatically generates an attributions file called PolyAttributions.txt under Assets/Poly/Resources. This file contains a list of third-party assets that you have imported into your project, with information about their titles, authors and license.

You can display this file at runtime by loading it from the resources folder into your UI:

using UnityEngine.UI;

Text attribsText  = ....;  // A text field in your UI.

// Load attributions text into the text field for display.
attribsText.text = Resources.Load<TextAsset>("PolyAttributions").text;

Advanced: Throttling Main Thread Usage

When importing assets, Poly Toolkit uses background threads to download and parse the server responses, but the assembling of graphics objects (meshes, textures, etc) must be done on the main thread.

By default, Poly Toolkit will perform all necessary main thread tasks in a single batch. This is the best behavior for most applications, since it optimizes for total load time and delivers imported assets to your application as quickly as possible.

However, if you have a performance-sensitive application (for example, a VR application), this default implementation might cause certain frames to take much longer than others, resulting in an uneven frame rate.

With these performance-sensitive applications in mind, Poly Toolkit offers a way to exercise more fine-grained control over main thread load.

To use this feature, set clientThrottledMainThread to true in PolyImportOptions.

PolyAsset assetToImport = ....;  // The asset to import

// Set up the PolyImportOptions.
PolyImportOptions options = PolyImportOptions.Default();
// Request client-throttling of main thread operations.
options.clientThrottledMainThread = true;

// Import.
PolyApi.Import(assetToImport, options, MyCallback);

When you enable this option, your callback will be called when the main thread work is ready to start, rather than when the imported asset is ready to use.

private IEnumerator throttler;
private GameObject assetObject;

private void ImportAssetCallback(PolyAsset asset,
    PolyStatusOr<PolyImportResult> result) {
  if (!result.Ok) {
    // Handle failure.
    return;
  }
  // This is the "skeleton" GameObject that will be progressively
  // created as you drive the throttler object.
  assetObject = result.Value.gameObject;
  // This is the throttler object, which you must manually enumerate
  // in order to create the game objects.
  throttler = result.Value.mainThreadThrottler.GetEnumerator();
}

At this point, assetObject will be nothing but a skeleton (just an empty object with some children). Your application must now enumerate the throttler object to cause Poly Toolkit to do work to create the object. Every time you advance the enumerator by one, Poly Toolkit will do one discrete unit of main thread work. It is up to your implementation to decide how much main thread work to do on each frame (for example, you could limit it to a given frame time budget).

For example, you cap the main thread work to a frame budget:

System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();

private void Update() {
  if (throttler != null) {
    // Poly Toolkit still has main thread work to do.
    // Do work until 10ms have elapsed, then stop.
    stopWatch.Reset();
    while (stopWatch.ElapsedMilliseconds < 10) {
      // Calling MoveNext() is what causes Poly Toolkit to do main thread
      // work (it's implemented as a coroutine).
      if (!throttler.MoveNext()) {
        // Main thread work is now done.
        throttler = null;
        break;
      }
    }
  }
}