Merge branch 'chapter3' into bozza
This commit is contained in:
commit
50e4953117
31
src/chapter3.0.md
Normal file
31
src/chapter3.0.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Progetti d'esempio
|
||||
|
||||
Per poter realizzare delle applicazioni mediante ARCore e Sceneform sono necessarie una serie di configurazioni iniziali.
|
||||
|
||||
Requisito necessario al funzionamento di ARCore è una versione di Android uguale o superiore ad Android 7.0 Nougat(API level 24).
|
||||
Inoltre se si sta lavorando su un progetto con API level minore di 26 è necessario esplicitare il supporto a Java 8 andando a modificare file `app/build.gradle`.
|
||||
|
||||
```gradle
|
||||
android {
|
||||
...
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Sempre nel file per il build del progetto è necessario aggiungere la dipendenza di Sceneform.
|
||||
|
||||
```gradle
|
||||
implementation "com.google.ar.sceneform.ux:sceneform-ux:1.6.0"
|
||||
```
|
||||
|
||||
Inoltre nell'Android Manifest[^manifest] va dichiarato l'utilizzo del permesso della fotocamera[^camera] e l'utilizzo di ARCore[^arcore].
|
||||
|
||||
[^manifest]: File in cui vengono dichiarate tutte caratteristiche di un'applicazione Android, tra cui anche i permessi.
|
||||
|
||||
[^camera]: Lo sviluppatore deve solo dichiarare l'utilizzo del permesso, la richiesta di concessione è gestita in automatico da Sceneform.
|
||||
|
||||
[^arcore]: L'utilizzo di ARCore deve essere dichiarata in quanto non tutti i dispositivi lo supportano.
|
128
src/chapter3.1.md
Normal file
128
src/chapter3.1.md
Normal file
@ -0,0 +1,128 @@
|
||||
## Augmented images
|
||||
|
||||
Nel primo progetto d'esempio si è affrontato un classico problema di AR marker based, ovvero il riconoscimento di un'immagine preimpostata e il conseguente sovrapponimento di un oggetto virtuale.
|
||||
Nel caso specifico si vuole riconoscere una foto del pianeta terra e sostituirvi un modello tridimensionale di essa.
|
||||
|
||||
### Aggiunta del modello
|
||||
|
||||
Il modello tridimensionale della terra è stato recuperato dal sito `poly.google.com`, che funge da repository per modelli 3D.
|
||||
La scelta del modello è stata dettata sia del formato[^format] in cui era disponibile, sia dalla licenza con cui veniva distribuito.
|
||||
Una volta ottenuto il modello è stato salvato nella cartella *"sampledata"*, il cui contenuto sarà usato solo in fase di progettazione.
|
||||
|
||||
L'importazione del modello all'interno del progetto di Android Studio è stato effettuato mediante l'utilizzo del plug-in *Google Sceneform Tools*, che si occupa sia di convertire il modello nel formato di Sceneform, sia di aggiornare il file `build.gradle` affinché sia incluso nell'APK[^apk] finale.
|
||||
|
||||
### Creazione del database
|
||||
|
||||
Il database contenente tutte le immagini che si desidera far riconosce all'applicazione, può essere creato sia a priori, sia a tempo di esecuzione.
|
||||
Per la prima soluzione Google mette a disposizione *The arcoreimag tool*, un software a linea di comando, che oltre a creare il database, si occupa anche di valutare l'immagine.
|
||||
|
||||
Dato che nel caso specifico si vuole far riconoscere un'unica immagine, si è optato per la generazione del database a runtime.
|
||||
In particolare quest'operazione avviene mediante la funzione `setupAugmentedImageDb`.
|
||||
|
||||
```kotlin
|
||||
private fun setupAugmentedImageDb (config: Config): Boolean {
|
||||
val image = loadImage(IMAGE_FILE_NAME) ?: return false
|
||||
|
||||
val augmentedImageDb = AugmentedImageDatabase(session)
|
||||
augmentedImageDb.addImage(IMAGE_NAME, image)
|
||||
config.augmentedImageDatabase = augmentedImageDb
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
### Riconoscimento dell'immagine
|
||||
|
||||
Il riconoscimento dell'immagine non può avvenire mediate l'utilizzo di una callback in quanto ARCore non permette di registrare un listener all'evento.
|
||||
Risulta dunque evidente che la verifica dell'avvenuto match sarà delegata allo sviluppatore.
|
||||
|
||||
Per fare ciò si è usato il metodo `addOnUpdateListener` dell'oggetto `Scene`, che permette di eseguire un pezzo di codice ogni qual volta la scena viene aggiornata.
|
||||
|
||||
```kotlin
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
//...
|
||||
|
||||
arSceneView.scene.addOnUpdateListener(this::detectAndPlaceAugmentedImage)
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Dove la funzione `detectAndPlaceAugmentedImage` si occupa di verificare la presenza di un match e nel caso di un riscontro positivo, dell'aggiunta dell'oggetto virtuale alla scena.
|
||||
|
||||
```kotlin
|
||||
private fun detectAndPlaceAugmentedImage(frameTime: FrameTime) {
|
||||
if (isModelAdded)
|
||||
return
|
||||
|
||||
val augmentedImage = arSceneView.arFrame
|
||||
.getUpdatedTrackables(AugmentedImage::class.java)
|
||||
.filter { isTrackig(it) }
|
||||
.find { it.name.contains(IMAGE_NAME) }
|
||||
?: return
|
||||
|
||||
val augmentedImageAnchor = augmentedImage.createAnchor(augmentedImage.centerPose)
|
||||
|
||||
buildRenderable(this, Uri.parse(MODEL_NAME)) {
|
||||
addTransformableNodeToScene(
|
||||
arFragment,
|
||||
augmentedImageAnchor,
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
isModelAdded = true
|
||||
}
|
||||
```
|
||||
|
||||
Il settaggio del flag `isModelAdded` al valore booleano di vero, si rende necessario al fine di evitare l'aggiunta incontrollata di nuovi modelli alla medesima immagine.
|
||||
|
||||
### Rendering del modello
|
||||
|
||||
Il rendering del modello avviene attraverso la funzione `buildRenderable` che a sua volta chiama la funzione di libreria `ModelRenderable.builder()`.
|
||||
Poiché quest'ultima è un operazione onerosa viene restituito un `Future`[^future] che racchiude il `Renderable` vero e proprio.
|
||||
L'interazione con l'oggetto concreto avviene mediante una callback che è possibile specificare attraverso il metodo `thenAccept`.
|
||||
|
||||
```kotlin
|
||||
fun buildRenderable(
|
||||
context: Context,
|
||||
model: Uri,
|
||||
onSuccess: (renderable: Renderable) -> Unit
|
||||
) {
|
||||
ModelRenderable.builder()
|
||||
.setSource(context, model)
|
||||
.build()
|
||||
.thenAccept(onSuccess)
|
||||
.exceptionally {
|
||||
Log.e("SCENEFORM", "unable to load model", it)
|
||||
return@exceptionally null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Aggiunta dell'oggetto virtuale nella scena
|
||||
|
||||
L'ultima operazione da compiere è l'aggiunta del modello renderizzato alla scena.
|
||||
Questa operazione avviene attraverso la funzione `addTrasformableNodeToScene` che si occupa di creare un'ancora in corrispondenza del punto reale d'interesse.
|
||||
A partire da quest'ancora viene creato un nodo che racchiude l'oggetto renderizzato.
|
||||
Inoltre nel caso specifico si è usato un `TransformabelNode`, in modo da concedere all'utente la possibilità di ridimensionare o ruotare il modello.
|
||||
|
||||
```kotlin
|
||||
fun addTransformableNodeToScene(
|
||||
arFragment: ArFragment,
|
||||
anchor: Anchor,
|
||||
renderable: Renderable
|
||||
) {
|
||||
val anchorNode = AnchorNode(anchor)
|
||||
val transformableNode = TransformableNode(arFragment.transformationSystem)
|
||||
transformableNode.renderable = renderable
|
||||
transformableNode.setParent(anchorNode)
|
||||
arFragment.arSceneView.scene.addChild(anchorNode)
|
||||
transformableNode.select()
|
||||
}
|
||||
```
|
||||
|
||||
[^format]: Attualmente sono supportati solo modelli OBJ, FBX e gLTF.
|
||||
|
||||
[^apk]: Formato delle applicazioni Android.
|
||||
|
||||
[^future]: In informatica con il termine *future*, o *promise*, *delay* e *deferred*, si indica un tecnica che permette di sincronizzare l'esecuzione di un programma concorrente.
|
93
src/chapter3.2.md
Normal file
93
src/chapter3.2.md
Normal file
@ -0,0 +1,93 @@
|
||||
## Runtime fetching models
|
||||
|
||||
Nella seconda applicazione d'esempio viene mostrato come sia possibile recuperare i modelli da renderizzare anche durante l'esecuzione dell'applicazione.
|
||||
Questa funzione risulta particolarmente utile quando si deve rilasciare un'applicazione che sfrutta numerosi modelli e non si vuole appesantire eccessivamente il volume del file *APK*.
|
||||
Inoltre concede maggiore libertà allo sviluppatore in quanto è possibile aggiungere nuovi modelli, o aggiornare quelli vecchi, senza dover operare sull'applicazione, ma lavorando esclusivamente lato server.
|
||||
|
||||
In questo caso specifico l'applicazione dovrà riconosce uno o più piani e in seguito ad un tocco dell'utente su di essi, mostrare un modello di *Andy*, la mascotte di Android.
|
||||
|
||||
Per quest'applicazione oltre alle configurazioni già viste in precedenza è necessario aggiungere una nuova dipendenza che include le funzioni necessarie per il fetching del modello.
|
||||
|
||||
```gradle
|
||||
implementation 'com.google.ar.sceneform:assets:1.6.0'
|
||||
```
|
||||
|
||||
Inoltre nell'Android Manifest bisogna aggiungere il permesso per accedere alla rete.
|
||||
|
||||
### Interazione con l'utente
|
||||
|
||||
L'interazione con l'utente avviene mediante un tocco sul display in corrispondenza di un piano.
|
||||
Sceneform ci permette di personalizzare il comportamento al verificarsi di questo evento tramite il metodo `setOnTapArPlaneListener`.
|
||||
|
||||
```kotlin
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
//...
|
||||
arFragment.setOnTapArPlaneListener(this::fetchAndPlaceModel)
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Dove la funzione `fetchAndPlaceModel` si occupa di recuperare il modello e renderizzarlo.
|
||||
|
||||
```kotlin
|
||||
private fun fetchAndPlaceModel(
|
||||
hitResult: HitResult,
|
||||
plane: Plane,
|
||||
motionEvent: MotionEvent
|
||||
) {
|
||||
val modelUri = Uri.parse(MODEL_SOURCE)
|
||||
val fetchedModel = fetchModel(this, modelUri)
|
||||
buildRenderable(this, fetchedModel, modelUri) {
|
||||
addTransformableNodeToScene(
|
||||
arFragment,
|
||||
hitResult.createAnchor(),
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching del model
|
||||
|
||||
Il recupero del modello avviene attraverso la funzione `fetchModel`, che a sua volta chiama la funzione di libreria `RenderableSource.builder()`.
|
||||
|
||||
```kotlin
|
||||
fun fetchModel(
|
||||
context: Context,
|
||||
source: Uri
|
||||
) : RenderableSource {
|
||||
return RenderableSource.builder()
|
||||
.setSource(context, source, RenderableSource.SourceType.GLTF2)
|
||||
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
|
||||
.build()
|
||||
}
|
||||
```
|
||||
|
||||
Attualmente[^sceneform-1.6] Sceneform supporta unicamente il fetching di modelli gLTF.
|
||||
|
||||
### Rendering e aggiunta del modello
|
||||
|
||||
Il rendering del modello avviene tramite la funzione `buildRenderable`, che riprende in buona parte quella vista precedentemente, con la differenza che in questo caso deve essere passato anche il modello recuperato.
|
||||
|
||||
```kotlin
|
||||
fun buildRenderable(
|
||||
context: Context,
|
||||
model: RenderableSource,
|
||||
modelUri: Uri,
|
||||
onSuccess: (renderable: Renderable) -> Unit
|
||||
) {
|
||||
ModelRenderable.builder()
|
||||
.setRegistryId(modelUri)
|
||||
.setSource(context, model)
|
||||
.build()
|
||||
.thenAccept(onSuccess)
|
||||
.exceptionally {
|
||||
Log.e("SCENEFORM", "unable to load model", it)
|
||||
return@exceptionally null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Infine l'aggiunta del modello renderizzato alla scena avviene mediante la medesima funzione `addTransformableNodeToScene` vista in precedenza.
|
||||
|
||||
[^sceneform-1.6]: Sceneform 1.6.0.
|
Loading…
Reference in New Issue
Block a user