# Build an Advanced Portal Experience

{% content-ref url="<https://app.gitbook.com/o/QMsd5hT6sxA9FBIB03yh/s/3UKy7VHRJWrNarYaMp13/>" %}
[Blippar Documentation Centre](https://app.gitbook.com/o/QMsd5hT6sxA9FBIB03yh/s/3UKy7VHRJWrNarYaMp13/)
{% endcontent-ref %}

## Introduction

This document provides a brief overview and walkthrough into the construction of a portal experience, built using **‘Blippar's WebAR SDK, A-Frame, Blender(optional).’** It displays an output of a portal model fixed to an identified surface. This portal provides a gateway through a door, to a **whole new virtual world!** ([see here](#h_01fhdhbxr43xnrtq5nc20t9he0)).

## Concept

The main concept on which the portal experience is built is described in this document.

## Portal Experience Logic

The portal experience logic has the following needs:

* Room 3D model, a Sphere with 360° image/video texture
* Door 3D model to enter the Room.
* Hider 3D model, a Sphere with invisible material to make the external surface of the Room invisible on the AR scene.

## WebAR SDK API Utilised

It mainly uses the following two WebAR SDK APIs:

1. *<mark style="color:blue;">SetPortalEntryCallback() API</mark>*\
   \
   When the user takes the mobile phone closer to the Door 3D model (which is at the origin of the detected surface), *SetPortalEntryCallback()* API triggers the passed callback.\\
2. *<mark style="color:blue;">DisableTracking(true) API</mark>*

* This API disables the Surface Tracking in the callback.
* This API can also be used to enable the gyro camera experience.
* Pass the argument as *true* as in *<mark style="color:blue;">DisableTracking(true)</mark>* call to enable it.
* The user can use the gyro camera to experience an entry into a virtual world and look around the inner side of the Room Sphere having 360° image/video material

## Procedure

Follow the below steps to create your AR portal experience web app:

{% hint style="info" %}

* Creating the Portal Models in Blender is [(STEP 1-OPTIONAL)](#h_01fhdh8wzw3qp9sy2e4dm7aygf) You can use the portal and hider 3D models that we have already created for your testing purposes from the [Github repo here](https://github.com/blippar/webar-sdk-example/tree/main/aframe/portal-webar) and proceed to [STEP 2](#h_01fhdh9jqfz8t39s9mywr15n5t)
* If you are making your own portal models, make sure the door is at the origin of the detected surface i.e. <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark> as shown in Fig 1.
  {% endhint %}

## **Step 1: Create Portal Models in Blender (Optional)** <a href="#h_01fhdh8wzw3qp9sy2e4dm7aygf" id="h_01fhdh8wzw3qp9sy2e4dm7aygf"></a>

The AR portal experience basically needs two models.

**portal.glb** containing:

* **Portal Door** - A *Grid* mesh converted into a rectangular door and located at the origin.
* **Portal Room** - A *Sphere* mesh with a 360° image texture

**portal-hider.glb** containing:

* **Portal Hider** - A *Sphere* mesh to cover/hide the Portal Room mesh. This Portal Hider model will be made transparent so that the Portal Room will look invisible from the outside.

To Create a ***portal.blend*** file follow the steps:

* Open the Blender application
* *Create* a *New* ***General** Blender* file.
* Select all the default objects (Cube, Camera and Light) and *Delete* them.
* Save the empty Blender scene as ***portal.blend*** file.

To Create and export the GLB model files in Blender follow the steps:

**A. Create a Portal Door Mesh**

* Add a ***Grid*** mesh
* Rotate it about the ***X axis*** by ***90°***
* Switch to ***Edit Mode,*** select the **inner \_Faces**\_ of the ***Grid*** mesh and ***Delete*** them
* Switch to ***Object Mode*** and edit the ***Scale*** properties as **X = 0.200**, **Y = 0.250**
* Switch to ***Edit Mode***, select **all the Faces** and **Extrude** by **0.1**
* Switch to ***Object Mode*** and edit the Location properties as **Y = 0.1 m**, so that the entrance is exactly at the origin.

**B. Create a Portal Room Mesh**

* Add a ***UV Sphere*** mesh and change its ***Add UV Sphere*** attributes to ***Segments = 32*** and ***Rings = 32***
* Change its ***Location*** properties to ***Y = 1.025,** so that the **Grid**(Door) mesh intersects on the surface of the sphere*
* Switch to **Edit Mode**, select the sphere's ***Faces*** appearing within the door boundary and **Subdivide** the Edges by setting ***Number of Cuts = 2***
* ***Deselect*** all and ***Select*** the sphere's ***Faces*** appearing within the door boundary and ***Delete*** them\_.\_

**C. Add a 360° Texture Image to the Portal Room Mesh**

* Select the ***Sphere*** mesh
* Add a new **Material** and change its ***Base Color*** to ***Image Texture***
* Click ***Open*** and select a **360° image file** of your choice
* ***Right click*** on the sphere and select ***Shade*** ***Smooth***
* Click ***Viewport Shading***, Click ***Display in Material preview mode*** to verify the 360° texture material
* Select the ***Grid*** mesh
* Add a new **Material** and change its ***Base Color*** to *any color* of your choice

**D. Create the Portal Hider Mesh**

* Select the ***Sphere*** mesh and ***Duplicate*** it in the same location
* ***Rename*** the new ***Sphere.001*** mesh as ***Hider***
* Scale the object to **1.01** across **all the axis**
* Go to its ***Material Properties*** and ***Remove*** its ***Material Slot***

**E. Export the Portal GLB**

* Select both the ***Grid*** and ***Sphere*** meshes
* Go to ***File*** -> ***Export*** -> ***glTF 2.0 (.glb/.gltf)***
* On the right side panel of ***Blender File View*** dialog, go to ***Include*** tab and check ***Selected Objects*** (if not checked)
* Enter the name as **portal.glb**
* Click ***Export glTF 2.0*** button

**F. Export the Portal Hider GLB**

* Select the ***Hider*** mesh
* Go to ***File*** -> ***Export*** -> ***glTF 2.0 (.glb/.gltf)***
* On the right side panel of ***Blender File View*** dialog, go to ***Include*** tab and check ***Selected Objects*** (if not checked)
* Enter the name as **portal-hider.glb**
* Click ***Export glTF 2.0*** button

## **Step 2: Setup Base Code Using the WebAR SDK Example** <a href="#h_01fhdh9jqfz8t39s9mywr15n5t" id="h_01fhdh9jqfz8t39s9mywr15n5t"></a>

* Purchase the Blippar License for your domain and download the WebAR SDK zip file. Extract the files and two folders named ***blippar*** and ***example*** will appear.
* Rename the example folder to portal-webar (or any name of your choice).
* Copy the ***portal.glb*** and ***portal-hider.glb*** files created in the previous step (if you skipped the previous step copy them from [this link](https://github.com/blippar/webar-sdk-example/tree/main/aframe/portal-webar)) and paste it in the models folder.

<figure><img src="https://content.gitbook.com/content/6tbyCGI9hWqlzoZSPewE/blobs/50fFkQPkHTnMmpOKW8IN/Portal_Base_Code%20(1).gif" alt=""><figcaption><p>Setup Base Code</p></figcaption></figure>

## **Step 3: Import Portal models** <a href="#h_01fhdh9sjhq6a265zc4s5neejx" id="h_01fhdh9sjhq6a265zc4s5neejx"></a>

* Open the WebAR SDK folder in **Visual Studio Code (VSC)**
* Click open ***portal-webar / index.html*** file
* Under <mark style="color:orange;">\<a-assets></mark> tag, do the following:
* \- Remove the following line

```html
<a-asset-item id="astronaut" src="models/astronaut.glb"></a-asset-item>
```

* +Add the following lines to import portal glb files as asset items

```html
<a-asset-item id="portalhider" src="models/portal-hider.glb"></a-asset-item>
<a-asset-item id="portal" src="models/portal.glb"></a-asset-item>
```

* Under <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark>
* \- Remove the following line

```html
<a-entity gltf-model="#astronaut" id="astronaut_1" rotation="0 0 0" position="0 0 0" scale="0.25 0.25 0.25" webar-loadmonitor="elType: glb"></a-entity>
```

* \+ Add the following lines to add the portal models as AFRAME entities, scaled to **3.0** on all the axis

```html
<a-entity gltf-model="#portalhider" id="portal_1" rotation="0 0 0" position="0 0 0" scale="3.0 3.0 3.0" webar-loadmonitor="elType: glb"></a-entity>
<a-entity gltf-model="#portal" id="portal_2" rotation="0 0 0" position="0 0 0" scale="3.0 3.0 3.0" webar-loadmonitor="elType: glb"></a-entity>
```

<figure><img src="https://content.gitbook.com/content/6tbyCGI9hWqlzoZSPewE/blobs/7abH6N2w7PH0maiJUllE/Portal_ImportModels.gif" alt=""><figcaption><p>Import Portal Models</p></figcaption></figure>

## **Step 4: Add Surface Landing Animation** <a href="#h_01fhdha14dhvcgh458j59avape" id="h_01fhdha14dhvcgh458j59avape"></a>

* \+ Include the [aframe-animation-timeline-component js](https://github.com/supermedium/superframe/tree/master/components/animation-timeline/) script tag after the WebAR SDK's script

```html
<script src="https://unpkg.com/aframe-animation-timeline-component@2.0.0/dist/aframe-animation-timeline-component.min.js"></script>
```

* \- Remove the following lines from the example code

```html
<!-- Refer API:Functions documentation for more details -->
<script>
  // Give a callback when the WebAR Stage <a-entity webar-stage> is ready to display the 3d object
  WEBARSDK.SetStageReadyCallback(() => {
    console.info('Stage is ready now!!!');
  });
</script>
```

* \+ Add the following lines of code under <mark style="color:orange;">\<body></mark> and before <mark style="color:orange;">\<a-scene</mark> <mark style="color:blue;">webar-scene</mark><mark style="color:orange;">></mark> element to:
* Create a custom *landingcursor* A-Frame component.
* The landing cursor A-Frame ring object/entity created in the following steps uses this *landingcursor* component.
* When initialised call WebAR SDK's <mark style="color:orange;">SetStageReady()</mark> callback to emit ***landit*** event to start ***landingTimeline*** animation sequence.
* Listen to ***cursorend*** mixin animation's ***animationcomplete*** event (*<mark style="color:red;">animationcomplete\_\_cursorend</mark>*), to make portal objects visible and then cursor object invisible.

```html
<script>
  var portalObject = undefined;
  var portalHiderObject = undefined;

  AFRAME.registerComponent("landingcursor", {
    init: function() {
      WEBARSDK.SetStageReadyCallback(() => {
        console.log('Stage is ready now!!!');
        portalHiderObject = document.getElementById('portal_1');
        portalObject = document.getElementById('portal_2');

        // First, show the surface landing animation
        this.el.emit('landit');
      });

      this.el.addEventListener("animationcomplete__cursorend", e => {
        // Second, show the 3D object
        portalHiderObject.setAttribute('visible', true);
        portalObject.setAttribute('visible', true);

        // Hide surface landing cursor
        this.el.setAttribute('visible', false);
      });
    }
  });
</script>
```

* \+ Add the following lines of code under <mark style="color:orange;">\<a-assets></mark> to:
* Add a timeline asset <mark style="color:orange;">\<a-timeline</mark> <mark style="color:red;">id</mark>=<mark style="color:blue;">"landingTimeline"</mark>> in A-Frame's <mark style="color:orange;">\<a-asset></mark>. It applies two timeline animations, ***cursorbegin*** and ***cursorend*** to the selected cursor entity (#landing).

```html
<a-timeline id="landingTimeline">
  <a-timeline-animation select="#landing" name="cursorbegin"></a-timeline-animation>
  <a-timeline-animation select="#landing" name="cursorend"></a-timeline-animation>
</a-timeline>
```

\+ Add the following lines of code under <mark style="color:orange;">\<a-assets></mark> to:

* add the animation ***mixin*** element <mark style="color:orange;">\<a-mixin</mark> <mark style="color:red;">id</mark>=<mark style="color:blue;">"landeffect" ...</mark><mark style="color:orange;">></mark> which will be later used by the cursor ring object and it defines the following two animations:\\
  * ***cursorbegin*** to make the cursor visible
  * ***cursorbegin*** to scale up the cursor object

```html
<a-mixin id="landeffect"
 animation__cursorbegin="property: visible; from: false; to: true; dur: 1; autoplay: false"
 animation__cursorend="property: scale; to: 0.75 0.75 0.75; dur: 2000; easing: easeInOutElastic; autoplay: false"
 scale="0.0001 0.0001 0.0001"
 position="0 0 0"
 visible="false"></a-mixin>
```

* \+ Add the following attribute to:
* Add an animation-timeline attribute to A-Frame's <mark style="color:orange;">\<a-scene</mark> <mark style="color:red;">webar-scene</mark><mark style="color:orange;">></mark> to link the landingTimeline element defined above

```html
animation-timeline__1="timeline: #landingTimeline; loop: false; startEvents: landit;"
```

* \+ Add the following line of code under <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark> to:
* Define the cursor ring object <mark style="color:orange;">\<a-ring</mark> *l<mark style="color:red;">andingcursor</mark>* <mark style="color:red;">id</mark>=<mark style="color:blue;">"landing"</mark> <mark style="color:red;">mixin</mark>=<mark style="color:blue;">"landeffect" ...</mark>> which will be animated to scale up when a surface is detected

```html
<a-ring landingcursor id="landing" mixin="landeffect" color="#FBB42C" material="opacity: 0.4; transparent: true" radius-inner="0.8" radius-outer="1" rotation="-90 0 0" segments-phi=30 segments-theta=96></a-ring>
```

## **Step 5: Add Portal Entry Logic** <a href="#h_01fhdhbg990yjm6aefr64dq44r" id="h_01fhdhbg990yjm6aefr64dq44r"></a>

* \+ Add the following lines of code under <mark style="color:orange;">\<script></mark> tag which is before the <mark style="color:orange;">\<a-scene</mark> <mark style="color:red;">webar-scene</mark><mark style="color:orange;">></mark> element:
* To get a callback using *<mark style="color:orange;">WEBARSDK.SetPortalEntryCallback()</mark>*, when the user goes closer to the Door which is at the origin of the detected surface i.e <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark>
* Once the *Portal Entry* *callbackFunction* is invoked, the *<mark style="color:red;">WEBARSDK.DisableTracking</mark>(<mark style="color:blue;">true</mark>)* API is called to disable surface tracking and enable the gyro camera which is when the user's mobile phone camera is virtually inside the Room sphere model.

```javascript
let closenessRatio = 0.25;

WEBARSDK.SetPortalEntryCallback(() => {
  console.log("Entered the portal door");

 let enableGyroCam = true;
  WEBARSDK.DisableTracking(enableGyroCam);

  // Position to the center of 360 view
  // Its a one way portal. No going back to the real world
  portalObject.object3D.position.set(0, 0, 3.0);
  portalHiderObject.object3D.position.set(0, 0, 3.0);
  console.log("Enabled gyro cam");
},
closenessRatio);
```

## **Step 6: Hide Portal Backside** <a href="#h_01fhdhaw5cqrdzhvdcybebh7ns" id="h_01fhdhaw5cqrdzhvdcybebh7ns"></a>

* \+ Add *<mark style="color:red;">hider-material</mark>* attribute to <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">gltf-model</mark>=<mark style="color:blue;">"#portalhider" ...</mark><mark style="color:orange;">></mark> as in the below code:

```html
<a-entity gltf-model="#portalhider" id="portal_1" rotation="0 0 0" position="0 0 0" scale="3.0 3.0 3.0" webar-loadmonitor="elType: glb"
hider-material></a-entity>
```

* \+ Add the following lines of code under \<script> tag which is before the \<a-scene webar-scene> element to:
* Define a custom A-Frame component which makes the Hider model and the object that is appearing behind this Hider model invisible.
* Enable this by setting the material's *colorWrite* to *false*.

```javascript
AFRAME.registerComponent("hider-material", {
  init: function () {
    this.el.addEventListener('model-loaded', this.update.bind(this));
  },
  update: function () {
    this.el.object3D.traverse(function (child) {
      if (child.isMesh) {
        child.material.colorWrite = false;
      }
    });
  }
});
```

<figure><img src="https://content.gitbook.com/content/6tbyCGI9hWqlzoZSPewE/blobs/3VKRdrhCjttKmsPAtZZm/Portal_Hide_Backside.gif" alt=""><figcaption><p>Hide Portal Backside</p></figcaption></figure>

## **Step 7: Start the HTTPS Server** <a href="#h_01fhdhbqbcfa6cpy58as6dbzbd" id="h_01fhdhbqbcfa6cpy58as6dbzbd"></a>

* Configure the Live Server VSC Extension settings.json to setup a self-signed HTTPS server.
* For more details, refer the documentation on [Develop Locally.](https://docs.blippar.com/webar-sdk/v1.7.3/publish-your-creation/develop-locally)
* Click Go Live to start the HTTPS Server
* Load the HTTPS server url in your mobile browser to start the WebAR portal experience

## **Step 8: Full Source Code** <a href="#h_01fhsmdcf5ttqa3n58ynf47rp6" id="h_01fhsmdcf5ttqa3n58ynf47rp6"></a>

The full source code of this Portal WebAR example is available in the [Github repo here.](https://github.com/blippar/webar-sdk-example/tree/main/aframe/portal-webar)

## **Resulting Portal WebAR Experience** <a href="#h_01fhdhbxr43xnrtq5nc20t9he0" id="h_01fhdhbxr43xnrtq5nc20t9he0"></a>

<figure><img src="https://content.gitbook.com/content/6tbyCGI9hWqlzoZSPewE/blobs/QpZeWWWbYQbAJvdQwqFP/final_616dbf2d3575b4009b53a5aa_65801%20(1).gif" alt=""><figcaption><p>Resulting Portal WebAR Experience</p></figcaption></figure>
