Skip to content

Commit

Permalink
Merge pull request #5 from PatriceVignola/add-android-support
Browse files Browse the repository at this point in the history
✨ Add Android support - with huge kudos @PatriceVignola
  • Loading branch information
rastapasta authored Jan 2, 2018
2 parents 5861f6b + 8a9a13e commit 518b97e
Show file tree
Hide file tree
Showing 45 changed files with 1,751 additions and 10 deletions.
58 changes: 51 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,34 @@
![license](https://img.shields.io/github/license/rastapasta/react-native-gl-model-view.svg)

A `<ModelView>` component for [react-native](https://github.com/facebook/react-native), allowing you to
display and animate any Wavefront .OBJ 3D object. Realized with a native bridge to [GLView](https://github.com/nicklockwood/GLView).
display and animate any Wavefront .OBJ 3D object. Realized with a native bridge to [GLView](https://github.com/nicklockwood/GLView) for iOS and a native bridge to [jPCT-AE](http://www.jpct.net/jpct-ae/) for Android.

<img src="docs/AnimatedAPI.gif" width="32%"/> <img src="docs/GestureResponder.gif" width="32%"/> <img src="docs/Multiple.gif" width="32%" />

Main features:

* Display, rotate, scale and translate textured 3D models!
* Animate with blasting fast 60 fps by using the [Animated API](https://facebook.github.io/react-native/docs/animations.html#using-the-native-driver) native driver
* Supports [Wavefront .OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file) and GLEssentials model formats
* Supports [Wavefront .OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file) and GLEssentials model formats (iOS)
* Supports [Wavefront .OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file), [Autodesk 3DS](https://en.wikipedia.org/wiki/.3ds), [Quake 2 MD2](https://en.wikipedia.org/wiki/MD2_(file_format)), [ASC](https://codeyarns.com/2011/08/17/asc-file-format-for-3d-points/) and GLEssentials model formats (Android)
* Supports all texture image formats supported by [UIImage](https://developer.apple.com/library/content/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/LoadingImages/LoadingImages.html#//apple_ref/doc/uid/TP40010156-CH17-SW8)

## Requirements

* iOS - feel free to PR an Android port ;)
* Cocoapods - to install the [GLView](https://github.com/nicklockwood/GLView) dependency.
* Cocoapods (for iOS) - to install the [GLView](https://github.com/nicklockwood/GLView) dependency.

## Getting started

You can install and try linking the project automatically:

`$ react-native add react-native-gl-model-view`
```sh
$ npm install --save react-native-gl-model-view
$ react-native link
```

or do it manually as described below:

### Manual installation
### iOS Manual installation

`$ npm install --save react-native-gl-model-view`

Expand All @@ -39,8 +42,39 @@ pod 'React', :path => '../node_modules/react-native'
pod 'RNGLModelView', :path => '../node_modules/react-native-gl-model-view'
```

### Android Manual installation

`$ npm install --save react-native-gl-model-view`

Afterwards, add the following lines to the **android/settings.gradle** file:

```gradle
include ':react-native-gl-model-view'
project(':react-native-gl-model-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gl-model-view/android/app')
```

Finally, add the react-native-gl-model-view project as a dependency of the **android/app/build.gradle** file:

```gradle
dependencies {
compile project(':react-native-gl-model-view')
...
}
```


## Usage

### Model and texture loading

#### iOS

To load a model on iOS, you need to put the file at the root of your Xcode project via the editor.

#### Android

To load a model on Android, you need to place the model in the **android/app/src/main/assets** folder. Create a new folder if it doesn't exist yet.

### Static

```javascript
Expand Down Expand Up @@ -132,6 +166,8 @@ Check out the [example project](https://github.com/rastapasta/react-native-gl-mo

To build it, switch into the `example` folder and set it up as following:

#### For iOS

```sh
$ npm install
$ cd ios
Expand All @@ -140,10 +176,18 @@ $ cd ..
$ react-native run-ios
```

#### For Android

```sh
$ npm install
$ react-native link
$ react-native run-android
```

## Backlog

* Bridge to [GLModel.modelWithData](https://github.com/nicklockwood/GLView/blob/master/GLView/Models/GLModel.m#L424) to allow flexbile model sources
* Android bridge via [jPCT 3D engine](http://www.jpct.net/jpct-ae/)
* Update to the latest react-native version to fix the Android gesture responder bug

## Special thanks

Expand Down
34 changes: 34 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}

apply plugin: 'com.android.library'

android {
compileSdkVersion 23
buildToolsVersion "23.0.1"

defaultConfig {
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
}

repositories {
mavenCentral()
}

dependencies {
compile 'com.facebook.react:react-native:+'
compile fileTree(dir: "libs", include: ["*.jar"])
}
Binary file added android/libs/jpct_ae.jar
Binary file not shown.
3 changes: 3 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rnglmodelview">
</manifest>
15 changes: 15 additions & 0 deletions android/src/main/java/com/rnglmodelview/MainActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.rnglmodelview;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "RNGLModelView";
}
}
46 changes: 46 additions & 0 deletions android/src/main/java/com/rnglmodelview/MainApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.rnglmodelview;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNGLModelViewPackage()
);
}

@Override
protected String getJSMainModuleName() {
return "index";
}
};

@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}

@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
184 changes: 184 additions & 0 deletions android/src/main/java/com/rnglmodelview/RNGLModelView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package com.rnglmodelview;

import android.content.Context;
import android.opengl.GLSurfaceView;

import com.rnglmodelview.exceptions.ModelObjectNotSupportedException;
import com.threed.jpct.Loader;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.Texture;

import java.io.IOException;
import java.io.InputStream;

import javax.annotation.Nullable;

public class RNGLModelView extends GLSurfaceView {

private RNGLModelViewRenderer mRenderer;

private Object3D mModel;

private float mModelRotateX = 0;
private float mModelRotateY = 0;
private float mModelRotateZ = 0;
private float mModelTranslateX = 0;
private float mModelTranslateY = 0;
private float mModelTranslateZ = 0;
private float mModelScaleX = 1;
private float mModelScaleY = 1;
private float mModelScaleZ = 1;

public RNGLModelView(Context context) {
super(context);
setEGLContextClientVersion(2);

mRenderer = new RNGLModelViewRenderer(context);
setRenderer(mRenderer);
}

public void setModel(String modelFileName) {
mModel = loadModel(modelFileName);

// In jpct, the coordinate system is rotated 180 degrees around x and Object3D forces the mesh
// into that orientation. Since we want to keep the OpenGL-like coordinate system, we force the
// mesh back into its previous rotation.
mModel.rotateX((float)Math.PI);
mModel.rotateMesh();
mModel.clearRotation();

mRenderer.setModel(mModel);
updateModelTransform();
}

public void setModelTexture(@Nullable String textureFileName) {
mRenderer.setTexture(loadTexture(textureFileName));
}

public void setAnimate(@Nullable boolean animate) {
mRenderer.setAnimate(animate);
}

public void setModelRotateX(@Nullable float rotateX) {
mModelRotateX = rotateX;
updateModelTransform();
}

public void setModelRotateY(@Nullable float rotateY) {
mModelRotateY = rotateY;
updateModelTransform();
}

public void setModelRotateZ(@Nullable float rotateZ) {
mModelRotateZ = rotateZ;
updateModelTransform();
}

public void setModelScale(@Nullable float scale) {
mModelScaleX = scale;
mModelScaleY = scale;
mModelScaleZ = scale;
updateModelTransform();
}

public void setModelScaleX(@Nullable float scaleX) {
mModelScaleX = scaleX;
updateModelTransform();
}

public void setModelScaleY(@Nullable float scaleY) {
mModelScaleY = scaleY;
updateModelTransform();
}

public void setModelScaleZ(@Nullable float scaleZ) {
mModelScaleZ = scaleZ;
updateModelTransform();
}

public void setModelTranslateX(@Nullable float translateX) {
mModelTranslateX = translateX;
updateModelTransform();
}

public void setModelTranslateY(@Nullable float translateY) {
mModelTranslateY = translateY;
updateModelTransform();
}

public void setModelTranslateZ(@Nullable float translateZ) {
mModelTranslateZ = translateZ;
updateModelTransform();
}

private Object3D loadModel(String modelFileName) {
String modelFileNameArray[] = modelFileName.split("\\.");
String extension = modelFileNameArray[modelFileNameArray.length - 1].toLowerCase();

Object3D model = null;

try {
InputStream modelStream = getContext().getAssets().open(modelFileName);

switch (extension) {
case "obj":
model = Object3D.mergeAll(Loader.loadOBJ(modelStream, null, 1));
break;
case "3ds":
model = Object3D.mergeAll(Loader.load3DS(modelStream, 1));
break;
case "md2":
model = Loader.loadMD2(modelStream, 1);
break;
case "asc":
model = Loader.loadASC(modelStream, 1, false);
break;
case "model":
model = RNGLModelViewModelLoader.loadMODEL(modelStream);
break;
}
} catch (IOException | ModelObjectNotSupportedException e) {
e.printStackTrace();
}

return model;
}

private Texture loadTexture(String textureFileName) {
Texture texture = null;

try {
InputStream textureStream = getContext().getAssets().open(textureFileName);
texture = new Texture(textureStream);

} catch (IOException e) {
e.printStackTrace();
}

return texture;
}

private void updateModelTransform() {
if (mModel != null) {
Matrix rotationMatrix = new Matrix();

// First, we scale the identity matrix
rotationMatrix.setRow(0, mModelScaleX, 0, 0, 0);
rotationMatrix.setRow(1, 0, mModelScaleY, 0, 0);
rotationMatrix.setRow(2, 0, 0, mModelScaleZ, 0);

// Second, we rotate the scaled matrix
rotationMatrix.rotateZ((float)Math.toRadians(mModelRotateZ));
rotationMatrix.rotateY((float)Math.toRadians(mModelRotateY));
rotationMatrix.rotateX((float)Math.toRadians(mModelRotateX));

// Finally, we create the translation matrix
Matrix translatonMatrix = new Matrix();
translatonMatrix.translate(mModelTranslateX, mModelTranslateY, mModelTranslateZ);

mModel.setTranslationMatrix(translatonMatrix);
mModel.setRotationMatrix(rotationMatrix);
}
}
}
Loading

0 comments on commit 518b97e

Please sign in to comment.