Статьи

Как использовать бесплатные 3D-модели от Google Poly в приложениях для Android

В наши дни существует огромный спрос на приложения для Android, которые предлагают захватывающую виртуальную реальность или дополненную реальность. Как разработчик, есть много различных структур, которые вы можете использовать для создания таких приложений.

Но если вы не являетесь опытным 3D-художником, как вы собираетесь создавать 3D-объекты, которые вы бы отображали в этих приложениях? Готовы ли вы потратить месяцы на обучение работе с программами трехмерного моделирования, такими как Blender или Maya? Если это не так, вам следует рассмотреть возможность использования Google Poly , онлайн-репозитория, содержащего тысячи 3D-ресурсов, которые поставляются с лицензиями Creative Commons.

Большинство активов, которые вы можете найти на Poly сегодня, являются низкополигональными с простыми материалами. Это связано с тем, что средний мобильный графический процессор еще не стал достаточно мощным для отображения трехмерных объектов с большим количеством полигонов в режиме реального времени.

В этом уроке я познакомлю вас с API Poly. Я также покажу вам, как использовать Processing для Android для рендеринга загруженных 3D-ресурсов.

Чтобы максимально использовать этот урок, вам понадобятся:

  • последняя версия Android Studio
  • устройство под управлением Android API уровня 21 или выше
  • и учетная запись Google Cloud

Все HTTP-запросы, которые вы делаете в Poly API, должны сопровождаться ключом API, который принадлежит вам. Чтобы получить ключ, начните с входа в консоль Google Cloud и перехода на панель инструментов API.

Панель управления API на облачной консоли

Затем нажмите кнопку « Включить API и службы» , разверните категорию « Другие » и выберите « Poly API» .

Раздел других API

Теперь вы можете нажать кнопку Включить , чтобы включить Poly API.

Экран Poly API

Как только API включен, для него автоматически создается набор ключей API. Вы можете открыть вкладку Credentials, чтобы взглянуть на них.

Страница учетных данных

Для этого урока вам понадобится только ключ Android . Запишите это, чтобы вы могли использовать его позже.

Поскольку у Poly в настоящее время нет официального инструментария для платформы Android, вам придется работать с Poly API напрямую, используя его интерфейс REST. Используя сетевую библиотеку Fuel , оптимизированную для языка Kotlin, вы можете сэкономить много времени и усилий. Поэтому добавьте следующую зависимость implementation в файл build.gradle модуля app :

1
implementation ‘com.github.kittinunf.fuel:fuel-android:1.13.0’

Для отображения 3D-ресурсов, загруженных из репозитория Poly, вам также потребуется механизм рендеринга. Обработка для Android идет с одним, так что добавьте его как другую зависимость.

1
implementation ‘org.p5android:processing-core:4.0.1’

Наконец, не забудьте запросить разрешение INTERNET в файле манифеста вашего проекта.

1
<uses-permission android:name=»android.permission.INTERNET»/>

Чтобы иметь возможность загрузить актив Poly, вы должны знать его уникальный идентификатор. Используя браузер, поддерживающий WebGL, вы можете легко определить идентификатор любого ресурса. Это прямо в адресной строке.

Поли ID актива отображается в адресной строке

Однако, если вы хотите, чтобы ваши пользователи динамически, во время выполнения, решали, какие активы они хотят использовать, вы можете использовать assets.list REST assets.list для определения идентификаторов этих активов. Этот метод позволяет вам искать ресурсы, используя различные параметры, такие как ключевые слова, категории и форматы 3D-файлов.

Для реалистичного примера давайте теперь попробуем найти идентификаторы нескольких активов, которые относятся к категории animals . Вы, конечно, можете выбрать любую другую действительную категорию, например, architecture , food или people .

Перед составлением HTTP-запроса рекомендуется объявить ключ API и базовый URL-адрес Poly API как константы в вашей деятельности.

1
2
3
4
companion object {
    const val key = «Abcdefghabcdefgh1234567810»
    const val baseURL = «https://poly.googleapis.com/v1»
}

Используя базовый URL-адрес, вы можете создать URL- assets.list метода REST assets.list как показано ниже:

1
val listURL = «$baseURL/assets»

На этом этапе вы можете создать действительный HTTP-запрос GET, вызвав метод httpGet() и передав ему ключ API и нужную категорию в качестве параметров запроса. При желании вы можете использовать параметр запроса format чтобы указать желаемый формат ресурсов. Poly поддерживает OBJ, FBX, TILT и ряд других популярных 3D-форматов.

Поскольку метод выполняется асинхронно и его результатом является документ JSON, вы должны присоединить к нему обработчик событий с помощью метода responseJSON() . Следующий код показывает вам, как:

1
2
3
4
5
6
7
8
9
listURL.httpGet(listOf(
        «category» to «animals»,
        «key» to key,
        «format» to «OBJ»
)).responseJson { _, _, result ->
 
    // More code here
    
}

Внутри обработчика событий, если ваш запрос был успешным, у вас будет доступ к списку объектов Asset . В поле имени каждого такого объекта указывается его идентификатор.

Кроме того, каждый объект будет иметь такие поля, как displayName , license и authorName , которые могут оказаться полезными. А сейчас давайте просто напечатаем name и displayName всех объектов. Следующий код показывает вам, как:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
result.fold({
    // Get assets array
    val assets = it.obj().getJSONArray(«assets»)
 
    // Loop through array
    for(i in 0 until assets.length()) {
        // Get id and displayName
        val id = assets.getJSONObject(i).getString(«name»)
        val displayName =
                assets.getJSONObject(i).getString(«displayName»)
 
        // Print id and displayName
        Log.d(«POLY», «(ID: $id) — (NAME: $displayName)»)
    }
}, {
    // In case of an error
    Log.e(«POLY», «An error occurred»)
})

Если вы запустите свое приложение сейчас, вы сможете увидеть следующий вывод в окне Logcat в Android Studio.

Окно Logcat, показывающее имена активов и идентификаторы

Получив уникальный идентификатор актива, вы можете напрямую добавить его к базовому URL-адресу Poly API для создания URL-адреса актива.

1
2
3
4
5
// some asset id
val assetID = «assets/3yiIERrKNQr»
 
// its url
val assetURL = «$baseURL/$assetID»

Когда вы снова сделаете HTTP-запрос GET к URL-адресу ресурса с помощью httpGet() , вы получите документ JSON, содержащий только один объект Asset .

01
02
03
04
05
06
07
08
09
10
11
assetURL.httpGet(listOf(«key» to key))
       .responseJson { _, _, result ->
           result.fold({
               val asset = it.obj()
        
               // More code here
        
           }, {
               Log.e(«POLY», «An error occurred»)
           })
       }

Как вы могли заметить в приведенном выше коде, в этом запросе также должен быть параметр запроса, в котором указан ваш ключ API.

Вы уже узнали, как использовать некоторые поля, присутствующие в объекте Asset на предыдущем шаге. Теперь все, что вам нужно сделать, это использовать массив formats присутствующий в объекте, чтобы определить URL-адреса и имена файлов, связанных с ресурсом. Каждый элемент в массиве будет иметь три важных поля:

  • formatType , который позволяет вам определить тип ресурса
  • root , который содержит имя и URL основного файла, связанного с активом
  • resources , которые содержат сведения обо всех дополнительных файлах, связанных с ресурсом, таких как материалы и текстуры

Если вы работаете с форматом OBJ, первичным файлом будет файл .obj, содержащий данные вершин и граней, а вторичными файлами обычно будут файлы .mtl, содержащие данные об используемых материалах. В следующем коде показано, как определить URL-адреса как первичных, так и вторичных файлов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var objFileURL:String?
var mtlFileURL:String?
var mtlFileName:String?
 
val formats = asset.getJSONArray(«formats»)
 
// Loop through all formats
for(i in 0 until formats.length()) {
    val currentFormat = formats.getJSONObject(i)
 
    // Check if current format is OBJ
    if(currentFormat.getString(«formatType») == «OBJ») {
        // Get .obj file details
        objFileURL = currentFormat.getJSONObject(«root»)
                                .getString(«url»)
 
        // Get the first .mtl file details
        mtlFileURL = currentFormat.getJSONArray(«resources»)
                        .getJSONObject(0)
                        .getString(«url»)
 
        mtlFileName = currentFormat.getJSONArray(«resources»)
                        .getJSONObject(0)
                        .getString(«relativePath»)
        break
    }
}

В приведенном выше коде, помимо URL- адреса файла .mtl , мы также определяем его имя, используя полеlativePath . Это важно, потому что имя жестко закодировано в элементе mtllib файла .obj и не должно изменяться.

Получив URL-адреса обоих файлов, вы можете использовать метод httpDownload() библиотеки Fuel для их загрузки. Вот как вы можете загрузить их в личный каталог вашего приложения, абсолютный путь которого можно определить с filesDir свойства filesDir :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
// download and store obj file as asset.obj
objFileURL!!.httpDownload().destination { _, _ ->
    File(filesDir, «asset.obj»)
}.response { _, _, result ->
    result.fold({}, {
        Log.e(«POLY», «An error occurred»)
    })
}
 
// download and store mtl file without
// changing its name
mtlFileURL!!.httpDownload().destination { _, _ ->
    File(filesDir, mtlFileName)
}.response { _, _, result ->
    result.fold({}, {
        Log.e(«POLY», «An error occurred»)
    })
}

Вам понадобится 3D-холст, чтобы нарисовать загруженный актив Poly. Чтобы создать его, вы должны расширить класс PApplet предлагаемый библиотекой Processing for Android. Однако созданный таким образом холст будет по умолчанию поддерживать только 2D-фигуры. Чтобы настроить его также для рисования трехмерных фигур, переопределите метод settings() и передайте P3D в качестве аргумента методу fullScreen() , который также делает холст размером с экран пользователя.

1
2
3
4
5
6
7
val canvas = object : PApplet() {
    override fun settings() {
        fullScreen(PConstants.P3D)
    }
 
    // More code here
}

Затем создайте новое свойство внутри класса, чтобы представить актив Poly как объект PShape .

1
var myPolyAsset: PShape?

Чтобы инициализировать свойство, переопределите метод setup() и вызовите метод loadShape() , передав в качестве аргумента абсолютный путь к загруженному вами файлу .obj .

1
2
3
override fun setup() {
    myPolyAsset = loadShape(File(filesDir, «asset.obj»).absolutePath)
}

Теперь вы можете начать рисовать на холсте, переопределив метод draw() . Внутри метода первое, что вам нужно сделать, это вызвать метод background() чтобы убедиться, что вы всегда рисуете на чистом холсте.

1
2
3
4
5
override fun draw() {
    background(0)
         
    // More code here
}

При непосредственном привлечении большинство активов Poly выглядят очень маленькими и перевернутыми. Вы можете исправить это либо с помощью пользовательской камеры, либо с помощью преобразования холста. Чтобы сделать это руководство простым и интуитивно понятным, давайте используем преобразования canvas.

Чтобы увеличить размер актива, используйте метод scale() и передайте ему большое отрицательное значение. Значение должно быть отрицательным, чтобы убедиться, что актив перевернут по вертикали. При желании вы можете использовать метод translate() для настройки его положения вдоль осей X и Y. Следующий код показывает вам, как:

1
2
scale(-50f)
translate(-4f,-14f)

Теперь вы можете пойти дальше и нарисовать актив, вызвав метод shape() .

1
shape(myPolyAsset)

Холст в настоящее время не является частью иерархии представления вашей деятельности. Поэтому, если вы попытаетесь запустить свое приложение сейчас, вы не сможете увидеть актив. Чтобы это исправить, сначала добавьте новый виджет FrameLayout в XML-файл макета упражнения.

1
2
3
4
5
<FrameLayout
    android:layout_width=»match_parent»
    android:layout_height=»match_parent»
    android:id=»@+id/canvas_holder»>
</FrameLayout>

Затем создайте новый экземпляр PFragment с помощью canvas и укажите его на виджет FrameLayout .

1
2
val fragment = PFragment(canvas)
fragment.setView(canvas_holder, this)

На этом этапе вы можете снова запустить приложение, чтобы увидеть актив.

Приложение, показывающее актив Poly

Теперь вы знаете, как использовать API Poly для поиска и загрузки 3D-ресурсов. В этом руководстве вы также узнали, как визуализировать и управлять этими активами с помощью Processing for Android.

Стоит отметить, что многие ресурсы, доступные в Poly, были созданы с помощью Google Blocks, приложения, доступного для пользователей HTC Vive и Oculus Rift. Если у вас есть эти VR-гарнитуры, подумайте о создании и представлении ваших собственных моделей.

Чтобы узнать больше о Poly API, вы можете обратиться к официальной документации .