Virtual reality cardboard messenger - Part 1


This project aims at developing inexpensive virtual reality (VR) text messages for everyone on a click of a trigger. The 3ds file which is a part of animation has played a major role as it is the image file which is been used. This project enables users to know about the use of VR Cardboard and its functioning.
Notes-This epic work appeared due to several events. First, in the Android Emulator appeared support for hardware video acceleration, allowing full-rate work, not only the interface, but also test programs that use OpenGL ES 2.0. Second, approaching birthday beloved wife, and the best complement to a new smartphone or tablet will personally written program card for him. Said - done: create the outline of the project on android Tutorial'u , We reach the shelf old Direct3D-projects using file upload .3ds, render-to-texture and pack shaders rewritten in Java and OpenGL ES 2.0, we obtain that in the picture. Greeting text and the like add later. All the information on the use of OpenGL ES 2.0 for Android was highly fragmented, knowledge gathered bit by bit ... I hope this post will help those who in the future will face the same problems as me. And now more. 
How to use the above functionality for end users?
The following steps involved in the use of VR application for text message and rendering .3ds file functionality are listed below:
Step 1: Start the application on your mobile devices.
Step 2: The Application will ask the user to put the device into the cardboard.
Step 3: Pull the magnet to see the text appearing on the image.
Step 4: You will find the text message getting showcased and vice-versa on pulling the magnet each time.
How to develop this application on Eclipse?
It is very simple for developers to make this application go live. Follow the below given steps to achieve your goal:
Step 1: Click on New in the File Menu Bar.
Step 2: Go to the Android Application Project in the drop down.
Step 3: A pop-up will appear showing the Application Name in which you have to enter VR Messenger.Similarly, enter the project name as VR Messenger, you can also keep a different project name.Similarly, enter the package name as com.message.postcard, this also you can keep with a different package name.
Step 4: The Minimum Required SDK should be API 16: Android 4.1 (Jelly Bean), target SDK you can keep  as much as you want.


Step 5: Click the Next Button thrice and Choose Blank Activity, again click on Next and enter Activity Name as VRActivity.
Step 6: Enter Layout Name as main_activity and click on Finish button.


Step 7: Now, download the cardboard.jar
Step 8: Go to the manifest file to give following permissions:
Ø  <uses-sdk android:minSdkVersion="16"/> indicates that the device must be running API Level 16 (Jellybean) or higher.
Ø  <uses-sdk android:targetSdkVersion="19"/> indicates our app is targetting API Level 19 (KitKat).
Ø  <uses-feature android:glEsVersion="0x00020000" android:required="true" /> indicates that the device must support OpenGL ES 2.0 to run the demo app.
Ø  android:screenOrientation="landscape" indicates that the activity's required screen orientation is "landscape." This is the orientation you must set for VR apps. The view used by the Cardboard SDK, CardboardView, only renders on fullscreen and landscape (landscapereverseLandscapesensorLandscape) modes.
Ø  The setting android:configChanges="orientation|keyboardHidden" is also recommended, but not mandatory.
Ø  android.permission.NFC permission is required by the Cardboard SDK to access Cardboard's NFC tag.
Ø android.permission.READ_EXTERNAL_STORAGE and android.permission.WRITE_EXTERNAL_STORAGE. These permissions are required by the Cardboard SDK to pair the user's phone to their VR viewer.
Ø  android.permission.VIBRATE permission is required by our demo app to make the phone vibrate to inform the user that something has happened.
Here is the demo showcase of permissions file –
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.message.postcard"
     android:versionCode="1"
    android:versionName="1.0" >
  <uses-permission android:name="android.permission.NFC" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-sdk android:minSdkVersion="16"/>
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
         <activity
            android:name="com.message.postcard.VRActivity"
            android:screenOrientation="landscape"
            android:configChanges="orientation|keyboardHidden"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 </manifest>
Step 9: Model- The word "model" here I mean here is this very rosettes. In fact, you can use any object or the whole scene, it does not matter.It is understood that all further code is located in the same class inherited from class Renderer.
Load Model- Boot code file .3ds I give here will not: long, and the post is not about that (in principle, it is worthy of a separate post), but the rendering code model are given, because, firstly, very much I rake collected on the way, and secondly, it includes almost all of the calls gl , thirdly, some functions need below. However, if you are interested in only the implementation of the effect can skip this section. So, in the end, all these models are kept within such structures: The Code for the Scene3D class is as follows: 
package com.message.postcard;

import java.util.ArrayList;

import android.opengl.Matrix;

public class Scene3D {
       public ArrayList<Material3D> materials;
       public ArrayList<Object3D> objects;
       public ArrayList<Light3D> lights;
       public ArrayList<Animation> animations;
       public float[] background;
       public float[] ambient;

       public Material3D FindMaterial(String name)
       {
              if (materials == null || name == null) return null;
              int i, n = materials.size();
              for (i = 0; i < n; i++) {
                     Material3D mat = materials.get(i);
                     if (mat.name.equals(name))
                           return mat;
              }
              return null;
       }

       public Object3D FindObject(String name)
       {
              if (objects == null || name == null) return null;
              int i, n = objects.size();
              for (i = 0; i < n; i++) {
                     Object3D obj = objects.get(i);
                     if (obj.name.equals(name))
                           return obj;
              }
              return null;
       }

       public Light3D FindLight(String name)
       {
              if (lights == null || name == null) return null;
              int i, n = lights.size();
              for (i = 0; i < n; i++) {
                     Light3D light = lights.get(i);
                     if (light.name.equals(name))
                           return light;
              }
              return null;
       }

       public Animation FindAnimation(int id)
       {
              if (animations == null || id == 0xffff) return null;
              int i, n = animations.size();
              for (i = 0; i < n; i++) {
                     Animation anim = animations.get(i);
                     if (anim.id == id)
                           return anim;
              }
              return null;
       }

       private void lerp3(float[] out, float[] from, float[] to, float t)
       {
              for (int i = 0; i < 3; i++)
                     out[i] = from[i] + (to[i] - from[i]) * t;
       }

       private AnimKey findVec(AnimKey[] keys, float time)
       {
              AnimKey key = keys[keys.length - 1];

              // We'll use either first, or last, or interpolated key
              for (int j = 0; j < keys.length; j++) {
                     if (keys[j].time >= time) {
                           if (j > 0) {
                                  float local = (time - keys[j - 1].time) /
                                         (keys[j].time - keys[j - 1].time);
                                  key = new AnimKey();
                                  key.time = time;
                                  key.data = new float[3];
                                  lerp3(key.data, keys[j - 1].data, keys[j].data, local);
                           }
                           else
                                  key = keys[j];
                           break;
                     }
              }

              return key;
       }

       private void applyRot(float[] result, float[] data, float t)
       {
              if (Math.abs(data[3]) > 1.0e-7 && Math.hypot(Math.hypot(data[0], data[1]), data[2]) > 1.0e-7)
                     Matrix.rotateM(result, 0, (float) (data[3] * t * 180 / Math.PI), data[0], data[1], data[2]);
       }

       public void Compute(float time)
       {
              int i, n = animations.size();
              for (i = 0; i < n; i++) {
                     Animation anim = animations.get(i);
                     Object3D obj = anim.object;
                     float[] result = new float[16];

                     Matrix.setIdentityM(result, 0);

                     if (anim.position != null && anim.position.length > 0) {
                           AnimKey key = findVec(anim.position, time);
                           float[] pos = key.data;
                           Matrix.translateM(result, 0, pos[0], pos[1], pos[2]);
                     }

                     if (anim.rotation != null && anim.rotation.length > 0) {
                           // All rotations that are prior to the target time should be applied sequentially
                           for (int j = anim.rotation.length - 1; j > 0; j--) {
                                  if (time >= anim.rotation[j].time) // rotation in the past, apply as is
                                         applyRot(result, anim.rotation[j].data, 1);
                                  else if (time > anim.rotation[j - 1].time) {
                                         // rotation between key frames, apply part of it
                                         float local = (time - anim.rotation[j - 1].time) /
                                                       (anim.rotation[j].time - anim.rotation[j - 1].time);
                                         applyRot(result, anim.rotation[j].data, local);
                                  }
                                  // otherwise, it's a rotation in the future, skip it
                           }

                           // Always apply the first rotation
                           applyRot(result, anim.rotation[0].data, 1);
                     }

                     if (anim.scaling != null && anim.scaling.length > 0) {
                           AnimKey key = findVec(anim.scaling, time);
                           float[] scale = key.data;
                           Matrix.scaleM(result, 0, scale[0], scale[1], scale[2]);
                     }

                     if (anim.parent != null)
                           Matrix.multiplyMM(anim.result, 0, anim.parent.result, 0, result, 0);
                     else
                           Matrix.translateM(anim.result, 0, result, 0, 0, 0, 0);

                     if (obj != null && obj.trMatrix != null) {
                           float[] pivot = new float[16];
                           Matrix.setIdentityM(pivot, 0);
                           Matrix.translateM(pivot, 0, -anim.pivot[0], -anim.pivot[1], -anim.pivot[2]);
                           Matrix.multiplyMM(result, 0, pivot, 0, obj.trMatrix, 0);
                     }
                     else {
                           Matrix.setIdentityM(result, 0);
                           Matrix.translateM(result, 0, -anim.pivot[0], -anim.pivot[1], -anim.pivot[2]);
                     }
                     Matrix.multiplyMM(anim.world, 0, anim.result, 0, result, 0);
              }
       }
}

class Object3D {
       public String name;
       public int vertCount;
       public int indCount;
       public ArrayList<FaceMat> faceMats;
       public int glVertices;
       public int glIndices;
       public float[] vertexBuffer;
       public float[] trMatrix;
}

class FaceMat {
       public Material3D material;
       public short[] indexBuffer;
       public int indCount;
       public int bufOffset;
}

class Light3D {
       public String name;
       public float[] pos;
       public float[] color;
       public float[] dir;
       public float theta, phi;
}

class Material3D {
       public String name;
       public float[] ambient;
       public float[] diffuse;
       public float[] specular;
       public String texture;
       public float shininess;
       public float shinStren;
       public float transparency;
       public float selfIllum;
       public int type;
}

class Animation {
       public int id;
       public String name;
       public Object3D object;
       public Light3D light;
       public Animation parent;
       public float[] pivot;

       public AnimKey[] position;
       public AnimKey[] rotation;
       public AnimKey[] scaling;

       public float[] result;
       public float[] world;
}

class AnimKey {
       public float time;
       public float[] data;

}
Step 10: Create the Load3DS class à
The Load3DS class is made for the .3ds file whose code is as follows:
package com.message.postcard;

import java.io.*;
import java.util.ArrayList;

import android.opengl.Matrix;
import android.util.FloatMath;
import android.util.Log;

class Load3DS {
       private final int CHUNK_MAIN     = 0x4D4D;
       private final int CHUNK_OBJMESH  = 0x3D3D;
       private final int CHUNK_OBJBLOCK = 0x4000;
       private final int CHUNK_TRIMESH  = 0x4100;
       private final int CHUNK_VERTLIST = 0x4110;
       private final int CHUNK_FACELIST = 0x4120;
       private final int CHUNK_FACEMAT  = 0x4130;
       private final int CHUNK_MAPLIST  = 0x4140;
       private final int CHUNK_SMOOTHG  = 0x4150;
       private final int CHUNK_TRMATRIX = 0x4160;
       private final int CHUNK_LIGHT    = 0x4600;
       private final int CHUNK_SPOTL    = 0x4610;
       private final int CHUNK_ONOFF    = 0x4620;
       private final int CHUNK_CAMERA   = 0x4700;
       private final int CHUNK_RGBC     = 0x0010;
       private final int CHUNK_RGB24    = 0x0011;
       private final int CHUNK_SHORT    = 0x0030;
       private final int CHUNK_BACKCOL  = 0x1200;
       private final int CHUNK_AMB      = 0x2100;
       private final int CHUNK_MATERIAL = 0xAFFF;
       private final int CHUNK_MATNAME  = 0xA000;
       private final int CHUNK_AMBIENT  = 0xA010;
       private final int CHUNK_DIFFUSE  = 0xA020;
       private final int CHUNK_SPECULAR = 0xA030;
       private final int CHUNK_SHININES = 0xA040;
       private final int CHUNK_SHINSTRN = 0xA041;
       private final int CHUNK_TRANSP   = 0xA050;
       private final int CHUNK_SELFILL  = 0xA084;
       private final int CHUNK_MTLTYPE  = 0xA100;
       private final int CHUNK_TEXTURE  = 0xA200;
       private final int CHUNK_REFLMAP  = 0xA220;
       private final int CHUNK_BUMPMAP  = 0xA230;
       private final int CHUNK_MAPFILE  = 0xA300;
       private final int CHUNK_MAPPARAM = 0xA351;
       private final int CHUNK_KEYFRAMER = 0xB000;
       private final int CHUNK_TRACKINFO = 0xB002;
       private final int CHUNK_SPOTINFO  = 0xB007;
       private final int CHUNK_FRAMES    = 0xB008;
       private final int CHUNK_OBJNAME   = 0xB010;
       private final int CHUNK_PIVOT     = 0xB013;
       private final int CHUNK_TRACKPOS  = 0xB020;
       private final int CHUNK_TRACKROT  = 0xB021;
       private final int CHUNK_TRACKSCL  = 0xB022;
       private final int CHUNK_HIERARCHY = 0xB030;

       private BufferedInputStream file;
       private final byte[] bytes = new byte[8];
       private long filePos;

       public Scene3D Load(InputStream stream)
       {
              file = null;
              Scene3D scene = null;

              try {
                     filePos = 0;
                     file = new BufferedInputStream(stream);
                     scene = ProcessFile(stream.available());
              } catch (FileNotFoundException e) {
                     e.printStackTrace();
              } catch (IOException e) {
                     e.printStackTrace();
              }

              try {
                     if (file != null)
                           file.close();
              } catch (IOException e) {
                     e.printStackTrace();
              }

              return scene;
       }

       private void Skip(long count) throws IOException
       {
              file.skip(count);
              filePos += count;
       }

       private void Seek(long end) throws IOException
       {
              if (filePos < end) {
                     Skip(end - filePos);
                     filePos = end;
              }
       }

       private byte ReadByte() throws IOException
       {
              file.read(bytes, 0, 1);
              filePos++;
              return bytes[0];
       }

       private int ReadUnsignedByte() throws IOException
       {
              file.read(bytes, 0, 1);
              filePos++;
              return (bytes[0]&0xff);
       }

       private int ReadUnsignedShort() throws IOException
       {
              file.read(bytes, 0, 2);
              filePos += 2;
              return ((bytes[1]&0xff) << 8 | (bytes[0]&0xff));
       }

       private int ReadInt() throws IOException
       {
              file.read(bytes, 0, 4);
              filePos += 4;
              return (bytes[3]) << 24 | (bytes[2]&0xff) << 16 | (bytes[1]&0xff) <<  8 | (bytes[0]&0xff);
       }

       private float ReadFloat() throws IOException
       {
              return Float.intBitsToFloat(ReadInt());
       }

       private Scene3D ProcessFile(long fileLen) throws IOException
       {
              Scene3D scene = null;

              while (filePos < fileLen) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_MAIN:
                           if (scene == null)
                                  scene = ChunkMain(chunkLen);
                           else
                                  Skip(chunkLen);
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }

              return scene;
       }

       private Scene3D ChunkMain(int len) throws IOException
       {
              Scene3D scene = new Scene3D();
              scene.materials = new ArrayList<Material3D>();
              scene.objects = new ArrayList<Object3D>();
              scene.lights = new ArrayList<Light3D>();
              scene.animations = new ArrayList<Animation>();

              long end = filePos + len;
              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_OBJMESH:
                           Chunk3DEditor(scene, chunkLen);
                           break;

                     case CHUNK_KEYFRAMER:
                           ChunkKeyframer(scene, chunkLen);
                           break;

                     case CHUNK_BACKCOL:
                           scene.background = new float[4];
                           ChunkColor(chunkLen, scene.background);
                           break;

                     case CHUNK_AMB:
                           scene.ambient = new float[4];
                           ChunkColor(chunkLen, scene.ambient);
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              scene.Compute(0);

              return scene;
       }

       private void Chunk3DEditor(Scene3D scene, int len) throws IOException
       {
              long end = filePos + len;
              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_OBJBLOCK:
                           ChunkObject(scene, chunkLen);
                           break;

                     case CHUNK_MATERIAL:
                           Material3D mat = ChunkMaterial(chunkLen);
                           if (mat != null)
                                  scene.materials.add(mat);
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);
       }

       private void ChunkObject(Scene3D scene, int len) throws IOException
       {
              long end = filePos + len;

              if (len == 0) return;
              String name = ChunkName(0);

              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_TRIMESH:
                           Object3D obj = ChunkTrimesh(chunkLen, name, scene);
                           if (obj != null)
                                  scene.objects.add(obj);
                           break;

                     case CHUNK_LIGHT:
                           Light3D light = ChunkLight(chunkLen, name);
                           if (light != null)
                                  scene.lights.add(light);
                           break;

                     case CHUNK_CAMERA:
                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);
       }

       private Object3D ChunkTrimesh(int len, String name, Scene3D scene) throws IOException
       {
              long end = filePos + len;

              Object3D obj = new Object3D();
              obj.name = name;
              obj.faceMats = new ArrayList<FaceMat>();
              obj.indCount = 0;

              int i, k, num;

              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_FACELIST:
                           ChunkFaceList(chunkLen, obj, scene);
                           break;

                     case CHUNK_MAPLIST:
                           num = ReadUnsignedShort();
                           for (i = 0, k = 6; i < num; i++, k += 8) {
                                  obj.vertexBuffer[k + 0] = ReadFloat();
                                  obj.vertexBuffer[k + 1] = 1 - ReadFloat();
                           }
                           break;

                     case CHUNK_VERTLIST:
                           num = ReadUnsignedShort();
                           obj.vertCount = num;
                           obj.vertexBuffer = new float[8*num];
                           for (i = 0, k = 0; i < num; i++, k += 8) {
                                  ChunkVector(obj.vertexBuffer, k);
                                  obj.vertexBuffer[k + 3] = 0;
                                  obj.vertexBuffer[k + 4] = 0;
                                  obj.vertexBuffer[k + 5] = 0;
                                  obj.vertexBuffer[k + 6] = 0;
                                  obj.vertexBuffer[k + 7] = 0;
                           }
                           break;

                     case CHUNK_TRMATRIX:
                           float[] localCoord = new float[16];
                           ChunkVector(localCoord, 4*0);
                           ChunkVector(localCoord, 4*2);
                           ChunkVector(localCoord, 4*1);
                           ChunkVector(localCoord, 4*3);
                           localCoord[3] = localCoord[7] = localCoord[11] = 0;
                           localCoord[15] = 1;

                           obj.trMatrix = new float[16];
                           Matrix.invertM(obj.trMatrix, 0, localCoord, 0);
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              return obj;
       }

       private static void CrossProduct(float[] res, float[] v1, float[] v2)
       {
              res[0] = v1[1]*v2[2] - v1[2]*v2[1];
              res[1] = v1[2]*v2[0] - v1[0]*v2[2];
              res[2] = v1[0]*v2[1] - v1[1]*v2[0];
       }

       private static float DotSquare(float[] v, int offset)
       {
              return v[offset + 0]*v[offset + 0] + v[offset + 1]*v[offset + 1] + v[offset + 2]*v[offset + 2];
       }

       private static void VecSubstract(float[] res, float[] v, int offset1, int offset2)
       {
              res[0] = v[offset1 + 0] - v[offset2 + 0];
              res[1] = v[offset1 + 1] - v[offset2 + 1];
              res[2] = v[offset1 + 2] - v[offset2 + 2];
       }

       private static void VecAdd(float[] v, int offset, float[] a, int off)
       {
              v[offset + 0] += a[off + 0];
              v[offset + 1] += a[off + 1];
              v[offset + 2] += a[off + 2];
       }

       private void VecNormalize(float[] v, int offset)
       {
              double nlen = 1 / FloatMath.sqrt(DotSquare(v, offset));
              v[offset + 0] *= nlen;
              v[offset + 1] *= nlen;
              v[offset + 2] *= nlen;
       }

       private void ChunkFaceList(int len, Object3D obj, Scene3D scene) throws IOException
       {
              long end = filePos + len;

              int i, j, k, l, m, t, num = ReadUnsignedShort(), unused = num, idx;

              int faceCount = num;
              int[] faceBuffer = new int[3*faceCount];
              boolean[] faceUsed = new boolean[faceCount];
              float[] v = new float[3];
              float[] v1 = new float[3];
              float[] v2 = new float[3];

              float[] vgNorm = new float[3*3*faceCount]; // per-vertex normal for each face
              int[] vertGroup = new int[3*faceCount]; // per-vertex group for each face
              boolean[] vgUsed = new boolean[3*faceCount]; // per-vertex "group used" bit for each face
              int[] vgNum = new int[obj.vertCount + 1]; // per-vertex face count, and then offset
              int[] faceGroup = new int[faceCount]; // per-face smoothing group

              int[] vgUniqs = new int[obj.vertCount + 1]; // per-vertex unique groups count
              int[] vertUGroup = new int[3*faceCount]; // per-vertex unique groups list for each face

              for (i = 0; i <= obj.vertCount; i++)
                     vgNum[i] = vgUniqs[i] = 0;

              for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
                     j = ReadUnsignedShort();
                     k = ReadUnsignedShort();
                     l = ReadUnsignedShort();
                     Skip(2);

                     faceUsed[i] = false;
                     faceBuffer[idx + 0] = j;
                     faceBuffer[idx + 2] = k;
                     faceBuffer[idx + 1] = l;

                     // initialize smoothing groups data
                     faceGroup[i] = 0;

                     for (t = 0; t < 9; t++)
                           vgNorm[i * 9 + t] = 0;

                     for (t = 0; t < 3; t++) {
                           vertGroup[idx + t] = 0;
                           vertUGroup[idx + t] = 0;
                           vgUsed[idx + t] = false;
                     }

                     vgNum[j]++;
                     vgNum[k]++;
                     vgNum[l]++;
              }

              int a, sum = 0;
              for (i = 0; i < obj.vertCount; i++) {
                     a = vgNum[i];
                     vgNum[i] = sum;
                     sum += a;
              }
              vgNum[obj.vertCount] = sum; // now we can store all the faces and their normals and groups per-vertex

              for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
                     j = faceBuffer[idx + 0];
                     k = faceBuffer[idx + 2];
                     l = faceBuffer[idx + 1];

                     VecSubstract(v1, obj.vertexBuffer, l*8, j*8);
                     VecSubstract(v2, obj.vertexBuffer, k*8, j*8);
                     CrossProduct(v, v1, v2);

                     VecAdd(vgNorm, vgNum[j]*3, v, 0);
                     VecAdd(vgNorm, vgNum[k]*3, v, 0);
                     VecAdd(vgNorm, vgNum[l]*3, v, 0);
                    
                     vgNum[j]++;
                     vgNum[k]++;
                     vgNum[l]++;
              }

              for (i = obj.vertCount - 1; i > 0; i--) // offsets were shifted, so shift them back
                     vgNum[i] = vgNum[i - 1];
              vgNum[0] = 0;

              boolean gotSmoothGroups = false;

              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_FACEMAT:
                           FaceMat mat = new FaceMat();
                           String name = ChunkName(0);
                           mat.material = scene.FindMaterial(name);
                           num = ReadUnsignedShort();
                           mat.indCount = num;
                           mat.indexBuffer = new short[3*num];
                           mat.bufOffset = obj.indCount;
                           obj.indCount += 3*num;
                           k = 0;
                           for (i = 0; i < num; i++) {
                                  j = ReadUnsignedShort();
                                  if (!faceUsed[j]) {
                                         faceUsed[j] = true;
                                         unused--;
                                  }
                                  for (t = 0; t < 3; t++)
                                         mat.indexBuffer[k++] = (short) j;
                           }
                           obj.faceMats.add(mat);
                           break;

                     case CHUNK_SMOOTHG:
                           for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
                                  faceGroup[i] = ReadInt();
                                 
                                  for (t = 0; t < 3; t++) {
                                         j = faceBuffer[idx + t];
                                         vertGroup[vgNum[j]] = faceGroup[i];
                                         vgNum[j]++;
                                  }
                           }
                           for (i = obj.vertCount - 1; i > 0; i--)
                                  vgNum[i] = vgNum[i - 1];
                           vgNum[0] = 0;
                           gotSmoothGroups = true;
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              int newVertCount = 0, g;

              if (gotSmoothGroups) {
                     for (i = 0; i < obj.vertCount; i++) {
                           for (m = vgNum[i]; m < vgNum[i + 1]; m++) { // for every normal and face of this vertex
                                  if (!vgUsed[m]) {
                                         // vertGroup[m] is a new group...
                                         vertUGroup[vgNum[i] + vgUniqs[i]] = vertGroup[m];
                                         vgUniqs[i]++;
                                         newVertCount++;
                                  }
      
                                  for (t = m; t < vgNum[i + 1]; t++) // mark all equal groups (including this one) as duplicates
                                         if (vertGroup[t] == vertGroup[m])
                                                vgUsed[t] = true;
                           }
                     }

                     if (newVertCount == obj.vertCount)
                           gotSmoothGroups = false;
              }

              if (gotSmoothGroups) {
                     // reindex all vertices, build new normals
                     int newIndex = 0;
                     int[] vertIndex = new int[faceCount*3]; // new vertex indices
                     float[] newVertexBuffer = new float[newVertCount*8];

                     for (i = 0, idx = 0; i < faceCount; i++, idx += 3)
                           for (t = 0; t < 3; t++)
                                  vertIndex[idx + t] = 0;

                     idx = 3;
                     for (i = 0; i < obj.vertCount; i++) {
                           for (t = 0; t < vgUniqs[i]; t++) { // for every unique normal and face of this vertex
                                  for (m = 0; m < 8; m++)
                                         newVertexBuffer[newIndex*8 + m] = obj.vertexBuffer[i*8 + m]; // duplicate all vertex data (including zero normals)

                                  g = vertUGroup[vgNum[i] + t]; // unique group mask for this vertex
                                  for (m = vgNum[i]; m < vgNum[i + 1]; m++) // for every NON-unique normal and face of this vertex
                                         if ((vertGroup[m] & g) != 0 || vertGroup[m] == g) // also works for zero group
                                                VecAdd(newVertexBuffer, idx, vgNorm, m*3); // add normal to vertex
      
                                  vertIndex[vgNum[i] + t] = newIndex;
                                  newIndex++;
                                  idx += 8;
                           }
                     }

                     int fg, vi;
                     for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
                           fg = faceGroup[i];
                           for (m = 0; m < 3; m++) {
                                  vi = faceBuffer[idx + m]; // face vertex
                                  for (t = 0; t < vgUniqs[vi]; t++) // for every unique group of this vertex
                                         if (fg == vertUGroup[vgNum[vi] + t]) { // found the right one
                                                faceBuffer[idx + m] = vertIndex[vgNum[vi] + t];
                                                break;
                                         }
                           }
                     }

                     Log.i("Load3DS", String.format("Resized object %s from %d to %d vertices", obj.name, obj.vertCount, newVertCount));
                     obj.vertCount = newVertCount;
                     obj.vertexBuffer = newVertexBuffer;
              }
              else // nothing changed, no need to recalculate anything
                     for (i = 0, idx = 3; i < obj.vertCount; i++, idx += 8) // just copy all the normals
                           for (m = vgNum[i]; m < vgNum[i + 1]; m++) // for every NON-unique normal and face of this vertex
                                  VecAdd(obj.vertexBuffer, idx, vgNorm, m*3); // add normal to vertex

              for (i = 0, k = 3; i < obj.vertCount; i++, k += 8)
                     VecNormalize(obj.vertexBuffer, k);

              for (m = 0; m < obj.faceMats.size(); m++) {
                     FaceMat mat = obj.faceMats.get(m);
                     k = 0;
                     for (i = 0; i < mat.indCount; i++)
                           for (t = 0; t < 3; t++) {
                                  j = 3 * (int) mat.indexBuffer[k];
                                  mat.indexBuffer[k++] = (short) faceBuffer[j + t];
                           }
              }

              if (unused > 0) {
                     FaceMat mat = new FaceMat();
                     mat.indexBuffer = new short[3*unused];
                     mat.bufOffset = obj.indCount;
                     obj.indCount += 3*unused;
                     k = 0;
                     for (i = 0; i < faceCount; i++)
                           if (!faceUsed[i]) {
                                  faceUsed[i] = true;
                                  j = i * 3;
                                  for (t = 0; t < 3; t++)
                                         mat.indexBuffer[k++] = (short) faceBuffer[j + t];
                           }
                     obj.faceMats.add(mat);
              }
       }

       private Light3D ChunkLight(int len, String name) throws IOException
       {
              long end = filePos + len;

              Light3D light = new Light3D();
              light.name = name;
              light.pos = new float[3];
              ChunkVector(light.pos, 0);

              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_RGBC:
                           light.color = new float[4];
                           ChunkRGBC(light.color);
                           break;

                     case CHUNK_RGB24:
                           light.color = new float[4];
                           ChunkRGB24(light.color);
                           break;

                     case CHUNK_SPOTL:
                           light.dir = new float[4];
                           ChunkVector(light.dir, 0);
                           light.theta = (float) (ReadFloat() * Math.PI / 180.0f);
                           light.phi = (float) (ReadFloat() * Math.PI / 180.0f);
                           break;

                     case CHUNK_ONOFF:
                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              return light;
       }

       private Material3D ChunkMaterial(int len) throws IOException
       {
              long end = filePos + len;

              Material3D mat = new Material3D();

              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_TEXTURE:
                           mat.texture = ChunkMap(chunkLen);
                           break;

                     case CHUNK_BUMPMAP:
                     case CHUNK_REFLMAP:
                           ChunkMap(chunkLen);
                           break;

                     case CHUNK_AMBIENT:
                           mat.ambient = new float[4];
                           ChunkColor(chunkLen, mat.ambient);
                           break;

                     case CHUNK_DIFFUSE:
                           mat.diffuse = new float[4];
                           ChunkColor(chunkLen, mat.diffuse);
                           break;

                     case CHUNK_SPECULAR:
                           mat.specular = new float[4];
                           ChunkColor(chunkLen, mat.specular);
                           break;

                     case CHUNK_MATNAME:
                           mat.name = ChunkName(chunkLen);
                           break;

                     case CHUNK_MTLTYPE:
                           mat.type = ReadUnsignedShort();
                           break;

                     case CHUNK_SHININES:
                           mat.shininess = 100 - ChunkPercent(chunkLen);
                           break;

                     case CHUNK_SHINSTRN:
                           mat.shinStren = ChunkPercent(chunkLen);
                           break;

                     case CHUNK_TRANSP:
                           mat.transparency = ChunkPercent(chunkLen);
                           break;

                     case CHUNK_SELFILL:
                           mat.selfIllum = ChunkPercent(chunkLen);
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              return mat;
       }

       private String ChunkMap(int len) throws IOException
       {
              long end = filePos + len;

              String name = null;

              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_MAPFILE:
                           name = ChunkName(chunkLen);
                           break;

                     case CHUNK_MAPPARAM:
                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              return name;
       }

       private void ChunkKeyframer(Scene3D scene, int len) throws IOException
       {
              int fstart = 0, fend = 100;

              long end = filePos + len;
              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_FRAMES:
                           fstart = ReadInt();
                           fend = ReadInt();
                           break;

                     case CHUNK_TRACKINFO:
                           Animation anim = ChunkMeshTrack(chunkLen, scene);
                           if (anim != null)
                                  scene.animations.add(anim);
                           break;

                     case CHUNK_SPOTINFO:
                     default:
                           Skip(chunkLen);
                     }
              }

              if (fstart < fend)
                     for (int i = 0; i < scene.animations.size(); i++) {
                           Animation anim = scene.animations.get(i);
                           if (anim.position != null)
                                  for (int j = 0; j < anim.position.length; j++)
                                         anim.position[j].time = (anim.position[j].time - fstart) / (fend - fstart);
                           if (anim.rotation != null)
                                  for (int j = 0; j < anim.rotation.length; j++)
                                         anim.rotation[j].time = (anim.rotation[j].time - fstart) / (fend - fstart);
                           if (anim.scaling != null)
                                  for (int j = 0; j < anim.scaling.length; j++)
                                         anim.scaling[j].time = (anim.scaling[j].time - fstart) / (fend - fstart);
                     }

              Seek(end);
       }

       private Animation ChunkMeshTrack(int len, Scene3D scene) throws IOException
       {
              Animation anim = new Animation();
              int num, i, j, k;

              anim.result = new float[16];
              Matrix.setIdentityM(anim.result, 0);

              anim.world = new float[16];
              Matrix.setIdentityM(anim.world, 0);

              long end = filePos + len;
              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_HIERARCHY:
                           anim.id = ReadUnsignedShort();
                           break;

                     case CHUNK_OBJNAME:
                           String name = ChunkName(0);
                           anim.light = scene.FindLight(name);
                           anim.object = scene.FindObject(name);
                           Skip(4);
                           anim.parent = scene.FindAnimation(ReadUnsignedShort());
                           break;

                     case CHUNK_PIVOT:
                           anim.pivot = new float[3];
                           ChunkVector(anim.pivot, 0);
                           break;

                     case CHUNK_TRACKPOS:
                           Skip(10);
                           num = ReadInt();
                           anim.position = new AnimKey[num];
                           for (i = 0; i < num; i++) {
                                  anim.position[i] = new AnimKey();
                                  anim.position[i].time = ReadInt();
                                  k = ReadUnsignedShort();
                                  for (j = 0; j < 5; j++)
                                         if ((k & (1 << j)) != 0)
                                                Skip(4);
                                  anim.position[i].data = new float[3];
                                  ChunkVector(anim.position[i].data, 0);
                           }
                           break;

                     case CHUNK_TRACKROT:
                           Skip(10);
                           num = ReadInt();
                           anim.rotation = new AnimKey[num];
                           for (i = 0; i < num; i++) {
                                  anim.rotation[i] = new AnimKey();
                                  anim.rotation[i].time = ReadInt();
                                  k = ReadUnsignedShort();
                                  for (j = 0; j < 5; j++)
                                         if ((k & (1 << j)) != 0)
                                                Skip(4);
                                  anim.rotation[i].data = new float[4];
                                  anim.rotation[i].data[3] = ReadFloat();
                                  ChunkVector(anim.rotation[i].data, 0);
                           }
                           break;

                     case CHUNK_TRACKSCL:
                           Skip(10);
                           num = ReadInt();
                           anim.scaling = new AnimKey[num];
                           for (i = 0; i < num; i++) {
                                  anim.scaling[i] = new AnimKey();
                                  anim.scaling[i].time = ReadInt();
                                  k = ReadUnsignedShort();
                                  for (j = 0; j < 5; j++)
                                         if ((k & (1 << j)) != 0)
                                                Skip(4);
                                  anim.scaling[i].data = new float[3];
                                  ChunkVector(anim.scaling[i].data, 0);
                           }
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              return anim;
       }

       private void ChunkColor(int len, float[] color) throws IOException
       {
              long end = filePos + len;
              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_RGBC:
                           ChunkRGBC(color);
                           break;

                     case CHUNK_RGB24:
                           ChunkRGB24(color);
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);
       }

       private float ChunkPercent(int len) throws IOException
       {
              float v = 0;

              long end = filePos + len;
              while (filePos < end) {
                     int chunkID = ReadUnsignedShort();
                     int chunkLen = ReadInt() - 6;

                     switch (chunkID) {
                     case CHUNK_SHORT:
                           v = ReadUnsignedShort() / 100.0f;
                           break;

                     default:
                           Skip(chunkLen);
                     }
              }
              Seek(end);

              return v;
       }

       private String ChunkName(int len) throws IOException
       {
              long end = filePos + len;
              int slen = 0;
              byte[] buffer = new byte[128];
              byte c;

              do {
                     c = ReadByte();
                     if (c != 0)
                           buffer[slen++] = c;
              } while (c != 0);

              if (len != 0)
                     Seek(end);

              return new String(buffer, 0, slen);
       }

       private void ChunkVector(float[] vec, int offset) throws IOException
       {
              vec[offset + 0] = ReadFloat();
              vec[offset + 2] = ReadFloat();
              vec[offset + 1] = ReadFloat();
       }

       private void ChunkRGBC(float[] c) throws IOException
       {
              c[0] = ReadFloat();
              c[1] = ReadFloat();
              c[2] = ReadFloat();
              c[3] = 1;
       }

       private void ChunkRGB24(float[] c) throws IOException
       {
              c[0] = ReadUnsignedByte() / 255.0f;
              c[1] = ReadUnsignedByte() / 255.0f;
              c[2] = ReadUnsignedByte() / 255.0f;
              c[3] = 1;
       }
}
Step 11: Open the class VRActivity as defined below:
CardboardActivity is the starting point for coding a cardboard app. CardboardActivity is the base activity that provides easy integration with Cardboard devices. It exposes events to interact with Cardboards and handles many of the details commonly required when creating an activity for VR rendering.
Note that CardboardActivity uses sticky immersive mode, in which the system UI is hidden, and the content takes up the whole screen. This is a requirement for a VR app, since CardboardView will only render when the activity is in fullscreen mode. See Using Immersive Full-Screen Mode for more discussion of this feature. The demo app's VRActivity extends CardboardActivity. VRActivity implements the following interface: 
CardboardView.StereoRenderer: Interface for renderers that delegate all stereoscopic rendering details to the view. Implementors should simply render a view as they would normally do using the provided transformation parameters. All stereoscopic rendering and distortion correction details are abstracted from the renderer and managed internally by the view. 
Step 12: Initializes the CardboardView in the onCreate() method:
/**
* Sets the view to our CardboardView and initializes the transformation matrices we will use
* to render our scene.
* @param savedInstanceState
*/
@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.common_ui);
   CardboardView cardboardView = (CardboardView) findViewById(R.id.cardboard_view);
   // Associate a CardboardView.StereoRenderer with cardboardView.
   cardboardView.setRenderer(this);
   // Associate the cardboardView with this activity.
   setCardboardView(cardboardView);

   // Initialize other objects here.

}

Once you get the CardboardView you associate it with a renderer, and then you associate the CardboardView with the activity.
To know more, click on the following link: Virtual reality cardboard messenger - Part 2

Delhi, INDIA

0 comments: