Android Open Source - ObjViewer_Android Object View






From Project

Back to project page ObjViewer_Android.

License

The source code is released under:

Apache License

If you think the Android project ObjViewer_Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package nz.gen.geek_central.ObjViewer;
/*//from   w ww .  jav  a2  s  .  com
    3D view widget. Lets the user apply interactive rotation of the
    object around any axis. Also does rotation animations.

    Copyright 2011, 2013 by Lawrence D'Oliveiro <ldo@geek-central.gen.nz>.

    Licensed under the Apache License, Version 2.0 (the "License"); you may not
    use this file except in compliance with the License. You may obtain a copy of
    the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    License for the specific language governing permissions and limitations under
    the License.
*/

import javax.microedition.khronos.opengles.GL10;
import nz.gen.geek_central.GLUseful.ObjReader;
import nz.gen.geek_central.GLUseful.Rotation;
import android.graphics.PointF;
import android.view.MotionEvent;
import static nz.gen.geek_central.android.useful.Useful.GetTime;
import nz.gen.geek_central.android.useful.BundledSavedState;
import static nz.gen.geek_central.GLUseful.GLUseful.gl;

public class ObjectView extends android.opengl.GLSurfaceView
  {
    private static boolean DefaultUseLighting = true;
    private static boolean DefaultClockwiseFaces = false;
    private static Rotation DefaultRotation = Rotation.Null;

    private ObjReader.Model TheObject = null;
    private boolean UseLighting = DefaultUseLighting;
    private boolean ClockwiseFaces = DefaultClockwiseFaces;
    private Rotation CurRotation = DefaultRotation;
    private PointF LastMouse = null;

    private class ObjectViewRenderer implements Renderer
      {
      /* Note I ignore the passed GL10 argument, and exclusively use
        static methods from GLES11 class for all OpenGL drawing, since
        this seems to be the preferred way */

        public ObjectViewRenderer()
          {
            super();
          /* nothing else to do, really */
          } /*ObjectViewRenderer*/

        public void onDrawFrame
          (
            GL10 _gl
          )
          {
            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);
            gl.glMatrixMode(gl.GL_MODELVIEW);
            gl.glLoadIdentity();
            if (TheObject != null)
              {
                if (UseLighting)
                  {
                    gl.glEnable(gl.GL_LIGHTING);
                    gl.glLightModelfv
                      (
                        /*pname =*/ gl.GL_LIGHT_MODEL_AMBIENT,
                        /*params =*/ new float[] {0.3f, 0.3f, 0.3f, 1.0f},
                        /*offset =*/ 0
                      ); /* so hopefully objects are never completely black */
                  /* light positions are fixed relative to view */
                    gl.glEnable(gl.GL_LIGHT0);
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT0,
                        /*pname =*/ gl.GL_POSITION,
                        /*params =*/ new float[] {0.0f, 2.0f, 0.0f, 1.0f},
                        /*offset =*/ 0
                      );
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT0,
                        /*pname =*/ gl.GL_AMBIENT,
                        /*params =*/ new float[] {0.4f, 0.4f, 0.4f, 1.0f},
                        /*offset =*/ 0
                      );
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT0,
                        /*pname =*/ gl.GL_DIFFUSE,
                        /*params =*/ new float[] {0.7f, 0.7f, 0.7f, 1.0f},
                        /*offset =*/ 0
                      );
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT0,
                        /*pname =*/ gl.GL_SPECULAR,
                        /*params =*/ new float[] {0.7f, 0.7f, 0.7f, 1.0f},
                        /*offset =*/ 0
                      );
                    gl.glEnable(gl.GL_LIGHT1);
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT1,
                        /*pname =*/ gl.GL_POSITION,
                        /*params =*/ new float[] {0.0f, 0.0f, -2.0f, 1.0f},
                        /*offset =*/ 0
                      );
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT1,
                        /*pname =*/ gl.GL_DIFFUSE,
                        /*params =*/ new float[] {0.3f, 0.3f, 0.3f, 1.0f},
                        /*offset =*/ 0
                      );
                    gl.glLightfv
                      (
                        /*light =*/ gl.GL_LIGHT1,
                        /*pname =*/ gl.GL_SPECULAR,
                        /*params =*/ new float[] {0.3f, 0.3f, 0.3f, 1.0f},
                        /*offset =*/ 0
                      );
                  }
                else
                  {
                    gl.glDisable(gl.GL_LIGHTING);
                  } /*if*/
                final float MaxDim =
                    Math.max
                      (
                        Math.max
                          (
                            TheObject.BoundMax.x - TheObject.BoundMin.x,
                            TheObject.BoundMax.y - TheObject.BoundMin.y
                          ),
                        TheObject.BoundMax.z - TheObject.BoundMin.z
                      );
                final float Scale = 2.5f;
                gl.glTranslatef(0.0f, 0.0f, -2.5f);
                CurRotation.Apply();
                gl.glScalef(Scale / MaxDim, Scale / MaxDim, Scale / MaxDim);
                gl.glTranslatef
                  (
                    - (TheObject.BoundMax.x + TheObject.BoundMin.x) / 2.0f,
                    - (TheObject.BoundMax.y + TheObject.BoundMin.y) / 2.0f,
                    - (TheObject.BoundMax.z + TheObject.BoundMin.z) / 2.0f
                  );
                gl.glFrontFace
                  (
                    ClockwiseFaces ?
                        gl.GL_CW
                    :
                        gl.GL_CCW
                  );
                TheObject.Draw();
              } /*if*/
          } /*onDrawFrame*/

        public void onSurfaceChanged
          (
            GL10 _gl,
            int ViewWidth,
            int ViewHeight
          )
          {
            gl.glViewport(0, 0, ViewWidth, ViewHeight);
            gl.glMatrixMode(gl.GL_PROJECTION);
            gl.glLoadIdentity();
            gl.glFrustumf
              (
                /*l =*/ ViewWidth > ViewHeight ? - 1.0f : - (float)ViewWidth / ViewHeight,
                /*r =*/ ViewWidth > ViewHeight ? 1.0f : (float)ViewWidth / ViewHeight,
                /*b =*/ ViewWidth < ViewHeight ? -1.0f : - (float)ViewHeight / ViewWidth,
                /*t =*/ ViewWidth < ViewHeight ? 1.0f : (float)ViewHeight / ViewWidth,
                /*n =*/ 1.0f,
                /*f =*/ 10.0f
              );
          } /*onSurfaceChanged*/

        public void onSurfaceCreated
          (
            GL10 _gl,
            javax.microedition.khronos.egl.EGLConfig Config
          )
          {
            gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            gl.glEnable(gl.GL_CULL_FACE);
            gl.glShadeModel(gl.GL_SMOOTH);
            gl.glEnable(gl.GL_DEPTH_TEST);
          } /*onSurfaceCreated*/

      } /*ObjectViewRenderer*/

    public ObjectView
      (
        android.content.Context TheContext,
        android.util.AttributeSet TheAttributes
      )
      {
        super(TheContext, TheAttributes);
        setRenderer(new ObjectViewRenderer());
        setRenderMode(RENDERMODE_WHEN_DIRTY);
      } /*ObjectView*/

    private class RotationAnimator implements Runnable
      {
        final android.view.animation.Interpolator AnimFunction;
        final double StartTime, EndTime;
        final Rotation StartRotation, DeltaRotation;

        public RotationAnimator
          (
            android.view.animation.Interpolator AnimFunction,
            double Duration,
            Rotation EndRotation
          )
          {
            this.AnimFunction = AnimFunction;
            this.StartTime = GetTime();
            this.EndTime = this.StartTime + Duration;
            this.StartRotation = CurRotation;
            this.DeltaRotation = EndRotation.mul(StartRotation.inv());
            CurrentAnim = this;
            post(this);
          } /*RotationAnimator*/

        public void run()
          {
            if (CurrentAnim == this)
              {
                final double CurrentTime = GetTime();
                final float AnimAmt =
                    AnimFunction.getInterpolation((float)((CurrentTime - StartTime) / (EndTime - StartTime)));
                CurRotation = DeltaRotation.mul(AnimAmt).mul(StartRotation);
                requestRender();
                if (CurrentTime < EndTime)
                  {
                    post(this);
                  }
                else
                  {
                    CurrentAnim = null;
                  } /*if*/
              } /*if*/
          } /*run*/

      } /*RotationAnimator*/

    private RotationAnimator CurrentAnim = null;

    @Override
    public android.os.Parcelable onSaveInstanceState()
      {
        final android.os.Bundle MyState = new android.os.Bundle();
        MyState.putBoolean("UseLighting", UseLighting);
        MyState.putBoolean("ClockwiseFaces", ClockwiseFaces);
        MyState.putParcelable("CurRotation", CurRotation);
        return
            new BundledSavedState
              (
                super.onSaveInstanceState(),
                MyState
              );
      } /*onSaveInstanceState*/

    @Override
    public void onRestoreInstanceState
      (
        android.os.Parcelable SavedState
      )
      {
        super.onRestoreInstanceState(((BundledSavedState)SavedState).SuperState);
        final android.os.Bundle MyState = ((BundledSavedState)SavedState).MyState;
        UseLighting = MyState.getBoolean("UseLighting", DefaultUseLighting);
        ClockwiseFaces = MyState.getBoolean("ClockwiseFaces", DefaultClockwiseFaces);
        CurRotation = (Rotation)MyState.getParcelable("CurRotation");
        requestRender();
      } /*onRestoreInstanceState*/

    private Rotation CalculateNewRotation
      (
        PointF LastMouse,
        PointF ThisMouse,
        boolean Fling /* debug */
      )
      {
        final PointF MidPoint = new PointF(getWidth() / 2.0f, getHeight() / 2.0f);
        final float Radius =
            (float)Math.hypot(ThisMouse.x - MidPoint.x, ThisMouse.y - MidPoint.y);
        final float DeltaR =
                Radius
            -
                (float)Math.hypot(LastMouse.x - MidPoint.x, LastMouse.y - MidPoint.y);
          /* radial movement, for rotation about X and Y axes */
        final float MidDiag = (float)Math.hypot(MidPoint.x, MidPoint.y);
        final float MaxRot = (float)Math.PI; /* impose limit in case of wild fling gestures */
        final float ZAngle =
            Radius / MidDiag >= 0.5f ?
                (float)
                  (
                        Math.atan2
                          (
                            ThisMouse.y - MidPoint.y,
                            ThisMouse.x - MidPoint.x
                          )
                    -
                        Math.atan2
                          (
                            LastMouse.y - MidPoint.y,
                            LastMouse.x - MidPoint.x
                          )
                  )
            : /* disable Z-rotation too close to centre where its too hard to control */
                0.0f;
        final float RotationSin = DeltaR / MidDiag;
          /* scale rotation angle by assuming depth of
            axis is equal to radius of view */
          { /* debug */
            final double RotationAngle =
                Math.toDegrees
                  (
                    Math.abs(RotationSin) > 1.0f ?
                      /* can happen with fling gestures */
                        RotationSin * (float)Math.PI / 2.0f
                    :
                        (float)Math.asin(RotationSin)
                  );
        System.err.printf("ObjViewer.ObjectView.CalculateNewRotation fling %s LastMouse(%.3f, %.3f)  ThisMouse(%.3f, %.3f) => rotation %.3f about (%.3f, %.3f, 0)\n", Fling, LastMouse.x, LastMouse.y, ThisMouse.x, ThisMouse.y, RotationAngle, (ThisMouse.y - MidPoint.y) / Radius, (ThisMouse.x - MidPoint.x) / Radius);
          } /* debug */
        return
                new Rotation /* X+Y axis */
                  (
                    Math.abs(RotationSin) > 1.0f ?
                      /* can happen with fling gestures */
                        Math.max(Math.min(RotationSin * (float)Math.PI / 2.0f, MaxRot), - MaxRot)
                    :
                        (float)Math.asin(RotationSin),
                    false,
                    (ThisMouse.y - MidPoint.y) / Radius,
                    (ThisMouse.x - MidPoint.x) / Radius,
                    0
                  )
            .mul
              (
                new Rotation(Math.max(Math.min(ZAngle, MaxRot), - MaxRot), false, 0, 0, -1) /* Z axis */
              )
            .mul
              (
                CurRotation
              );
              /* ordering of composing the new rotations doesn't matter
                because axes are orthogonal */
      } /*CalculateNewRotation*/

    private final android.view.GestureDetector FlingDetector =
        new android.view.GestureDetector
          (
            getContext(),
            new android.view.GestureDetector.SimpleOnGestureListener()
              {
                @Override
                public boolean onFling
                  (
                    MotionEvent DownEvent,
                    MotionEvent UpEvent,
                    float XVelocity,
                    float YVelocity
                  )
                  {
                    final double CurrentTime = GetTime();
                    final float InitialAttenuate = 5.0f; /* attenuates initial speed */
                    final float FinalAttenuate = 2.0f; /* attenuates duration of spin */
                    final float SpinDuration =
                            (float)Math.hypot(XVelocity, YVelocity)
                        /
                            (float)Math.hypot(getWidth(), getHeight())
                        /
                            FinalAttenuate;
                    System.err.printf("ObjViewer.ObjectView: spin duration = %.3fs\n", SpinDuration); /* debug */
                    final PointF LastMouse = new PointF(UpEvent.getX(), UpEvent.getY());
                    final PointF ThisMouse =
                            new PointF
                              (
                                    LastMouse.x
                                +
                                        XVelocity
                                    *
                                        SpinDuration
                                    /
                                        InitialAttenuate,
                                    LastMouse.y
                                +
                                        YVelocity
                                    *
                                        SpinDuration
                                    /
                                        InitialAttenuate
                              );
                    new RotationAnimator
                      (
                        /*AnimFunction =*/ new android.view.animation.DecelerateInterpolator(),
                        /*Duration =*/ SpinDuration,
                        /*EndRotation =*/ CalculateNewRotation(LastMouse, ThisMouse, true)
                      );
                    return
                        true;
                  } /*onFling*/
              } /*GestureDetector.SimpleOnGestureListener*/
          );

    @Override
    public boolean onTouchEvent
      (
        MotionEvent TheEvent
      )
      {
        boolean Handled = false;
        if (CurrentAnim == null && FlingDetector.onTouchEvent(TheEvent))
          {
            Handled = true;
          } /*if*/
        if (!Handled)
          {
            switch (TheEvent.getAction())
              {
            case MotionEvent.ACTION_DOWN:
                CurrentAnim = null;
                LastMouse = new PointF(TheEvent.getX(), TheEvent.getY());
                Handled = true;
            break;
            case MotionEvent.ACTION_MOVE:
                if (LastMouse != null && TheObject != null)
                  {
                    final PointF ThisMouse = new PointF(TheEvent.getX(), TheEvent.getY());
                    CurRotation = CalculateNewRotation(LastMouse, ThisMouse, false);
                    LastMouse = ThisMouse;
                    requestRender();
                  } /*if*/
                Handled = true;
            break;
            case MotionEvent.ACTION_UP:
                LastMouse = null;
                Handled = true;
            break;
              } /*switch*/
          } /*if*/
        return
            Handled;
      } /*onTouchEvent*/

    public void ResetOrientation
      (
        boolean Animate
      )
      {
        SetOrientation(Rotation.Null, Animate);
      } /*ResetOrientation*/

    public void SetObject
      (
        ObjReader.Model NewObject
      )
      {
        TheObject = NewObject;
        ResetOrientation(false);
      } /*SetObject*/

    public ObjReader.Model GetObject()
      {
        return
            TheObject;
      } /*GetObject*/

    public void SetUseLighting
      (
        boolean UseLighting
      )
      {
        this.UseLighting = UseLighting;
        requestRender();
      } /*SetUseLighting*/

    public boolean GetUseLighting()
      {
        return
            UseLighting;
      } /*GetUseLighting*/

    public void SetClockwiseFaces
      (
        boolean ClockwiseFaces
      )
      {
        this.ClockwiseFaces = ClockwiseFaces;
        requestRender();
      } /*SetClockwiseFaces*/

    public boolean GetClockwiseFaces()
      {
        return
            ClockwiseFaces;
      } /*GetClockwiseFaces*/

    public void SetOrientation
      (
        Rotation NewOrientation,
        android.view.animation.Interpolator AnimFunction, /* optional */
        float AnimDuration /* ignored unless AnimFunction is specified */
      )
      {
        if (AnimFunction != null)
          {
            new RotationAnimator
              (
                /*AnimFunction =*/ AnimFunction,
                /*Duration =*/ AnimDuration,
                /*EndRotation =*/ NewOrientation
              );
          }
        else
          {
            CurRotation = NewOrientation;
            requestRender();
          } /*if*/
      } /*SetOrientation*/

    public void SetOrientation
      (
        Rotation NewOrientation,
        boolean Animate
      )
      {
        SetOrientation
          (
            /*NewOrientation =*/ NewOrientation,
            /*AnimFunction =*/
                Animate ?
                    new PolyAccelerationDecelerationInterpolator(3.0f)
                :
                    null,
            /*AnimDuration =*/ 1.5f
          );
      } /*SetOrientation*/

  } /*ObjectView*/




Java Source Code List

nz.gen.geek_central.GLUseful.GLUseful.java
nz.gen.geek_central.GLUseful.GeomBuilder.java
nz.gen.geek_central.GLUseful.ObjReader.java
nz.gen.geek_central.GLUseful.Rotation.java
nz.gen.geek_central.GLUseful.Vec3f.java
nz.gen.geek_central.ObjViewer.Main.java
nz.gen.geek_central.ObjViewer.ObjectView.java
nz.gen.geek_central.ObjViewer.Picker.java
nz.gen.geek_central.ObjViewer.PolyAccelerationDecelerationInterpolator.java
nz.gen.geek_central.android.useful.BundledSavedState.java
nz.gen.geek_central.android.useful.Useful.java