├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── ic_delete.png
│ │ │ ├── bg_round_down_white.xml
│ │ │ └── bg_round_up_green_dark.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_delete.png
│ │ │ ├── ic_file.png
│ │ │ ├── ic_folder.png
│ │ │ ├── ic_file_ds.png
│ │ │ ├── ic_file_obj.png
│ │ │ ├── ic_file_stl.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_arrow_left.png
│ │ │ ├── ic_file_model.png
│ │ │ ├── ic_model_zoom.png
│ │ │ ├── ic_model_turn_x.png
│ │ │ ├── ic_model_turn_y.png
│ │ │ ├── ic_model_turn_left.png
│ │ │ ├── ic_model_turn_right.png
│ │ │ └── abc_ic_ab_back_mtrl_am_alpha.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── colors.xml
│ │ │ ├── styles.xml
│ │ │ └── strings.xml
│ │ ├── values-v21
│ │ │ └── styles.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── menu
│ │ │ ├── menu_main.xml
│ │ │ └── menu_show_model.xml
│ │ └── layout
│ │ │ ├── activity_draw_grid.xml
│ │ │ ├── item_frequent_folder.xml
│ │ │ ├── item_directory_level.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_show_model.xml
│ │ │ ├── dialog_renderer_setting.xml
│ │ │ ├── content_main.xml
│ │ │ ├── item_filelist_main.xml
│ │ │ ├── dialog_show_setting.xml
│ │ │ ├── activity_file_directory.xml
│ │ │ ├── activity_about.xml
│ │ │ └── activity_open_file.xml
│ │ ├── java
│ │ └── com
│ │ │ └── dyman
│ │ │ └── show3dmodel
│ │ │ ├── view
│ │ │ └── MyDialog.java
│ │ │ ├── adapter
│ │ │ ├── listener
│ │ │ │ └── OnAdapterItemListener.java
│ │ │ ├── BaseSwipListAdapter.java
│ │ │ ├── FolderListAdapter.java
│ │ │ ├── FileListAdapter.java
│ │ │ └── FileRvListAdapter.java
│ │ │ ├── config
│ │ │ ├── IntentKey.java
│ │ │ └── MyConfig.java
│ │ │ ├── bean
│ │ │ ├── FolderBean.java
│ │ │ └── FileBean.java
│ │ │ ├── ui
│ │ │ ├── AboutActivity.java
│ │ │ ├── BaseActivity.java
│ │ │ ├── ShowModelActivity.java
│ │ │ ├── OpenFileActivity.java
│ │ │ └── MainActivity.java
│ │ │ ├── utils
│ │ │ ├── TimeUtils.java
│ │ │ ├── ToastUtils.java
│ │ │ ├── SnackBarUtils.java
│ │ │ ├── DividerItemDecoration.java
│ │ │ ├── DialogUtils.java
│ │ │ └── FileUtils.java
│ │ │ ├── manager
│ │ │ └── SharePreferenceManager.java
│ │ │ └── db
│ │ │ └── DatabaseHelper.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── easyshow3d
├── .gitignore
├── libs
│ └── jpct_ae.jar
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── dyman
│ │ │ │ └── easyshow3d
│ │ │ │ ├── EasyShowGlobal.java
│ │ │ │ ├── thread
│ │ │ │ ├── IAnalysisFinishCallback.java
│ │ │ │ ├── VerticesThread.java
│ │ │ │ ├── FaceThread.java
│ │ │ │ └── AnalysisThreadHelper.java
│ │ │ │ ├── imp
│ │ │ │ └── ModelLoaderListener.java
│ │ │ │ ├── utils
│ │ │ │ ├── LoadUtil.java
│ │ │ │ ├── Normal.java
│ │ │ │ ├── IOUtils.java
│ │ │ │ ├── ShaderUtil.java
│ │ │ │ ├── MatrixState.java
│ │ │ │ └── FileUtils.java
│ │ │ │ ├── bean
│ │ │ │ ├── ObjProObject.java
│ │ │ │ └── ModelObject.java
│ │ │ │ ├── view
│ │ │ │ ├── ShowModelView.java
│ │ │ │ ├── ModelView.java
│ │ │ │ └── Show3dsMd2View.java
│ │ │ │ └── ModelFactory.java
│ │ ├── assets
│ │ │ ├── easy_show_frag.sh
│ │ │ ├── easy_show_frag_color.sh
│ │ │ ├── easy_show_frag_clipplane.sh
│ │ │ ├── easy_show_vertex.sh
│ │ │ └── easy_show_vertex_clipplane.sh
│ │ ├── AndroidManifest.xml
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── dyman
│ │ │ └── easyshow3d
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── dyman
│ │ └── easyshow3d
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── art
├── 00.png
├── 01.png
├── 02.png
├── 03.png
├── 04.png
├── 05.png
├── show3D_demo.gif
└── show3D_download_code.png
├── model
├── 茶壶.obj
└── 小黄人.stl
├── apk
└── Show3D-v1.6.1.170915.apk
├── .idea
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── modules.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/easyshow3d/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':easyshow3d'
2 |
--------------------------------------------------------------------------------
/art/00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/00.png
--------------------------------------------------------------------------------
/art/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/01.png
--------------------------------------------------------------------------------
/art/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/02.png
--------------------------------------------------------------------------------
/art/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/03.png
--------------------------------------------------------------------------------
/art/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/04.png
--------------------------------------------------------------------------------
/art/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/05.png
--------------------------------------------------------------------------------
/model/茶壶.obj:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/model/茶壶.obj
--------------------------------------------------------------------------------
/model/小黄人.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/model/小黄人.stl
--------------------------------------------------------------------------------
/art/show3D_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/show3D_demo.gif
--------------------------------------------------------------------------------
/easyshow3d/libs/jpct_ae.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/easyshow3d/libs/jpct_ae.jar
--------------------------------------------------------------------------------
/apk/Show3D-v1.6.1.170915.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/apk/Show3D-v1.6.1.170915.apk
--------------------------------------------------------------------------------
/art/show3D_download_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/art/show3D_download_code.png
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/drawable/ic_delete.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_delete.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_file.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_folder.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_file_ds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_file_ds.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_file_obj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_file_obj.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_file_stl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_file_stl.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_arrow_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_arrow_left.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_file_model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_file_model.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_model_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_model_zoom.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_model_turn_x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_model_turn_x.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_model_turn_y.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_model_turn_y.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_model_turn_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_model_turn_left.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_model_turn_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/ic_model_turn_right.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/abc_ic_ab_back_mtrl_am_alpha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gytai/EasyShow3D/master/app/src/main/res/mipmap-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/EasyShowGlobal.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d;
2 |
3 | /**
4 | * Created by dyman on 2017/9/13.
5 | */
6 |
7 | public class EasyShowGlobal {
8 | }
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | easyshow3d/src/main/java/com/dyman/easyshow3d/bean/ObjProObject.java
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/view/MyDialog.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.view;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.Context;
5 |
6 | /**
7 | * Created by dyman on 2017/9/14.
8 | */
9 |
10 | public class MyDialog {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 26 21:35:21 CST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 15dp
7 |
8 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/assets/easy_show_frag.sh:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | //接收从顶点着色器过来的参数
3 | varying vec4 ambient;
4 | varying vec4 diffuse;
5 | varying vec4 specular;
6 | void main()
7 | {
8 | //将计算出的颜色给此片元
9 | vec4 finalColor=vec4(0.9,0.9,0.9,1.0);
10 | gl_FragColor = finalColor*ambient+finalColor*specular+finalColor*diffuse;//给此片元颜色值
11 | }
--------------------------------------------------------------------------------
/easyshow3d/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/adapter/listener/OnAdapterItemListener.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.adapter.listener;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by dyman on 16/7/24.
7 | */
8 | public interface OnAdapterItemListener {
9 | /**
10 | * item的layout的点击事件
11 | */
12 | void onItemClick(View v, int position);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/adapter/BaseSwipListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.adapter;
2 |
3 | import android.widget.BaseAdapter;
4 |
5 | /**
6 | * Created by dyman on 16/8/20.
7 | */
8 | public abstract class BaseSwipListAdapter extends BaseAdapter {
9 | public boolean getSwipEnableByPosition(int position) {
10 | return true;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/thread/IAnalysisFinishCallback.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.thread;
2 |
3 | /**
4 | * Created by dyman on 18/5/17.
5 | */
6 |
7 | public interface IAnalysisFinishCallback {
8 | void verticeProgressUpdate(int threadID, int nums);
9 | void faceProgressUpdate(int threadID, int nums);
10 | void alvFinish();
11 | void alvFaceFinish();
12 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_round_down_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_round_up_green_dark.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/assets/easy_show_frag_color.sh:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | uniform vec4 aColor;
3 | varying vec4 ambient; //从顶点着色器传递过来的环境光最终强度
4 | varying vec4 diffuse; //从顶点着色器传递过来的散射光最终强度
5 | varying vec4 specular; //从顶点着色器传递过来的镜面光最终强度
6 | void main() {
7 | vec4 finalColor=aColor; //物体本身的颜色
8 | //综合三个通道光的最终强度及片元的颜色计算出最终片元的颜色并传递给管线
9 | gl_FragColor = finalColor*ambient+finalColor*specular+finalColor*diffuse;
10 | }
--------------------------------------------------------------------------------
/easyshow3d/src/main/assets/easy_show_frag_clipplane.sh:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | //接收从顶点着色器过来的参数
3 | varying vec4 ambient;
4 | varying vec4 diffuse;
5 | varying vec4 specular;
6 | varying float u_clipDist;
7 | void main()
8 | {
9 | if(u_clipDist < 0.0) discard;
10 |
11 | //将计算出的颜色给此片元
12 | vec4 finalColor=vec4(0.95,0.95,0.95,1.0);
13 | gl_FragColor = finalColor*ambient+finalColor*specular+finalColor*diffuse;//给此片元颜色值
14 | }
--------------------------------------------------------------------------------
/easyshow3d/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EasyShow3D
3 |
4 | 请等一会.
5 | STL 文件加载中...
6 | STL 文件解析失败.
7 | 文件过大,解析失败!
8 | 关闭对话框
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/imp/ModelLoaderListener.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.imp;
2 |
3 | import com.dyman.easyshow3d.bean.ModelObject;
4 |
5 | /**
6 | * Created by dyman on 2017/9/13.
7 | */
8 |
9 | public interface ModelLoaderListener {
10 |
11 | void loadBegin();
12 |
13 | void loadedUpdate(float progress);
14 |
15 | void loadedFinish(ModelObject modelObject);
16 |
17 | void loaderCancel();
18 |
19 | void loaderFailure();
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/config/IntentKey.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.config;
2 |
3 | /**
4 | * Created by dyman on 16/8/19.
5 | */
6 | public class IntentKey {
7 |
8 | public static String DIRECTORY_PATH = "directory_path";
9 | public static String TITLE = "title";
10 | public static String DIRECTORY_NAME = "directory_name";
11 | public static String KEY_TYPE = "type";
12 | public final static String DIRECTORY_KEY = "directory_key";
13 |
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_show_model.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/easyshow3d/src/test/java/com/dyman/easyshow3d/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_draw_grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #26A69A
4 | #0E6961
5 | #4055D5
6 |
7 | #F5F5F5
8 | #EBEBEB
9 | #D0D0D0
10 | #B53030
11 | #0E6961
12 | #E0E0E0
13 | #404040
14 |
15 |
16 |
17 | #707070
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/bean/FolderBean.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.bean;
2 |
3 | /**
4 | * Created by dyman on 16/8/19.
5 | */
6 | public class FolderBean {
7 |
8 |
9 | private String folderName;
10 | private String folderPath;
11 |
12 |
13 | public String getFolderName() {
14 | return folderName;
15 | }
16 |
17 | public void setFolderName(String folderName) {
18 | this.folderName = folderName;
19 | }
20 |
21 | public String getFolderPath() {
22 | return folderPath;
23 | }
24 |
25 | public void setFolderPath(String folderPath) {
26 | this.folderPath = folderPath;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/dyman/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/easyshow3d/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/dyman/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/easyshow3d/src/androidTest/java/com/dyman/easyshow3d/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.dyman.easyshow3d.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_frequent_folder.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
17 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/config/MyConfig.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.config;
2 |
3 | import android.os.Environment;
4 |
5 | import java.io.File;
6 |
7 | /**
8 | * Created by dyman on 16/8/21.
9 | */
10 | public class MyConfig {
11 |
12 | public final static int POST_DELAYED_TIME = 200;
13 |
14 | /**
15 | * 常用文件目录
16 | */
17 | public static final String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
18 | /** QQ文件目录 */
19 | public static final String QQFilePath = rootPath+ File.separator+"tencent"+File.separator+"QQfile_recv";
20 | /** 系统下载目录 */
21 | public static final String SystemDownloadPath = rootPath+File.separator+"Download";
22 | /** WPS文件目录 */
23 | public static final String WPSFilePath = rootPath+File.separator+"documents";
24 | /** 微信文件目录 */
25 | public static final String WXFilePath = rootPath+File.separator+"tencent"+File.separator+"MicroMsg"+File.separator+"Download";
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/easyshow3d/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 | buildToolsVersion "26.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 19
10 | versionCode 2
11 | versionName "1.2"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(dir: 'libs', include: ['*.jar'])
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 | compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
30 | testCompile 'junit:junit:4.12'
31 | compile files('libs/jpct_ae.jar')
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_directory_level.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
18 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/ui/AboutActivity.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.ui;
2 |
3 | import android.os.Handler;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.Toolbar;
6 | import android.view.View;
7 |
8 | import com.dyman.show3dmodel.R;
9 | import com.dyman.show3dmodel.config.MyConfig;
10 |
11 | public class AboutActivity extends BaseActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_about);
17 |
18 | initToolbar();
19 | }
20 |
21 | private void initToolbar() {
22 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_activity_about);
23 | setSupportActionBar(toolbar);
24 | toolbar.setNavigationOnClickListener(new View.OnClickListener() {
25 | @Override
26 | public void onClick(View view) {
27 | new Handler().postDelayed(new Runnable() {
28 | @Override
29 | public void run() {
30 | finish();
31 | }
32 | }, MyConfig.POST_DELAYED_TIME);
33 | }
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/utils/LoadUtil.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.utils;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.ByteOrder;
5 | import java.nio.FloatBuffer;
6 |
7 | public class LoadUtil {
8 |
9 | //求两个向量的叉积
10 | public static float[] getCrossProduct(float x1,float y1,float z1,float x2,float y2,float z2) {
11 | //求出两个矢量叉积矢量在XYZ轴的分量ABC
12 | float A=y1*z2-y2*z1;
13 | float B=z1*x2-z2*x1;
14 | float C=x1*y2-x2*y1;
15 |
16 | return new float[]{A,B,C};
17 | }
18 |
19 | //向量规格化
20 | public static float[] vectorNormal(float[] vector) {
21 | //求向量的模
22 | float module=(float)Math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]+vector[2]*vector[2]);
23 | return new float[]{vector[0]/module,vector[1]/module,vector[2]/module};
24 | }
25 |
26 | public static FloatBuffer fromArrayToBuff(float[] a) {
27 | ByteBuffer bf = ByteBuffer.allocateDirect(a.length*4);
28 | bf.order(ByteOrder.nativeOrder());//设置字节顺序
29 | FloatBuffer result = bf.asFloatBuffer();
30 | result.put(a);
31 | result.position(0);
32 | return result;
33 | }
34 |
35 |
36 | /**
37 | * 矫正坐标 坐标圆心移动
38 | * @param
39 | * @param position
40 | */
41 | public static void adjust_coordinate(float[] vertex_array , int position, float adjust){
42 | vertex_array[position] -= adjust;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_show_model.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
23 |
24 |
25 |
30 |
31 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/utils/Normal.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.utils;
2 |
3 | import java.util.Set;
4 |
5 | //表示法向量的类,此类的一个对象表示一个法向量
6 | public class Normal
7 | {
8 | public static final float DIFF=0.0000001f;//判断两个法向量是否相同的阈值
9 | //法向量在XYZ轴上的分量
10 | float nx;
11 | float ny;
12 | float nz;
13 |
14 | public Normal(float nx, float ny, float nz)
15 | {
16 | this.nx=nx;
17 | this.ny=ny;
18 | this.nz=nz;
19 | }
20 |
21 | @Override
22 | public boolean equals(Object o)
23 | {
24 | if(o instanceof Normal)
25 | {//若两个法向量XYZ三个分量的差都小于指定的阈值则认为这两个法向量相等
26 | Normal tn=(Normal)o;
27 | if(Math.abs(nx-tn.nx) sn)
54 | {
55 | //存放法向量和的数组
56 | float[] result=new float[3];
57 | //把集合中所有的法向量求和
58 | for(Normal n:sn)
59 | {
60 | result[0]+=n.nx;
61 | result[1]+=n.ny;
62 | result[2]+=n.nz;
63 | }
64 | //将求和后的法向量规格化
65 | return LoadUtil.vectorNormal(result);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/utils/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.utils;
2 |
3 | import android.util.Log;
4 |
5 | import java.text.DateFormat;
6 | import java.text.SimpleDateFormat;
7 | import java.util.Calendar;
8 | import java.util.Date;
9 |
10 | /**
11 | * Created by dyman on 16/8/19.
12 | */
13 | public class TimeUtils {
14 |
15 | private static final String TAG = "TimeUtils";
16 |
17 | /** 将时间格式化 */
18 | public static String getTimeFormat(long time) {
19 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
20 | Date d1 = new Date(time);
21 | String t1 = format.format(time);
22 | return t1;
23 | }
24 |
25 | public static String timeTransfer(long time) {
26 | long hour = 0, minute = 0, second = 0, ms = 0;
27 | if (time > 60 * 60 * 1000 ) {
28 | ms = time % 1000;
29 | second = (time / 1000) % 60;
30 | minute = (time / (60*1000)) % 60;
31 | hour = (time / (60*60*1000)) % 60;
32 | } else if (time > 60 * 1000) {
33 | ms = time % 1000;
34 | second = (time / 1000) % 60;
35 | minute = (time / (60*1000)) % 60;
36 | } else if (time > 1000) {
37 | ms = time % 1000;
38 | second = (time / 1000) % 60;
39 | } else {
40 | ms = time % 1000;
41 | }
42 |
43 | return hour + "h" + minute + "m" + second + "s" + ms + "ms";
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "24.0.2"
6 |
7 | compileOptions {
8 | sourceCompatibility JavaVersion.VERSION_1_8
9 | targetCompatibility JavaVersion.VERSION_1_8
10 | }
11 |
12 | defaultConfig {
13 | applicationId "com.dyman.show3dmodel"
14 | minSdkVersion 16
15 | targetSdkVersion 22
16 | versionCode 2
17 | versionName "1.6.1.170915"
18 | jackOptions {
19 | enabled true
20 | }
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | android.applicationVariants.all { variant ->
30 | variant.outputs.each { output ->
31 | output.outputFile = new File(output.outputFile.parent, defaultConfig.applicationId + "-v" + defaultConfig.versionName + ".apk");
32 | }
33 | }
34 |
35 | }
36 |
37 | dependencies {
38 | compile fileTree(include: ['*.jar'], dir: 'libs')
39 | testCompile 'junit:junit:4.12'
40 | compile 'com.android.support:appcompat-v7:24.1.1'
41 | compile 'com.android.support:design:24.1.1'
42 | compile 'com.hanuor.onyx:onyx:1.1.4'
43 | compile 'com.baoyz.swipemenulistview:library:1.3.0'
44 | compile project(':easyshow3d')
45 | // compile 'com.github.DymanZy:EasyShow3D:1.1'
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/ui/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.ui;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Build;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.view.Window;
8 | import android.view.WindowManager;
9 |
10 | import com.dyman.show3dmodel.R;
11 | import com.dyman.show3dmodel.manager.SystemBarTintManager;
12 |
13 | /**
14 | * Created by dyman on 16/8/19.
15 | * 更改状态栏的颜色,所以Activity统一继承BaseActivity
16 | */
17 | public class BaseActivity extends AppCompatActivity {
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
23 | setTranslucentStatus(true);
24 | SystemBarTintManager tintManager = new SystemBarTintManager(this);
25 | tintManager.setStatusBarTintEnabled(true);
26 | tintManager.setStatusBarTintResource(R.color.colorPrimary);//通知栏所需颜色
27 | }
28 | }
29 |
30 | @TargetApi(19)
31 | private void setTranslucentStatus(boolean on) {
32 | Window win = getWindow();
33 | WindowManager.LayoutParams winParams = win.getAttributes();
34 | final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
35 | if (on) {
36 | winParams.flags |= bits;
37 | } else {
38 | winParams.flags &= ~bits;
39 | }
40 | win.setAttributes(winParams);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
34 |
35 |
38 |
39 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/thread/VerticesThread.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.thread;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * Created by dyman on 18/5/17.
7 | */
8 |
9 | public class VerticesThread extends Thread {
10 |
11 | private static final String TAG = "VerticesThread";
12 | /** 定义解析的起始点 */
13 | private int start;
14 | /** 定义解析的结束点 */
15 | private int end;
16 | /** obj文件的数据 */
17 | private ArrayList vLines;
18 | /** 当前线程的ID */
19 | private int threadID;
20 | /** 解析完成的回调 */
21 | private IAnalysisFinishCallback finishCallback;
22 | private boolean isFinish = false;
23 | private float[] alv;
24 |
25 |
26 | public VerticesThread(int threadID, ArrayList vLines, float[] alv, int start, int end, IAnalysisFinishCallback finishCallback) {
27 | this.threadID = threadID;
28 | this.vLines = vLines;
29 | this.alv = alv;
30 | this.start = start;
31 | this.end = end;
32 | this.finishCallback = finishCallback;
33 | }
34 |
35 |
36 | @Override
37 | public void run() {
38 |
39 | for (int i = start; i < end; i++) {
40 | String[] tempsa = vLines.get(i);
41 | if (tempsa[0].trim().equals("v")) {
42 | alv[i * 3 + 0] = Float.parseFloat(tempsa[1]);
43 | alv[i * 3 + 1] = Float.parseFloat(tempsa[2]);
44 | alv[i * 3 + 2] = Float.parseFloat(tempsa[3]);
45 | }
46 |
47 | if ((i - start) % 100 == 0) {
48 | finishCallback.verticeProgressUpdate(threadID, i - start);
49 | }
50 | }
51 | isFinish = true;
52 | finishCallback.alvFinish();
53 | }
54 |
55 |
56 | public boolean isFinish() {
57 | return isFinish;
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_renderer_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
15 |
20 |
30 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/bean/FileBean.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.bean;
2 |
3 | /**
4 | * Created by dyman on 16/8/18.
5 | */
6 | public class FileBean {
7 | public static final String ID = "fileID";
8 | public static final String FILEPATH = "filePath";
9 | public static final String FILENAME = "fileName";
10 | public static final String FILETYPE = "fileType";
11 | public static final String CREATETIME = "createTime";
12 |
13 | private String id;
14 | private String filePath;
15 | private String fileName;
16 | private String fileType;
17 | private String createTime;
18 |
19 | public FileBean() {
20 | super();
21 | }
22 |
23 | public String getId() { return id;}
24 |
25 | public void setId(String id) { this.id = id;}
26 |
27 | public String getCreateTime() {
28 | return createTime;
29 | }
30 |
31 | public void setCreateTime(String createTime) {
32 | this.createTime = createTime;
33 | }
34 |
35 | public String getFileName() {
36 | return fileName;
37 | }
38 |
39 | public void setFileName(String fileName) {
40 | this.fileName = fileName;
41 | }
42 |
43 | public String getFilePath() {
44 | return filePath;
45 | }
46 |
47 | public void setFilePath(String filePath) {
48 | this.filePath = filePath;
49 | }
50 |
51 | public String getFileType() {
52 | return fileType;
53 | }
54 |
55 | public void setFileType(String fileType) {
56 | this.fileType = fileType;
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return "FileBean{" +
62 | "createTime='" + createTime + '\'' +
63 | ", id='" + id + '\'' +
64 | ", filePath='" + filePath + '\'' +
65 | ", fileName='" + fileName + '\'' +
66 | ", fileType='" + fileType + '\'' +
67 | '}';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
19 |
24 |
30 |
31 |
32 |
36 |
37 |
42 |
43 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/utils/ToastUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.utils;
2 |
3 | import android.content.Context;
4 | import android.widget.Toast;
5 |
6 |
7 | /**
8 | * Toast工具类
9 | * @author dzy
10 | * @version 1.00
11 | */
12 | public class ToastUtils {
13 | public static Toast toast = null;
14 | /**
15 | * 弹出端时toast
16 | *
17 | * @param context
18 | * 上下文
19 | * @param text
20 | * 弹出的信息
21 | * */
22 | public static void showShort(Context context, String text) {
23 | if (toast == null) {
24 | toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
25 | } else {
26 | toast.setDuration(Toast.LENGTH_SHORT);
27 | toast.setText(text);
28 | }
29 | toast.show();
30 | }
31 |
32 | /**
33 | * 弹出长时toast
34 | *
35 | * @param context
36 | * 上下文
37 | * @param text
38 | * 弹出的信息
39 | * */
40 | public static void showLong(Context context, String text) {
41 | if (toast == null) {
42 | toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
43 | } else {
44 | toast.setDuration(Toast.LENGTH_LONG);
45 | toast.setText(text);
46 | }
47 | toast.show();
48 | }
49 |
50 | /**
51 | * 弹出短时toast
52 | *
53 | * @param context
54 | * 上下文
55 | * @param resourseId
56 | * 弹出的信息ID
57 | * */
58 | public static void showShort(Context context, int resourseId) {
59 | if (toast == null) {
60 | toast = Toast.makeText(context, resourseId, Toast.LENGTH_SHORT);
61 | } else {
62 | toast.setDuration(Toast.LENGTH_SHORT);
63 | toast.setText(resourseId);
64 | }
65 | toast.show();
66 | }
67 |
68 | /**
69 | * 弹出长时toast
70 | *
71 | * @param context
72 | * 上下文
73 | * @param resourseId
74 | * 弹出的信息ID
75 | * */
76 | public static void showLong(Context context, int resourseId) {
77 | if (toast == null) {
78 | toast = Toast.makeText(context, resourseId, Toast.LENGTH_LONG);
79 | } else {
80 | toast.setDuration(Toast.LENGTH_LONG);
81 | toast.setText(resourseId);
82 | }
83 | toast.show();
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/assets/easy_show_vertex.sh:
--------------------------------------------------------------------------------
1 | uniform mat4 uMVPMatrix; //总变换矩阵
2 | uniform mat4 uMMatrix; //变换矩阵
3 | uniform vec3 uLightLocation; //光源位置
4 | uniform vec3 uCamera; //摄像机位置
5 | attribute vec3 aPosition; //顶点位置
6 | attribute vec3 aNormal; //顶点法向量
7 | //用于传递给片元着色器的变量
8 | varying vec4 ambient;
9 | varying vec4 diffuse;
10 | varying vec4 specular;
11 |
12 | //定位光光照计算的方法
13 | void pointLight( //定位光光照计算的方法
14 | in vec3 normal, //法向量
15 | inout vec4 ambient, //环境光最终强度
16 | inout vec4 diffuse, //散射光最终强度
17 | inout vec4 specular, //镜面光最终强度
18 | in vec3 lightLocation, //光源位置
19 | in vec4 lightAmbient, //环境光强度
20 | in vec4 lightDiffuse, //散射光强度
21 | in vec4 lightSpecular //镜面光强度
22 | ){
23 | ambient=lightAmbient; //直接得出环境光的最终强度
24 | vec3 normalTarget=aPosition+normal; //计算变换后的法向量
25 | vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
26 | newNormal=normalize(newNormal); //对法向量规格化
27 | //计算从表面点到摄像机的向量
28 | vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);
29 | //计算从表面点到光源位置的向量vp
30 | vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
31 | vp=normalize(vp);//格式化vp
32 | vec3 halfVector=normalize(vp+eye); //求视线与光线的半向量
33 | float shininess=50.0; //粗糙度,越小越光滑
34 | float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量与vp的点积与0的最大值
35 | diffuse=lightDiffuse*nDotViewPosition; //计算散射光的最终强度
36 | float nDotViewHalfVector=dot(newNormal,halfVector); //法线与半向量的点积
37 | float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); //镜面反射光强度因子
38 | specular=lightSpecular*powerFactor; //计算镜面光的最终强度
39 | }
40 |
41 |
42 | void main()
43 | {
44 | gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置
45 |
46 | vec4 ambientTemp, diffuseTemp, specularTemp; //存放环境光、散射光、镜面反射光的临时变量
47 | pointLight(normalize(aNormal),ambientTemp,diffuseTemp,specularTemp,uLightLocation,vec4(0.1,0.1,0.1,1.0),vec4(0.7,0.7,0.7,1.0),vec4(0.3,0.3,0.3,1.0));
48 |
49 | ambient=ambientTemp;
50 | diffuse=diffuseTemp;
51 | specular=specularTemp;
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/manager/SharePreferenceManager.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.manager;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.opengl.GLES20;
7 | import android.os.Environment;
8 |
9 | /**
10 | * Created by dyman on 16/7/20.
11 | */
12 | public class SharePreferenceManager {
13 |
14 | private Context context;
15 | private SharedPreferences sp = null;
16 | private SharedPreferences.Editor edit;
17 |
18 | public SharePreferenceManager(Context context) {
19 | this.context = context;
20 | sp = context.getSharedPreferences("setting", Activity.MODE_PRIVATE);
21 | edit = sp.edit();
22 | }
23 |
24 |
25 | public void setLastPath(String lastPath) {
26 | edit.putString("lastPath", lastPath);
27 | edit.commit();
28 | }
29 |
30 | public String getLastPath() {
31 | return sp.getString("lastPath", Environment.getExternalStorageDirectory().getAbsolutePath());
32 | }
33 |
34 |
35 | public void setRenderModel(int model) {
36 | edit.putInt("renderModel", model);
37 | edit.commit();
38 | }
39 |
40 | public int getRenderModel() {
41 | return sp.getInt("renderModel", GLES20.GL_TRIANGLES);
42 | }
43 |
44 | /**
45 | * 画坐标
46 | * @param b
47 | */
48 | public void setDrawLinesEnable(boolean b){
49 | edit.putBoolean("drawLines", b);
50 | edit.commit();
51 | }
52 |
53 | public boolean isDrawLines() {
54 | return sp.getBoolean("drawLines", false);
55 | }
56 |
57 | /**
58 | * 画网格
59 | * @param b
60 | */
61 | public void setDrawGridsEnable(boolean b){
62 | edit.putBoolean("drawGrids", b);
63 | edit.commit();
64 | }
65 |
66 | public boolean isDrawGrids() {
67 | return sp.getBoolean("drawGrids", false);
68 | }
69 |
70 |
71 | public void setAnalysisWay(boolean b) {
72 | edit.putBoolean("isSMM", b);
73 | edit.commit();
74 | }
75 |
76 | public boolean isSMM() { return sp.getBoolean("isSMM", false); }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_filelist_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
14 |
20 |
21 |
26 |
35 |
42 |
43 |
44 |
49 |
50 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/assets/easy_show_vertex_clipplane.sh:
--------------------------------------------------------------------------------
1 | uniform mat4 uMVPMatrix; //总变换矩阵
2 | uniform mat4 uMMatrix; //变换矩阵
3 | uniform vec3 uLightLocation; //光源位置
4 | uniform vec3 uCamera; //摄像机位置
5 | attribute vec3 aPosition; //顶点位置
6 | attribute vec3 aNormal; //顶点法向量
7 | uniform vec4 u_clipPlane;//剪裁平面
8 | //用于传递给片元着色器的变量
9 | varying vec4 ambient;
10 | varying vec4 diffuse;
11 | varying vec4 specular;
12 | varying float u_clipDist;
13 |
14 | //定位光光照计算的方法
15 | void pointLight( //定位光光照计算的方法
16 | in vec3 normal, //法向量
17 | inout vec4 ambient, //环境光最终强度
18 | inout vec4 diffuse, //散射光最终强度
19 | inout vec4 specular, //镜面光最终强度
20 | in vec3 lightLocation, //光源位置
21 | in vec4 lightAmbient, //环境光强度
22 | in vec4 lightDiffuse, //散射光强度
23 | in vec4 lightSpecular //镜面光强度
24 | ){
25 | ambient=lightAmbient; //直接得出环境光的最终强度
26 | vec3 normalTarget=aPosition+normal; //计算变换后的法向量
27 | vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
28 | newNormal=normalize(newNormal); //对法向量规格化
29 | //计算从表面点到摄像机的向量
30 | vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);
31 | //计算从表面点到光源位置的向量vp
32 | vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
33 | vp=normalize(vp);//格式化vp
34 | vec3 halfVector=normalize(vp+eye); //求视线与光线的半向量
35 | float shininess=50.0; //粗糙度,越小越光滑
36 | float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量与vp的点积与0的最大值
37 | diffuse=lightDiffuse*nDotViewPosition; //计算散射光的最终强度
38 | float nDotViewHalfVector=dot(newNormal,halfVector); //法线与半向量的点积
39 | float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); //镜面反射光强度因子
40 | specular=lightSpecular*powerFactor; //计算镜面光的最终强度
41 | }
42 |
43 |
44 | void main()
45 | {
46 | gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置
47 | pointLight(normalize(aNormal),ambient,diffuse,specular,
48 | uLightLocation,vec4(0.1,0.1,0.1,1.0),vec4(0.7,0.7,0.7,1.0),vec4(0.3,0.3,0.3,1.0));
49 |
50 | //将顶点位置(x0,y0,z0)代入平面方程Ax+By+Cz+D=0,
51 | //若Ax0+By0+Cz0+D>0,则顶点在平面的上侧,反之在平面的下侧
52 | u_clipDist = dot(aPosition.xyz, u_clipPlane.xyz) +u_clipPlane.w;
53 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_show_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
15 |
21 |
22 |
27 |
34 |
35 |
36 |
42 |
47 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/utils/IOUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.utils;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.Closeable;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 |
9 | public class IOUtils {
10 | private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
11 |
12 | /**
13 | * Convert input stream into byte[].
14 | *
15 | * @param input
16 | * @return Array of Byte
17 | * @throws IOException
18 | */
19 | public static byte[] toByteArray(InputStream input) throws IOException {
20 | ByteArrayOutputStream output = new ByteArrayOutputStream();
21 | copy(input, output);
22 | return output.toByteArray();
23 | }
24 |
25 | /**
26 | * Copy length size of input stream to output stream.
27 | * This method will NOT close input and output stream.
28 | *
29 | * @param input
30 | * @param output
31 | * @return long copied length
32 | * @throws IOException
33 | */
34 | private static long copy(InputStream input, OutputStream output) throws IOException {
35 | byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
36 | long count = 0;
37 | int n = 0;
38 | while ((n = input.read(buffer)) != -1) {
39 | output.write(buffer, 0, n);
40 | count += n;
41 | }
42 | return count;
43 | }
44 |
45 | /**
46 | * Copy length size of input stream to output stream.
47 | *
48 | * @param input
49 | * @param output
50 | * @return long copied length
51 | * @throws IOException
52 | */
53 | public static long copy(InputStream input, OutputStream output, int length) throws IOException {
54 | byte[] buffer = new byte[length];
55 | int count = 0;
56 | int n = 0;
57 | int max = length;
58 | while ((n = input.read(buffer, 0, max)) != -1) {
59 | output.write(buffer, 0, n);
60 | count += n;
61 | if (count > length) {
62 | break;
63 | }
64 |
65 | max -= n;
66 | if (max <= 0) {
67 | break;
68 | }
69 | }
70 | return count;
71 | }
72 |
73 | /**
74 | * Close closeable quietly.
75 | *
76 | * @param closeable
77 | */
78 | public static void closeQuietly(Closeable closeable) {
79 | if (closeable == null) {
80 | return;
81 | }
82 |
83 | try {
84 | closeable.close();
85 | } catch (Throwable e) {
86 | System.out.println("文件关闭失败");
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_file_directory.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
23 |
24 |
25 |
30 |
35 |
36 |
40 |
45 |
46 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/adapter/FolderListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.LinearLayout;
9 | import android.widget.TextView;
10 |
11 | import com.dyman.show3dmodel.R;
12 | import com.dyman.show3dmodel.adapter.listener.OnAdapterItemListener;
13 | import com.dyman.show3dmodel.bean.FileBean;
14 | import com.dyman.show3dmodel.bean.FolderBean;
15 |
16 | import java.util.List;
17 |
18 |
19 | /**
20 | * Created by dyman on 16/7/24.
21 | */
22 | public class FolderListAdapter extends RecyclerView.Adapter {
23 |
24 | private List mData;
25 |
26 | public FolderListAdapter(List data) {
27 | this.mData = data;
28 | }
29 |
30 | @Override
31 | public int getItemCount() {
32 | return mData.size();
33 | }
34 |
35 | @Override
36 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
37 | View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_frequent_folder, parent, false);
38 | return new MyViewHolder(v);
39 | }
40 |
41 | @Override
42 | public void onBindViewHolder(MyViewHolder holder, int position) {
43 | FolderBean bean = mData.get(position);
44 | holder.folderName.setText(bean.getFolderName());
45 |
46 | }
47 |
48 | //自定义回调接口
49 | public OnAdapterItemListener itemListener;
50 | public void setOnItemClickListener(OnAdapterItemListener itemClickListener) {
51 | this.itemListener = itemClickListener;
52 | }
53 |
54 | public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
55 | private LinearLayout openFolder;
56 | private TextView folderName;
57 |
58 | public MyViewHolder(View itemView) {
59 | super(itemView);
60 | openFolder = (LinearLayout) itemView.findViewById(R.id.openFolder_ll_item_frequent_folder);
61 | folderName = (TextView) itemView.findViewById(R.id.folderName_tv_item_frequent_folder);
62 |
63 | openFolder.setOnClickListener(this);
64 | }
65 |
66 | @Override
67 | public void onClick(View view) {
68 | if (itemListener != null){
69 | itemListener.onItemClick(view, getLayoutPosition());
70 | }
71 | }
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Show3D
3 | 打开
4 | 常用
5 | 设置
6 | 关于
7 | 确认
8 | 取消
9 |
10 | 请等一会.
11 | 文件加载中, 请稍后
12 | STL 文件解析失败.
13 | 文件过大,解析失败!
14 | 关闭对话框
15 | 父目录
16 | 选择STL文件
17 |
18 | STL
19 | OBJ
20 | 3DS
21 | 位置
22 | 手机
23 | 常用
24 | 打开
25 | 渲染设置
26 | 线渲染
27 | 面渲染
28 | 显示设置
29 | 显示坐标
30 | 显示网格
31 | 目录名
32 | 文件夹名称
33 | 文件名
34 | QQ
35 | Download
36 | 我的文档
37 | 微信
38 | 打开手机目录
39 | 查找stl文件
40 | 查找obj文件
41 | 查找3ds文件
42 | 文件不存在
43 |
44 | 文件大于10M可能会解析失败
45 | 分享到
46 | 已删除该记录
47 | 文件无效,是否删除?
48 | 撤销
49 |
50 |
51 | 这是一个浏览3D文件的app, 目前支持的文件格式有比较常用的STL、OBJ和3DS格式,暂不支持dwg、stp、igs等格式,将继续完善。
52 | Email: dyman_zy@163.com
53 | GitHub: https://github.com/DymanZy
54 | 如使用过程有任意疑问或建议,可以联系我。
55 |
56 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/bean/ObjProObject.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.bean;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.dyman.easyshow3d.imp.ModelLoaderListener;
7 | import com.dyman.easyshow3d.thread.AnalysisThreadHelper;
8 |
9 | import java.nio.ByteBuffer;
10 | import java.nio.ByteOrder;
11 |
12 | /**
13 | * Created by dyman on 18/5/16.
14 | */
15 |
16 | public class ObjProObject extends ModelObject{
17 |
18 | private static final String TAG = ObjProObject.class.getName();
19 |
20 | ModelLoaderListener listener;
21 | AnalysisThreadHelper helper;
22 |
23 |
24 | public ObjProObject(byte[] objByte, Context context, int drawMode, ModelLoaderListener listener) {
25 | this.modelType = "obj";
26 | this.drawWay = drawMode;
27 | this.listener = listener;
28 |
29 | parseModel(objByte, context);
30 | }
31 |
32 | @Override
33 | public void parseModel(byte[] data, Context context) {
34 | Log.e(TAG, "start parse model");
35 | helper = new AnalysisThreadHelper(3, this, listener);
36 | helper.analysis(data);
37 | }
38 |
39 | /**
40 | * 初始化顶点坐标与着色数据的方法(Obj和Stl的不同)
41 | * @param vertices
42 | * @param normals
43 | */
44 | public void initVertexData(float[] vertices,float[] normals) {
45 | //顶点坐标数据的初始化================begin============================
46 | vCount=vertices.length/3;
47 |
48 |
49 | //创建顶点坐标数据缓冲
50 | //vertices.length*4是因为一个整数四个字节
51 | ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
52 | vbb.order(ByteOrder.nativeOrder());//设置字节顺序
53 | mVertexBuffer = vbb.asFloatBuffer();//转换为Float型缓冲
54 | mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
55 | mVertexBuffer.position(0);//设置缓冲区起始位置
56 | //特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
57 | //转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
58 | //顶点坐标数据的初始化================end============================
59 |
60 |
61 |
62 | //顶点法向量数据的初始化================begin============================
63 | ByteBuffer cbb = ByteBuffer.allocateDirect(normals.length*4);
64 | cbb.order(ByteOrder.nativeOrder());//设置字节顺序
65 | mNormalBuffer = cbb.asFloatBuffer();//转换为Float型缓冲
66 | mNormalBuffer.put(normals);//向缓冲区中放入顶点法向量数据
67 | mNormalBuffer.position(0);//设置缓冲区起始位置
68 | //特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
69 | //转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
70 | //顶点着色数据的初始化================end============================
71 | }
72 |
73 | @Override
74 | public void cancelTask() {
75 | // TODO: 停止AnalysisThreadHelper的工作
76 | Log.e(TAG, "cancelTask: TODO: 停止AnalysisThreadHelper的工作");
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
20 |
21 |
22 |
29 |
30 |
31 |
37 |
41 |
42 |
48 |
49 |
55 |
56 |
61 |
62 |
67 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/adapter/FileListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.adapter;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 | import android.view.LayoutInflater;
10 |
11 | import com.dyman.show3dmodel.R;
12 | import com.dyman.show3dmodel.bean.FileBean;
13 | import com.dyman.show3dmodel.utils.FileUtils;
14 |
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * Created by dyman on 16/8/20.
20 | */
21 | public class FileListAdapter extends BaseSwipListAdapter {
22 |
23 | private static final String TAG = "FileListAdapter";
24 | private List mData;
25 | private Context context;
26 | private LayoutInflater inflater;
27 |
28 | public FileListAdapter(Context context, List list){
29 | this.context = context;
30 | this.mData = list;
31 | inflater = LayoutInflater.from(context);
32 | }
33 |
34 | @Override
35 | public int getCount() {
36 | return mData.size();
37 | }
38 |
39 | @Override
40 | public Object getItem(int i) {
41 | return mData.get(i);
42 | }
43 |
44 | @Override
45 | public long getItemId(int i) {
46 | return i;
47 | }
48 |
49 | @Override
50 | public View getView(final int i, View convertView, ViewGroup parent) {
51 | FileBean bean = mData.get(i);
52 | MyViewHolder holder = null;
53 | if (convertView == null) {
54 | convertView = inflater.inflate(R.layout.item_filelist_main, parent, false);
55 | holder = new MyViewHolder(
56 | (ImageView) convertView.findViewById(R.id.fileType_iv_item_fileList_main),
57 | (TextView) convertView.findViewById(R.id.fileName_tv_item_fileList_main),
58 | (TextView) convertView.findViewById(R.id.createTime_tv_item_fileList_main)
59 | );
60 | convertView.setTag(holder);
61 | } else {
62 | holder = (MyViewHolder) convertView.getTag();
63 | }
64 | int imageRes = FileUtils.getImageFromType(bean.getFileType());
65 | holder.fileImage.setImageResource(imageRes == 0 ? R.mipmap.ic_file_model : imageRes);
66 | holder.fileName.setText(bean.getFileName());
67 | holder.fileTime.setText(bean.getCreateTime());
68 |
69 | return convertView;
70 | }
71 |
72 | private class MyViewHolder{
73 | private ImageView fileImage;
74 | private TextView fileName;
75 | private TextView fileTime;
76 |
77 | public MyViewHolder(ImageView fileImage, TextView fileName, TextView fileTime) {
78 | super();
79 | this.fileImage = fileImage;
80 | this.fileName = fileName;
81 | this.fileTime = fileTime;
82 | }
83 | }
84 |
85 | @Override
86 | public boolean getSwipEnableByPosition(int position) {
87 | if (position % 2 == 0) {
88 | return false;
89 | }
90 | return true;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/adapter/FileRvListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.LinearLayout;
10 | import android.widget.TextView;
11 |
12 | import com.dyman.show3dmodel.R;
13 | import com.dyman.show3dmodel.adapter.listener.OnAdapterItemListener;
14 | import com.dyman.show3dmodel.bean.FileBean;
15 | import com.dyman.show3dmodel.utils.FileUtils;
16 |
17 | import java.util.List;
18 |
19 |
20 | /**
21 | * Created by dyman on 16/7/24.
22 | */
23 | public class FileRvListAdapter extends RecyclerView.Adapter {
24 |
25 | private List mData;
26 | private Context context;
27 |
28 | public FileRvListAdapter(Context context, List data) {
29 | this.mData = data;
30 | this.context = context;
31 | }
32 |
33 | @Override
34 | public int getItemCount() {
35 | return mData.size();
36 | }
37 |
38 | @Override
39 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
40 | View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_filelist_main, parent, false);
41 | return new MyViewHolder(v);
42 | }
43 |
44 | @Override
45 | public void onBindViewHolder(MyViewHolder holder, int position) {
46 | FileBean bean = mData.get(position);
47 |
48 | holder.fileNameTv.setText(bean.getFileName());
49 | int fileImage = FileUtils.getImageFromType(bean.getFileType());
50 | holder.fileImageIv.setImageResource(fileImage == 0 ? R.mipmap.ic_file_model : fileImage);
51 | holder.updateTimeTv.setText(bean.getCreateTime());
52 |
53 | }
54 |
55 | //自定义回调接口
56 | public OnAdapterItemListener itemListener;
57 | public void setOnItemClickListener(OnAdapterItemListener itemClickListener) {
58 | this.itemListener = itemClickListener;
59 | }
60 |
61 | public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
62 | private LinearLayout openFile;
63 | private TextView fileNameTv;
64 | private ImageView fileImageIv;
65 | private TextView updateTimeTv;
66 |
67 | public MyViewHolder(View itemView) {
68 | super(itemView);
69 | openFile = (LinearLayout) itemView.findViewById(R.id.openFile_ll_item_fileList_main);
70 | fileNameTv = (TextView) itemView.findViewById(R.id.fileName_tv_item_fileList_main);
71 | fileImageIv = (ImageView) itemView.findViewById(R.id.fileType_iv_item_fileList_main);
72 | updateTimeTv = (TextView) itemView.findViewById(R.id.createTime_tv_item_fileList_main);
73 |
74 | openFile.setOnClickListener(this);
75 | }
76 |
77 | @Override
78 | public void onClick(View view) {
79 | if (itemListener != null){
80 | itemListener.onItemClick(view, getLayoutPosition());
81 | }
82 | }
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EasyShow3D
2 | 简单易用的3D模型解析库,基于OpenGL ES渲染,支持OBJ、STL和3DS等格式的3D模型文件的解析浏览,同时还支持对模型的旋转和缩放操作。以后还会继续更新,目标是做成一个使用简单、功能齐全的3D模型解析库。欢迎大家star,fork。
3 |
4 | ## 个人项目(例子)
5 |
6 | 这是一个浏览3D 模型的 APP, 基于OpenGLES 2.0进行开发,目前支持的3D模型格式有OBJ、STL和3DS 等格式的3D模型文件, 暂不支持 stp、igs 、dwg 等格式,如有这方面的解决方法,请多多指教。
7 | 目前在尝试使用多线程解析OBJ模型,但整体效果并不算很好,仍在开发过程中。
8 |
9 | [传送门](https://www.pgyer.com/o4n9)
10 |
11 |
12 |
13 | 这里提供两个[示例模型](model/)
14 |
15 | ## Screenshots
16 |
17 |
18 |
19 | #### 模型解析示例
20 |
21 |
22 |
23 | ## 接入
24 |
25 | 1.在根目录下的build.gradle添加:
26 |
27 | ```java
28 | allprojects {
29 | repositories {
30 | ...
31 | maven { url 'https://www.jitpack.io' }
32 | }
33 | }
34 | ```
35 |
36 | 2.添加依赖
37 |
38 | ```java
39 | dependencies {
40 | compile 'com.github.DymanZy:EasyShow3D:1.2'
41 | }
42 | ```
43 |
44 | ## 使用
45 |
46 | #### 1.添加解析模型的布局:
47 |
48 | ```java
49 |
53 | ```
54 |
55 | #### 2.设置需要解析的模型
56 |
57 | ```java
58 | ModelFactory.decodeFile(ShowModelActivity.this, filePath, new ModelLoaderListener() {
59 | @Override
60 | public void loadedUpdate(float progress) {
61 | Log.i(TAG, "模型解析进度: " + progress);
62 | }
63 |
64 | @Override
65 | public void loadedFinish(ModelObject modelObject) {
66 | if (modelObject != null) {
67 | // 解析完成,显示模型
68 | showModelView.setModelObject(modelObject);
69 | }
70 | }
71 |
72 | @Override
73 | public void loaderCancel() {
74 | }
75 |
76 | @Override
77 | public void loaderFailure() {
78 | }
79 | });
80 | ```
81 |
82 | 其中filePath为模型的文件路径,自动区分Obj、Stl、3ds等格式进行解析,统一通过ModelLoaderListener的三个回调接口返回解析结果。最后通过 showModelView.setModelObject(modelObject) 即可显示模型。
83 |
84 | ## Instructions
85 |
86 | - 如果你有任何意见,bug,问题都可以给我提Issuse,我会第一时间关注并解决。
87 | - 目前还在尝试使用多线程加快OBJ文件的解析速度,敬请期待
88 |
89 |
90 | ## Thanks to the open source project
91 |
92 | - [SwipeMenuListView](https://github.com/baoyongzhang/SwipeMenuListView)
93 |
94 |
95 | ## 关于我
96 |
97 | - Email:dyman_zy@163.com
98 | - GitHub: [DymanZy](https://github.com/DymanZy)
99 | - 博客: [dyman's blog](https://dymanzy.github.io/)
100 |
101 | ## License
102 |
103 | ```
104 | Copyright 2017 dyman
105 |
106 | Licensed under the Apache License, Version 2.0 (the "License");
107 | you may not use this file except in compliance with the License.
108 | You may obtain a copy of the License at
109 |
110 | http://www.apache.org/licenses/LICENSE-2.0
111 |
112 | Unless required by applicable law or agreed to in writing, software
113 | distributed under the License is distributed on an "AS IS" BASIS,
114 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
115 | See the License for the specific language governing permissions and
116 | limitations under the License.
117 | ```
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/utils/SnackBarUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.utils;
2 |
3 | import android.support.design.widget.Snackbar;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by dyman on 16/8/21.
8 | */
9 | public class SnackBarUtils {
10 |
11 | private static final int COLOR_DANGER = 0xffa94442;//red
12 | private static final int COLOR_SUCCESS = 0xff3c763d;//green
13 | private static final int COLOR_INFO = 0xff0b80b9;//blue
14 | private static final int COLOR_WARNING = 0xffdee011;//yellow
15 |
16 | private static final int ACTION_COLOR = 0xffe0e0e0;//white
17 |
18 | private Snackbar mSnackbar;
19 |
20 |
21 | private SnackBarUtils(Snackbar snackbar) {
22 | this.mSnackbar = snackbar;
23 | }
24 |
25 |
26 | public static SnackBarUtils makeShort(View view, String text) {
27 | Snackbar snackbar = Snackbar.make(view, text, Snackbar.LENGTH_SHORT);
28 | return new SnackBarUtils(snackbar);
29 | }
30 |
31 |
32 | public static SnackBarUtils makeLong(View view, String text) {
33 | Snackbar snackbar = Snackbar.make(view, text, Snackbar.LENGTH_LONG);
34 | return new SnackBarUtils(snackbar);
35 | }
36 |
37 |
38 | private View getSnackBarLayout(Snackbar snackbar) {
39 | if (snackbar != null) {
40 | return snackbar.getView();
41 | }
42 | return null;
43 |
44 | }
45 |
46 |
47 | private Snackbar setSnackBarBackColor(int colorId) {
48 | View snackBarView = getSnackBarLayout(mSnackbar);
49 | if (snackBarView != null) {
50 | snackBarView.setBackgroundColor(colorId);
51 | }
52 | return mSnackbar;
53 | }
54 |
55 |
56 | public void info() {
57 | setSnackBarBackColor(COLOR_INFO);
58 | show();
59 | }
60 |
61 | public void info(String actionText, View.OnClickListener listener) {
62 | setSnackBarBackColor(COLOR_INFO);
63 | show(actionText, listener);
64 | }
65 |
66 | public void warning() {
67 | setSnackBarBackColor(COLOR_WARNING);
68 | show();
69 | }
70 |
71 | public void warning(String actionText, View.OnClickListener listener) {
72 | setSnackBarBackColor(COLOR_WARNING);
73 | show(actionText, listener);
74 | }
75 |
76 | public void danger() {
77 | setSnackBarBackColor(COLOR_DANGER);
78 | show();
79 | }
80 |
81 | public void danger(String actionText, View.OnClickListener listener) {
82 | setSnackBarBackColor(COLOR_DANGER);
83 | show(actionText, listener);
84 | }
85 |
86 | public void confirm() {
87 | setSnackBarBackColor(COLOR_SUCCESS);
88 | show();
89 | }
90 |
91 | public void confirm(String actionText, View.OnClickListener listener) {
92 | setSnackBarBackColor(COLOR_SUCCESS);
93 | show(actionText, listener);
94 | }
95 |
96 |
97 | public void show() {
98 | mSnackbar.show();
99 | }
100 |
101 | public void show(String actionText, View.OnClickListener listener) {
102 | mSnackbar.setActionTextColor(ACTION_COLOR);
103 | mSnackbar.setAction(actionText, listener).show();
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/view/ShowModelView.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 | import android.view.MotionEvent;
7 |
8 | import com.dyman.easyshow3d.bean.ModelObject;
9 |
10 |
11 | /**
12 | * Created by dyman on 16/7/25.
13 | */
14 | public class ShowModelView extends ModelView{
15 |
16 | private static final String TAG = "ShowModelView";
17 |
18 | public ShowModelView(Context context) {
19 | this(context, null);
20 | }
21 |
22 | public ShowModelView(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 |
27 |
28 | /**
29 | * 触摸事件回调方法,支持动作:单指旋转,双指缩放
30 | */
31 | @Override
32 | public boolean onTouchEvent(MotionEvent e) {
33 | float y = e.getY();
34 | float x = e.getX();
35 |
36 | switch (e.getAction() & MotionEvent.ACTION_MASK) {
37 |
38 | case MotionEvent.ACTION_DOWN:
39 | if (touchMode ==TOUCH_NONE && e.getPointerCount() == 1){
40 | touchMode = TOUCH_DRAG;
41 | mPreviousX = e.getX();
42 | mPreviousY = e.getY();
43 | }
44 | break;
45 |
46 | case MotionEvent.ACTION_POINTER_DOWN:
47 | if (e.getPointerCount() >= 2){
48 | pinchStartDistance = getPinchDistance(e);
49 | if (pinchStartDistance >= 50f){
50 | touchMode = TOUCH_ZOOM;
51 | }
52 | }
53 | break;
54 |
55 | case MotionEvent.ACTION_MOVE:
56 | if (touchMode == TOUCH_ZOOM && pinchStartDistance > 0){
57 | changeScale = getPinchDistance(e) / pinchStartDistance;
58 | wholeScale = changeScale * previousScale;
59 | } else if(touchMode == TOUCH_DRAG){
60 | float dy = y - mPreviousY;//计算触控笔Y位移
61 | float dx = x - mPreviousX;//计算触控笔X位移
62 | mRenderer.yAngle += dx * TOUCH_SCALE_FACTOR;//设置沿x轴旋转角度
63 | mRenderer.zAngle += dy * TOUCH_SCALE_FACTOR;//设置沿z轴旋转角度
64 | }
65 | requestRender();
66 | mPreviousY = y;//记录触控笔位置
67 | mPreviousX = x;//记录触控笔位置
68 | break;
69 |
70 | case MotionEvent.ACTION_POINTER_UP:
71 | if (touchMode == TOUCH_ZOOM){
72 | touchMode = TOUCH_NONE;
73 | previousScale = wholeScale;//记录缩放倍数
74 | }
75 | break;
76 |
77 | case MotionEvent.ACTION_UP:
78 | if (touchMode == TOUCH_DRAG){ touchMode = TOUCH_NONE; }
79 | break;
80 | }
81 | return true;
82 | }
83 |
84 |
85 | /**
86 | * 计算两指间的距离
87 | * @param event
88 | * @return
89 | */
90 | private float getPinchDistance(MotionEvent event) {
91 | float x=0;
92 | float y=0;
93 | try {
94 | x = event.getX(0) - event.getX(1);
95 | y = event.getY(0) - event.getY(1);
96 | } catch (IllegalArgumentException e) {
97 | e.printStackTrace();
98 | }
99 | return (float) Math.sqrt(x * x + y * y);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/ModelFactory.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.util.Log;
6 |
7 | import com.dyman.easyshow3d.bean.ModelObject;
8 | import com.dyman.easyshow3d.bean.ObjObject;
9 | import com.dyman.easyshow3d.bean.ObjProObject;
10 | import com.dyman.easyshow3d.bean.StlObject;
11 | import com.dyman.easyshow3d.imp.ModelLoaderListener;
12 | import com.dyman.easyshow3d.utils.FileUtils;
13 |
14 | import java.io.File;
15 | import java.io.IOException;
16 |
17 | import javax.security.auth.login.LoginException;
18 |
19 | /**
20 | * Created by dyman on 2017/9/13.
21 | */
22 |
23 | public class ModelFactory {
24 | private static final String TAG = "ModelFactory";
25 |
26 | private static byte[] modelBytes = null;
27 | private static String modelType;
28 | private static ModelObject modelObject;
29 |
30 | public static void decodeFile(Context context, String filePath, ModelLoaderListener listener) {
31 |
32 | if (FileUtils.isNullString(filePath)) {
33 | throw new IllegalArgumentException("filePath can't be null!");
34 | }
35 |
36 | modelType = FileUtils.getType(filePath).toLowerCase();
37 | File file = new File(filePath);
38 | try {
39 | modelBytes = FileUtils.getFileBytes(context, Uri.fromFile(file));
40 | } catch (IOException e) {
41 | e.printStackTrace();
42 | }
43 |
44 | if (modelBytes == null) {
45 | listener.loaderFailure();
46 | return;
47 | }
48 |
49 | modelObject = decodeByteArray(context, modelBytes, listener);
50 | }
51 |
52 |
53 | public static void multiTest(Context context, String filePath, ModelLoaderListener listener) {
54 | modelType = FileUtils.getType(filePath).toLowerCase();
55 | File file = new File(filePath);
56 | try {
57 | modelBytes = FileUtils.getFileBytes(context, Uri.fromFile(file));
58 | } catch (IOException e) {
59 | e.printStackTrace();
60 | }
61 |
62 | if (modelBytes == null) {
63 | listener.loaderFailure();
64 | return;
65 | }
66 |
67 | if (modelType.equals("obj")) {
68 | modelObject = new ObjProObject(modelBytes, context, ModelObject.DRAW_MODEL, listener);
69 | } else {
70 | Log.e(TAG, "multiTest: " + FileUtils.getName(filePath) + " file type must be .obj");
71 | }
72 | }
73 |
74 |
75 | private static ModelObject decodeByteArray(Context context, byte[] data, ModelLoaderListener listener) {
76 |
77 | ModelObject modelObject = null;
78 |
79 | if (modelType.equals("obj")) {
80 | modelObject = new ObjObject(data, context, ModelObject.DRAW_MODEL, listener);
81 | } else if (modelType.equals("stl")) {
82 | modelObject = new StlObject(data, context, ModelObject.DRAW_MODEL, listener);
83 | } else if (modelType.equals("3ds")) {
84 | Log.e(TAG, " can't handle 3ds model");
85 | listener.loaderFailure();
86 | } else {
87 | Log.e(TAG, "model type error!");
88 | listener.loaderFailure();
89 | }
90 |
91 | return modelObject;
92 | }
93 |
94 |
95 | public static void cancelDecode() {
96 | if (modelObject != null) {
97 | modelObject.cancelTask();
98 | } else {
99 | throw new NullPointerException("ModelObject was null, can't call cancelDecode()! please call decodeFile() first.");
100 | }
101 | }
102 |
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/utils/DividerItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.utils;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Rect;
7 | import android.graphics.drawable.Drawable;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.View;
11 |
12 | /**
13 | * Created by man_dzy on 2016/5/13.
14 | */
15 | public class DividerItemDecoration extends RecyclerView.ItemDecoration {
16 |
17 | private static final int[] ATTRS = new int[]{
18 | android.R.attr.listDivider
19 | };
20 |
21 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
22 |
23 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
24 |
25 | private Drawable mDivider;
26 |
27 | private int mOrientation;
28 |
29 | public DividerItemDecoration(Context context, int orientation) {
30 | final TypedArray a = context.obtainStyledAttributes(ATTRS);
31 | mDivider = a.getDrawable(0);
32 | a.recycle();
33 | setOrientation(orientation);
34 | }
35 |
36 | public void setOrientation(int orientation) {
37 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
38 | throw new IllegalArgumentException("invalid orientation");
39 | }
40 | mOrientation = orientation;
41 | }
42 |
43 | @Override
44 | public void onDraw(Canvas c, RecyclerView parent) {
45 | if (mOrientation == VERTICAL_LIST) {
46 | drawVertical(c, parent);
47 | } else {
48 | drawHorizontal(c, parent);
49 | }
50 |
51 | }
52 |
53 |
54 | public void drawVertical(Canvas c, RecyclerView parent) {
55 | final int left = parent.getPaddingLeft();
56 | final int right = parent.getWidth() - parent.getPaddingRight();
57 |
58 | final int childCount = parent.getChildCount();
59 | for (int i = 0; i < childCount; i++) {
60 | final View child = parent.getChildAt(i);
61 | android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
62 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
63 | .getLayoutParams();
64 | final int top = child.getBottom() + params.bottomMargin;
65 | final int bottom = top + mDivider.getIntrinsicHeight();
66 | mDivider.setBounds(left, top, right, bottom);
67 | mDivider.draw(c);
68 | }
69 | }
70 |
71 | public void drawHorizontal(Canvas c, RecyclerView parent) {
72 | final int top = parent.getPaddingTop();
73 | final int bottom = parent.getHeight() - parent.getPaddingBottom();
74 |
75 | final int childCount = parent.getChildCount();
76 | for (int i = 0; i < childCount; i++) {
77 | final View child = parent.getChildAt(i);
78 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
79 | .getLayoutParams();
80 | final int left = child.getRight() + params.rightMargin;
81 | final int right = left + mDivider.getIntrinsicHeight();
82 | mDivider.setBounds(left, top, right, bottom);
83 | mDivider.draw(c);
84 | }
85 | }
86 |
87 | @Override
88 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
89 | if (mOrientation == VERTICAL_LIST) {
90 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
91 | } else {
92 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/utils/ShaderUtil.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.utils;
2 |
3 | import android.content.res.Resources;
4 | import android.opengl.GLES20;
5 | import android.util.Log;
6 |
7 | import java.io.ByteArrayOutputStream;
8 | import java.io.InputStream;
9 |
10 | //加载顶点Shader与片元Shader的工具类
11 | public class ShaderUtil
12 | {
13 | //加载制定shader的方法
14 | public static int loadShader
15 | (
16 | int shaderType, //shader的类型 GLES20.GL_VERTEX_SHADER GLES20.GL_FRAGMENT_SHADER
17 | String source //shader的脚本字符串
18 | )
19 | {
20 | //创建一个新shader
21 | int shader = GLES20.glCreateShader(shaderType);
22 | //若创建成功则加载shader
23 | if (shader != 0)
24 | {
25 | //加载shader的源代码
26 | GLES20.glShaderSource(shader, source);
27 | //编译shader
28 | GLES20.glCompileShader(shader);
29 | //存放编译成功shader数量的数组
30 | int[] compiled = new int[1];
31 | //获取Shader的编译情况
32 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
33 | if (compiled[0] == 0)
34 | {//若编译失败则显示错误日志并删除此shader
35 | Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
36 | Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
37 | GLES20.glDeleteShader(shader);
38 | shader = 0;
39 | }
40 | }
41 | return shader;
42 | }
43 |
44 | //创建shader程序的方法
45 | public static int createProgram(String vertexSource, String fragmentSource)
46 | {
47 | //加载顶点着色器
48 | int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
49 | if (vertexShader == 0)
50 | {
51 | return 0;
52 | }
53 |
54 | //加载片元着色器
55 | int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
56 | if (pixelShader == 0)
57 | {
58 | return 0;
59 | }
60 |
61 | //创建程序
62 | int program = GLES20.glCreateProgram();
63 | //若程序创建成功则向程序中加入顶点着色器与片元着色器
64 | if (program != 0)
65 | {
66 | //向程序中加入顶点着色器
67 | GLES20.glAttachShader(program, vertexShader);
68 | checkGlError("glAttachShader");
69 | //向程序中加入片元着色器
70 | GLES20.glAttachShader(program, pixelShader);
71 | checkGlError("glAttachShader");
72 | //链接程序
73 | GLES20.glLinkProgram(program);
74 | //存放链接成功program数量的数组
75 | int[] linkStatus = new int[1];
76 | //获取program的链接情况
77 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
78 | //若链接失败则报错并删除程序
79 | if (linkStatus[0] != GLES20.GL_TRUE)
80 | {
81 | Log.e("ES20_ERROR", "Could not link program: ");
82 | Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
83 | GLES20.glDeleteProgram(program);
84 | program = 0;
85 | }
86 | }
87 | return program;
88 | }
89 |
90 | //检查每一步操作是否有错误的方法
91 | public static void checkGlError(String op)
92 | {
93 | int error;
94 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR)
95 | {
96 | Log.e("ES20_ERROR", op + ": glError " + error);
97 | throw new RuntimeException(op + ": glError " + error);
98 | }
99 | }
100 |
101 | //从sh脚本中加载shader内容的方法
102 | public static String loadFromAssetsFile(String fname,Resources r)
103 | {
104 | String result=null;
105 | try
106 | {
107 | InputStream in=r.getAssets().open(fname);
108 | int ch=0;
109 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
110 | while((ch=in.read())!=-1)
111 | {
112 | baos.write(ch);
113 | }
114 | byte[] buff=baos.toByteArray();
115 | baos.close();
116 | in.close();
117 | result=new String(buff,"UTF-8");
118 | result=result.replaceAll("\\r\\n","\n");
119 | }
120 | catch(Exception e)
121 | {
122 | e.printStackTrace();
123 | }
124 | return result;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/utils/MatrixState.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.utils;
2 |
3 | import android.opengl.Matrix;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.ByteOrder;
7 | import java.nio.FloatBuffer;
8 | import java.util.Stack;
9 |
10 |
11 | /**
12 | * 存储系统矩阵状态的类
13 | */
14 | public class MatrixState
15 | {
16 | private static float[] mProjMatrix = new float[16];//4x4矩阵 投影用
17 | private static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵
18 | private static float[] currMatrix;//当前变换矩阵
19 | public static float[] lightLocation=new float[]{0,0,0};//定位光光源位置
20 | public static FloatBuffer cameraFB;
21 | public static FloatBuffer lightPositionFB;
22 |
23 | public static Stack mStack=new Stack();//保护变换矩阵的栈
24 |
25 | public static void setInitStack()//获取不变换初始矩阵
26 | {
27 | currMatrix=new float[16];
28 | Matrix.setRotateM(currMatrix, 0, 0, 1, 0, 0);
29 | }
30 |
31 | public static void pushMatrix()//保护变换矩阵
32 | {
33 | mStack.push(currMatrix.clone());
34 | }
35 |
36 | public static void popMatrix()//恢复变换矩阵
37 | {
38 | currMatrix=mStack.pop();
39 | }
40 |
41 | public static void translate(float x,float y,float z)//设置沿xyz轴移动
42 | {
43 | Matrix.translateM(currMatrix, 0, x, y, z);
44 | }
45 |
46 | public static void rotate(float angle,float x,float y,float z)//设置绕xyz轴移动
47 | {
48 | Matrix.rotateM(currMatrix,0,angle,x,y,z);
49 | }
50 |
51 | public static void scale(float x, float y, float z){//改变三维尺寸比例
52 | Matrix.scaleM(currMatrix, 0, x, y, z);
53 | }
54 |
55 |
56 | //设置摄像机
57 | public static void setCamera
58 | (
59 | float cx, //摄像机位置x
60 | float cy, //摄像机位置y
61 | float cz, //摄像机位置z
62 | float tx, //摄像机目标点x
63 | float ty, //摄像机目标点y
64 | float tz, //摄像机目标点z
65 | float upx, //摄像机UP向量X分量
66 | float upy, //摄像机UP向量Y分量
67 | float upz //摄像机UP向量Z分量
68 | )
69 | {
70 | Matrix.setLookAtM
71 | (
72 | mVMatrix,
73 | 0,
74 | cx,
75 | cy,
76 | cz,
77 | tx,
78 | ty,
79 | tz,
80 | upx,
81 | upy,
82 | upz
83 | );
84 |
85 | float[] cameraLocation=new float[3];//摄像机位置
86 | cameraLocation[0]=cx;
87 | cameraLocation[1]=cy;
88 | cameraLocation[2]=cz;
89 |
90 | ByteBuffer llbb = ByteBuffer.allocateDirect(3*4);
91 | llbb.order(ByteOrder.nativeOrder());//设置字节顺序
92 | cameraFB=llbb.asFloatBuffer();
93 | cameraFB.put(cameraLocation);
94 | cameraFB.position(0);
95 | }
96 |
97 | //设置透视投影参数
98 | public static void setProjectFrustum
99 | (
100 | float left, //near面的left
101 | float right, //near面的right
102 | float bottom, //near面的bottom
103 | float top, //near面的top
104 | float near, //near面距离
105 | float far //far面距离
106 | )
107 | {
108 | Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
109 | }
110 |
111 | //设置正交投影参数
112 | public static void setProjectOrtho
113 | (
114 | float left, //near面的left
115 | float right, //near面的right
116 | float bottom, //near面的bottom
117 | float top, //near面的top
118 | float near, //near面距离
119 | float far //far面距离
120 | )
121 | {
122 | Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
123 | }
124 |
125 | //获取具体物体的总变换矩阵
126 | public static float[] getFinalMatrix()
127 | {
128 | float[] mMVPMatrix=new float[16];
129 | Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0);
130 | Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
131 | return mMVPMatrix;
132 | }
133 |
134 | //获取具体物体的变换矩阵
135 | public static float[] getMMatrix()
136 | {
137 | return currMatrix;
138 | }
139 |
140 | //设置灯光位置的方法
141 | public static void setLightLocation(float x,float y,float z)
142 | {
143 | lightLocation[0]=x;
144 | lightLocation[1]=y;
145 | lightLocation[2]=z;
146 | ByteBuffer llbb = ByteBuffer.allocateDirect(3*4);
147 | llbb.order(ByteOrder.nativeOrder());//设置字节顺序
148 | lightPositionFB=llbb.asFloatBuffer();
149 | lightPositionFB.put(lightLocation);
150 | lightPositionFB.position(0);
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/utils/DialogUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.utils;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.opengl.GLES20;
9 | import android.util.DisplayMetrics;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.Window;
14 | import android.view.WindowManager;
15 | import android.widget.CompoundButton;
16 | import android.widget.Switch;
17 | import android.widget.TextView;
18 |
19 | import com.dyman.show3dmodel.R;
20 | import com.dyman.show3dmodel.manager.SharePreferenceManager;
21 |
22 |
23 | /**
24 | * Created by dyman on 16/7/23.
25 | */
26 | public class DialogUtils {
27 |
28 | public static void showAlerDialog(Context context, String message, DialogInterface.OnClickListener onClickListener ){
29 | showAlertDialog(context, null, message, onClickListener);
30 | }
31 |
32 |
33 | public static void showAlertDialog(Context context, String title, String message,
34 | DialogInterface.OnClickListener onClickListener) {
35 | AlertDialog.Builder builder = new AlertDialog.Builder(context);
36 | builder.setTitle(title==null ? "提示": title);
37 | builder.setMessage(message);
38 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
39 | @Override
40 | public void onClick(DialogInterface dialogInterface, int i) {
41 | dialogInterface.dismiss();
42 | }
43 | });
44 | builder.setPositiveButton("确认", onClickListener);
45 | builder.show();
46 | }
47 |
48 |
49 | public static Dialog showSettingDialog(final Activity ctx) {
50 | View v = LayoutInflater.from(ctx).inflate(R.layout.dialog_show_setting, null);
51 | final Dialog dialog = new Dialog(ctx, R.style.noBackgroundDialog);
52 | Switch lineSwitch = (Switch) v.findViewById(R.id.showLines_switch_dialog_show_setting);
53 | Switch gridSwitch = (Switch) v.findViewById(R.id.showGrids_switch_dialog_show_setting);
54 | final SharePreferenceManager sp = new SharePreferenceManager(ctx);
55 | lineSwitch.setChecked(sp.isDrawLines());
56 | gridSwitch.setChecked(sp.isDrawGrids());
57 |
58 | lineSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
59 | @Override
60 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
61 | sp.setDrawLinesEnable(b);
62 | }
63 | });
64 | gridSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
65 | @Override
66 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
67 | sp.setDrawGridsEnable(b);
68 | }
69 | });
70 |
71 | v = setLayoutPadding(ctx, v, 30);
72 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
73 | dialog.setCanceledOnTouchOutside(true);
74 | dialog.setContentView(v);
75 |
76 | return dialog;
77 | }
78 |
79 |
80 | public static Dialog renderSettingDialog(final Activity ctx) {
81 | View v = LayoutInflater.from(ctx).inflate(R.layout.dialog_renderer_setting, null);
82 | final Dialog dialog = new Dialog(ctx, R.style.noBackgroundDialog);
83 | TextView lineRender = (TextView) v.findViewById(R.id.lineRender_dialog_renderer);
84 | TextView triRender = (TextView) v.findViewById(R.id.trianglesRender_dialog_renderer);
85 | final SharePreferenceManager sp = new SharePreferenceManager(ctx);
86 | lineRender.setOnClickListener(new View.OnClickListener() {
87 | @Override
88 | public void onClick(View view) {
89 | sp.setRenderModel(GLES20.GL_LINE_LOOP);
90 | dismissDialog(ctx, dialog);
91 | }
92 | });
93 |
94 | triRender.setOnClickListener(new View.OnClickListener() {
95 | @Override
96 | public void onClick(View view) {
97 | sp.setRenderModel(GLES20.GL_TRIANGLES);
98 | dismissDialog(ctx, dialog);
99 | }
100 | });
101 |
102 |
103 | v = setLayoutPadding(ctx, v, 50);
104 |
105 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
106 | dialog.setCanceledOnTouchOutside(true);
107 | dialog.setContentView(v);
108 |
109 | return dialog;
110 | }
111 |
112 | private static View setLayoutPadding(Activity ctx, View v, int padding){
113 | DisplayMetrics metrics = new DisplayMetrics();
114 | ctx.getWindowManager().getDefaultDisplay().getMetrics(metrics);
115 | int width = metrics.widthPixels - padding*2;
116 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(width, WindowManager.LayoutParams.WRAP_CONTENT);
117 | v.setLayoutParams(params);
118 | return v;
119 | }
120 |
121 |
122 | /**
123 | * 隐藏dialog,加了context生命判断,避免窗口句柄泄漏.
124 | *
125 | * @param ctx dialog依赖的activity
126 | * @param dialog 欲隐藏的dialog
127 | */
128 | public static void dismissDialog(Activity ctx, Dialog dialog) {
129 | if (dialog != null && dialog.isShowing() && ctx != null
130 | && !ctx.isFinishing())
131 | dialog.dismiss();
132 | }
133 |
134 | public interface MyDialogOnClick{
135 | public void modeChange(int newSetting);
136 | }
137 |
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/db/DatabaseHelper.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.db;
2 |
3 | import android.content.ContentValues;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.database.sqlite.SQLiteDatabase;
7 | import android.database.sqlite.SQLiteOpenHelper;
8 |
9 | import com.dyman.show3dmodel.bean.FileBean;
10 | import com.dyman.show3dmodel.manager.SystemBarTintManager;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Created by dyman on 16/8/18.
17 | */
18 | public class DatabaseHelper extends SQLiteOpenHelper {
19 | private static final String TAG = "DatabaseHelper";
20 |
21 | private Context context;
22 | private static final String DATABASE_NAME = "show3d_db";
23 | private static final String TABLE_NAME = "opened_file";
24 | private static final int DATABASE_VERSION = 1;
25 |
26 | public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
27 | super(context, name, factory, version);
28 | this.context = context;
29 | }
30 |
31 | public DatabaseHelper(Context context, String name, int version) {
32 | super(context, name, null, version);
33 | this.context = context;
34 | }
35 |
36 | public DatabaseHelper(Context context){
37 | this(context, DATABASE_NAME, DATABASE_VERSION);
38 | this.context = context;
39 | }
40 |
41 | @Override
42 | public void onCreate(SQLiteDatabase db) {
43 | String sql = "CREATE TABLE IF NOT EXISTS " +
44 | TABLE_NAME + "(" +
45 | FileBean.ID + " integer primary key autoincrement," +
46 | FileBean.FILEPATH + " varchar," +
47 | FileBean.FILENAME + " varchar," +
48 | FileBean.FILETYPE + " varchar," +
49 | FileBean.CREATETIME + " varchar," +
50 | "recentOpenTime" + " long" +
51 | ")";
52 | db.execSQL(sql);
53 | System.out.println("create a database");
54 | }
55 |
56 | @Override
57 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
58 | db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
59 | onCreate(db);
60 | System.out.println("upgrade a database");
61 | }
62 |
63 |
64 | /**
65 | * 插入数据,自检测是否重复插入
66 | * @param bean
67 | */
68 | public void insert(FileBean bean) {
69 | ContentValues values = new ContentValues();
70 |
71 | values.put(FileBean.FILEPATH, bean.getFilePath());
72 | values.put(FileBean.FILENAME, bean.getFileName());
73 | values.put(FileBean.FILETYPE, bean.getFileType().toLowerCase());
74 | values.put(FileBean.CREATETIME, bean.getCreateTime());
75 | values.put("recentOpenTime", System.currentTimeMillis());
76 |
77 | DatabaseHelper databaseHelper = new DatabaseHelper(context);
78 | SQLiteDatabase sqliteDatabase = databaseHelper.getWritableDatabase();
79 | if (!isHave(bean.getFilePath())) {
80 | sqliteDatabase.insert(TABLE_NAME, null, values);
81 | System.out.println("--------------sql---------insert----------fileName="+bean.getFileName());
82 | } else {
83 | sqliteDatabase.update(TABLE_NAME, values, FileBean.FILEPATH+"=?", new String[]{bean.getFilePath
84 | ()});
85 | }
86 |
87 | sqliteDatabase.close();
88 | databaseHelper.close();
89 | }
90 |
91 |
92 | public List selectAll() {
93 | DatabaseHelper databaseHelper = new DatabaseHelper(context);
94 | SQLiteDatabase sqLiteDatabase = databaseHelper.getReadableDatabase();
95 | Cursor cursor = sqLiteDatabase.query(TABLE_NAME, null, null, null, null, null, "recentOpenTime desc");
96 |
97 | List list = new ArrayList<>();
98 | while(cursor.moveToNext()) {
99 | String id = cursor.getString(cursor.getColumnIndex(FileBean.ID));
100 | String filePath = cursor.getString(cursor.getColumnIndex(FileBean.FILEPATH));
101 | String fileName = cursor.getString(cursor.getColumnIndex(FileBean.FILENAME));
102 | String fileType = cursor.getString(cursor.getColumnIndex(FileBean.FILETYPE));
103 | String createTime = cursor.getString(cursor.getColumnIndex(FileBean.CREATETIME));
104 |
105 | FileBean bean = new FileBean();
106 | bean.setId(id);
107 | bean.setFilePath(filePath);
108 | bean.setFileName(fileName);
109 | bean.setFileType(fileType);
110 | bean.setCreateTime(createTime);
111 |
112 | list.add(bean);
113 | }
114 |
115 | cursor.close();
116 | sqLiteDatabase.close();
117 | databaseHelper.close();
118 |
119 | return list;
120 | }
121 |
122 |
123 | public void delete(String fileID) {
124 | DatabaseHelper databaseHelper = new DatabaseHelper(context);
125 | SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
126 | sqLiteDatabase.delete(TABLE_NAME, FileBean.ID+"=?", new String[]{fileID});
127 | System.out.println("---------------sql-------delete-------------- fileID="+fileID);
128 | sqLiteDatabase.close();
129 | databaseHelper.close();
130 | }
131 |
132 | private boolean isHave(String filePath) {
133 | boolean result = false;
134 |
135 | DatabaseHelper databaseHelper = new DatabaseHelper(context);
136 | SQLiteDatabase sqLiteDatabase = databaseHelper.getReadableDatabase();
137 |
138 | Cursor cursor = sqLiteDatabase.query(TABLE_NAME,
139 | new String[] {FileBean.ID},
140 | FileBean.FILEPATH+"=?",
141 | new String[]{filePath},
142 | null, null, null);
143 |
144 | if (cursor.moveToFirst()) {
145 | System.out.println("该数据存在");
146 | result = true;
147 | } else {
148 | System.out.println("该数据不存在");
149 | }
150 |
151 | cursor.close();
152 | sqLiteDatabase.close();
153 | databaseHelper.close();
154 | return result;
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/thread/FaceThread.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.thread;
2 |
3 | import com.dyman.easyshow3d.bean.ObjProObject;
4 | import com.dyman.easyshow3d.utils.Normal;
5 |
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 | import java.util.HashSet;
9 |
10 | /**
11 | * Created by dyman on 18/5/17.
12 | */
13 |
14 | public class FaceThread extends Thread {
15 |
16 |
17 | private static final String TAG = "FaceThread";
18 |
19 | /** 定义解析的起始点 */
20 | private int start;
21 | /** 定义解析的结束点 */
22 | private int end;
23 | /** obj文件的数据 */
24 | private ArrayList fLines;
25 | /** 当前线程的ID */
26 | private int threadID;
27 | /** 解析完成的回调 */
28 | private IAnalysisFinishCallback finishCallback;
29 |
30 | private float[] alv;
31 | private ObjProObject objModel;
32 |
33 | //顶点组装面索引列表--根据面的信息从文件中加载
34 | private ArrayList alFaceIndex=new ArrayList<>();
35 | //平均前各个索引对应的点的法向量集合Map
36 | //此HashMap的key为点的索引, value为点所在的各个面的法向量的集合
37 | private HashMap> hmn=new HashMap<>();
38 |
39 | private boolean isFinish = false;
40 | private float[] vertices, normals;
41 |
42 |
43 | public FaceThread(int threadID, ArrayList fLines, float[] vertices, float[] normals, int start, int end, float[] alv, ObjProObject
44 | objModel, IAnalysisFinishCallback finishCallback) {
45 | this.threadID = threadID;
46 | this.fLines = fLines;
47 | this.start = start;
48 | this.end = end;
49 | this.alv = alv;
50 | this.finishCallback = finishCallback;
51 | this.objModel = objModel;
52 | this.vertices = vertices;
53 | this.normals = normals;
54 | }
55 |
56 |
57 | @Override
58 | public void run() {
59 | ArrayList normalsList = new ArrayList<>();
60 |
61 | for (int i = start; i < end; i++) {
62 | String[] tempsa = fLines.get(i);
63 | if (tempsa[0].trim().equals("f")) {
64 | int[] index = new int[3];
65 |
66 | //计算第0个顶点的索引,并获取此顶点的XYZ三个坐标
67 | index[0]=Integer.parseInt(tempsa[1].split("/")[0])-1;
68 | float x0=alv[3*index[0]];
69 | float y0=alv[3*index[0]+1];
70 | float z0=alv[3*index[0]+2];
71 | vertices[i * 9 + 0] = x0;
72 | vertices[i * 9 + 1] = y0;
73 | vertices[i * 9 + 2] = z0;
74 | objModel.adjustMaxMin(x0, y0, z0);
75 |
76 | //计算第1个顶点的索引,并获取此顶点的XYZ三个坐标
77 | index[1]=Integer.parseInt(tempsa[2].split("/")[0])-1;
78 | float x1=alv[3*index[1]];
79 | float y1=alv[3*index[1]+1];
80 | float z1=alv[3*index[1]+2];
81 | vertices[i * 9 + 3] = x1;
82 | vertices[i * 9 + 4] = y1;
83 | vertices[i * 9 + 5] = z1;
84 | objModel.adjustMaxMin(x1, y1, z1);
85 |
86 | //计算第2个顶点的索引,并获取此顶点的XYZ三个坐标
87 | index[2]=Integer.parseInt(tempsa[3].split("/")[0])-1;
88 | float x2=alv[3*index[2]];
89 | float y2=alv[3*index[2]+1];
90 | float z2=alv[3*index[2]+2];
91 | vertices[i * 9 + 6] = x2;
92 | vertices[i * 9 + 7] = y2;
93 | vertices[i * 9 + 8] = z2;
94 | objModel.adjustMaxMin(x2, y2, z2);
95 |
96 | //记录此面的顶点索引
97 | alFaceIndex.add(index[0]);
98 | alFaceIndex.add(index[1]);
99 | alFaceIndex.add(index[2]);
100 |
101 | //通过三角形面两个边向量0-1,0-2求叉积得到此面的法向量
102 | //求0号点到1号点的向量
103 | float vxa=x1-x0;
104 | float vya=y1-y0;
105 | float vza=z1-z0;
106 | //求0号点到2号点的向量
107 | float vxb=x2-x0;
108 | float vyb=y2-y0;
109 | float vzb=z2-z0;
110 | //通过求两个向量的叉积计算法向量
111 | float[] vNormal=vectorNormal(getCrossProduct(
112 | vxa,vya,vza,vxb,vyb,vzb
113 | ));
114 |
115 | for(int tempInxex:index) {//记录每个索引点的法向量到平均前各个索引对应的点的法向量集合组成的Map中
116 | //获取当前索引对应点的法向量集合
117 | HashSet hsn=hmn.get(tempInxex);
118 | if(hsn==null) {//若集合不存在则创建
119 | hsn=new HashSet<>();
120 | }
121 | //将此点的法向量添加到集合中
122 | //由于Normal类重写了equals方法,因此同样的法向量不会重复出现在此点
123 | //对应的法向量集合中
124 | hsn.add(new Normal(vNormal[0],vNormal[1],vNormal[2]));
125 | //将集合放进HsahMap中
126 | hmn.put(tempInxex, hsn);
127 | }
128 | }
129 |
130 | if ((i - start) % 100 == 0) {
131 | finishCallback.faceProgressUpdate(threadID, i - start);
132 | }
133 | }
134 |
135 |
136 | //生成法向量数组
137 | for (int i = 0; i < alFaceIndex.size(); i++) {
138 | int index = alFaceIndex.get(i);
139 | //根据当前点的索引从Map中取出一个法向量的集合
140 | HashSet hsn = hmn.get(index);
141 | //求出平均法向量
142 | float[] tn = Normal.getAverage(hsn);
143 | //将计算出的平均法向量存放到法向量数组中
144 | normals[start * 9 + i * 3 + 0] = tn[0];
145 | normals[start * 9 + i * 3 + 1] = tn[1];
146 | normals[start * 9 + i * 3 + 2] = tn[2];
147 | }
148 |
149 | isFinish = true;
150 | finishCallback.alvFaceFinish();
151 |
152 | }
153 |
154 |
155 | /**
156 | * 求两个向量的叉积
157 | */
158 | public static float[] getCrossProduct(float x1,float y1,float z1,float x2,float y2,float z2) {
159 | //求出两个矢量叉积矢量在XYZ轴的分量ABC
160 | float A=y1*z2-y2*z1;
161 | float B=z1*x2-z2*x1;
162 | float C=x1*y2-x2*y1;
163 |
164 | return new float[]{A,B,C};
165 | }
166 |
167 | /**
168 | * 向量规格化
169 | */
170 | public static float[] vectorNormal(float[] vector) {
171 | //求向量的模
172 | float module=(float)Math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]+vector[2]*vector[2]);
173 | return new float[]{vector[0]/module,vector[1]/module,vector[2]/module};
174 | }
175 |
176 |
177 | public boolean isFinish() {
178 | return isFinish;
179 | }
180 |
181 |
182 | }
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/thread/AnalysisThreadHelper.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.thread;
2 |
3 | import android.util.Log;
4 |
5 | import com.dyman.easyshow3d.bean.ObjProObject;
6 | import com.dyman.easyshow3d.imp.ModelLoaderListener;
7 |
8 | import java.text.DecimalFormat;
9 | import java.util.ArrayList;
10 |
11 | /**
12 | * Created by dyman on 2016/11/13.
13 | */
14 |
15 | public class AnalysisThreadHelper {
16 |
17 | private static final String TAG = "ATHelper";
18 | /** 解析顶点数据的线程组 */
19 | private VerticesThread[] vThreads;
20 | /** 待处理的顶点数据 */
21 | private ArrayList vLines = new ArrayList<>();
22 | /** 解析面数据的线程组 */
23 | private FaceThread[] fThreads;
24 | /** 待处理的面数据 */
25 | private ArrayList fLines = new ArrayList<>();
26 |
27 | private float[] alv;
28 | private float[] vertices;
29 | private float[] normals;
30 |
31 |
32 | private int threadNum = 3;
33 | private ObjProObject objModel;
34 | private int vLineNum = 0;
35 | private int fLineNum = 0;
36 |
37 | private ModelLoaderListener listener;
38 |
39 | public AnalysisThreadHelper(int threadNum, ObjProObject objModel, ModelLoaderListener listener) {
40 | this.threadNum = threadNum;
41 | this.objModel = objModel;
42 | this.listener = listener;
43 |
44 | vThreads = new VerticesThread[threadNum];
45 | fThreads = new FaceThread[threadNum];
46 | }
47 |
48 |
49 |
50 | /**
51 | * 传入3D文件,先开三条子线程解析顶点数据,再开三条子线程解析面数据
52 | * 最后通过回调返回解析结果
53 | */
54 | public void analysis(byte[] objByte) {
55 | //TODO 要将此方法改为异步执行
56 | listener.loadBegin();
57 | vLineNum = 0;
58 | fLineNum = 0;
59 | String line;
60 | String[] tempsa, tempsa1;
61 |
62 | // 1. 先将原数据处理分为顶点数据和面数据(这里很耗时间)
63 | String objText = new String(objByte);
64 | String[] objLines = objText.split("\n");
65 | for (int i = 0, len = objLines.length; i < len; i++) {
66 | line = objLines[i];
67 | tempsa = line.split("[ ]+");
68 | if (tempsa.length > 4 && !tempsa[4].trim().equals("")) {
69 | pushModelData(tempsa);
70 | tempsa1 = new String[] {tempsa[0], tempsa[1], tempsa[3], tempsa[4]};
71 | pushModelData(tempsa1);
72 | } else {
73 | pushModelData(tempsa);
74 | }
75 | }
76 | alv = new float[vLineNum*3];
77 | vertices = new float[fLineNum*9];
78 | normals = new float[fLineNum*9];
79 | // 2. 开启线程组解析顶点数据
80 | int vAveLength = vLineNum/3;
81 | for (int i = 0; i < threadNum; i++) {
82 | if (i == (threadNum-1)) {
83 | vThreads[i] = new VerticesThread(i, vLines, alv, i*vAveLength, vLineNum, finishCallback);
84 | } else {
85 | vThreads[i] = new VerticesThread(i, vLines, alv, i*vAveLength, (i+1)*vAveLength-1, finishCallback);
86 | }
87 | vThreads[i].start();
88 | }
89 | }
90 |
91 | private void pushModelData(String[] tempsa) {
92 | if (tempsa[0].trim().equals("v")) {
93 | vLineNum++;
94 | vLines.add(tempsa);
95 | } else if (tempsa[0].trim().equals("f")) {
96 | fLineNum++;
97 | fLines.add(tempsa);
98 | }
99 | }
100 |
101 | /**
102 | * 解析结果的回调,分顶点解析的回调和面解析的回调
103 | */
104 | private IAnalysisFinishCallback finishCallback = new IAnalysisFinishCallback() {
105 | float temp, progress;
106 | int[] vThreadNums = new int[threadNum];
107 | int[] fThreadNums = new int[threadNum];
108 | DecimalFormat df = new DecimalFormat("#.00");
109 |
110 | @Override
111 | public void verticeProgressUpdate(int threadID, int nums) {
112 | vThreadNums[threadID] = nums;
113 | temp = 0;
114 | for (int num : vThreadNums) {
115 | temp += num;
116 | }
117 | temp = temp / (float) (vLineNum + fLineNum);
118 | temp = Float.valueOf(df.format(temp));
119 | if (temp - progress > 0.01) {
120 | progress = temp;
121 | listener.loadedUpdate(progress);
122 | }
123 | }
124 |
125 | @Override
126 | public void faceProgressUpdate(int threadID, int nums) {
127 | fThreadNums[threadID] = nums;
128 | temp = 0;
129 | for (int num : fThreadNums) {
130 | temp += num;
131 | }
132 | temp = (temp + vLineNum) / (float) (vLineNum + fLineNum);
133 | temp = Float.valueOf(df.format(temp));
134 | if (temp - progress > 0.01) {
135 | progress = temp;
136 | listener.loadedUpdate(progress);
137 | }
138 | }
139 |
140 | @Override
141 | public void alvFinish() {
142 |
143 | if (vThreadAllFinish()) {
144 | //顶点解析完成, 开始解析三角面
145 |
146 | // 3. 开始解析面数据
147 | int fAveLength = fLineNum/3;
148 | for (int i = 0; i < threadNum; i++) {
149 | if (i == (threadNum-1)) {
150 | fThreads[i] = new FaceThread(i, fLines, vertices, normals, i*fAveLength, fLineNum,
151 | alv, objModel, finishCallback);
152 | } else {
153 | fThreads[i] = new FaceThread(i, fLines, vertices, normals, i*fAveLength, (i+1)*fAveLength-1,
154 | alv, objModel, finishCallback);
155 | }
156 | fThreads[i].start();
157 | }
158 | }
159 | }
160 |
161 | @Override
162 | public void alvFaceFinish() {
163 |
164 | if (fThreadAllFinish()) {
165 | // 全部解析完成, 返回verticesList和faceList
166 |
167 | // 4.初始化顶点坐标与着色数据, 通过回调通知View显示模型
168 | objModel.initVertexData(vertices,normals);
169 | listener.loadedFinish(objModel);
170 | }
171 | }
172 | };
173 |
174 |
175 | synchronized private boolean vThreadAllFinish() {
176 | for (VerticesThread thread : vThreads) {
177 | if (!thread.isFinish()) {
178 | return false;
179 | }
180 | }
181 | return true;
182 | }
183 |
184 |
185 | synchronized private boolean fThreadAllFinish() {
186 | for (FaceThread thread : fThreads) {
187 | if (!thread.isFinish()) return false;
188 | }
189 | return true;
190 | }
191 |
192 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/ui/ShowModelActivity.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.ui;
2 |
3 | import android.app.Dialog;
4 | import android.app.ProgressDialog;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.graphics.BitmapFactory;
8 | import android.net.Uri;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.support.v7.widget.Toolbar;
12 | import android.util.Log;
13 | import android.view.Menu;
14 | import android.view.MenuItem;
15 | import android.view.View;
16 |
17 | import com.dyman.easyshow3d.ModelFactory;
18 | import com.dyman.easyshow3d.bean.ModelObject;
19 | import com.dyman.easyshow3d.imp.ModelLoaderListener;
20 | import com.dyman.easyshow3d.view.ShowModelView;
21 | import com.dyman.show3dmodel.R;
22 | import com.dyman.show3dmodel.config.MyConfig;
23 | import com.dyman.show3dmodel.utils.DialogUtils;
24 | import com.dyman.show3dmodel.utils.FileUtils;
25 | import com.dyman.show3dmodel.utils.TimeUtils;
26 | import com.dyman.show3dmodel.utils.ToastUtils;
27 |
28 | import java.io.BufferedReader;
29 | import java.io.File;
30 | import java.io.IOException;
31 | import java.io.InputStream;
32 | import java.io.InputStreamReader;
33 |
34 | public class ShowModelActivity extends BaseActivity {
35 |
36 | private static final String TAG = "ShowModelActivity";
37 | private ShowModelView sModelView;
38 | private String filePath;
39 | private ProgressDialog dialog;
40 |
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_show_model);
46 | filePath = getIntent().getStringExtra("filePath");
47 |
48 | initToolBar();
49 | initView();
50 | isExist(filePath);
51 |
52 | }
53 |
54 |
55 | /**
56 | * 初始化Toolbar
57 | */
58 | private void initToolBar() {
59 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_activity_show_model);
60 | setSupportActionBar(toolbar);
61 |
62 | String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
63 | if (fileName.length() > 15) {
64 | toolbar.setTitle(fileName.substring(0, 15) + "...");
65 | } else if (fileName.length() > 0) {
66 | toolbar.setTitle(fileName);
67 | }
68 |
69 | toolbar.setNavigationOnClickListener(new View.OnClickListener() {
70 | @Override
71 | public void onClick(View view) {
72 | new Handler().postDelayed(new Runnable() {
73 | @Override
74 | public void run() {
75 | finish();
76 | }
77 | }, MyConfig.POST_DELAYED_TIME);
78 | }
79 | });
80 | }
81 |
82 |
83 | private void initView() {
84 | sModelView = (ShowModelView) findViewById(R.id.showModelView);
85 |
86 | dialog = new ProgressDialog(this);
87 | dialog.setMessage(getString(R.string.model_load_progress_title));
88 | dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
89 | dialog.setIndeterminate(false);
90 | dialog.setCancelable(false);
91 | dialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel), new DialogInterface.OnClickListener() {
92 | @Override
93 | public void onClick(DialogInterface dialog, int which) {
94 | ModelFactory.cancelDecode();
95 | }
96 | });
97 | dialog.setMax(100);
98 | }
99 |
100 |
101 | /**
102 | * 检查模型是否存在,存在则进行解析
103 | *
104 | * @param filePath
105 | */
106 | private void isExist(String filePath) {
107 | if (filePath != null && !filePath.equals("")) {
108 | loadModel(filePath);
109 | } else {
110 | ToastUtils.showShort(ShowModelActivity.this, getString(R.string.text_file_not_exist));
111 | finish();
112 | }
113 | }
114 |
115 |
116 | @Override
117 | public boolean onCreateOptionsMenu(Menu menu) {
118 | getMenuInflater().inflate(R.menu.menu_show_model, menu);
119 | return true;
120 | }
121 |
122 |
123 | @Override
124 | public boolean onOptionsItemSelected(MenuItem item) {
125 | switch (item.getItemId()) {
126 | case R.id.menu_renderer_setting:
127 | Dialog renderDialog = DialogUtils.renderSettingDialog(ShowModelActivity.this);
128 | renderDialog.show();
129 | break;
130 |
131 | case R.id.menu_share:
132 | Uri fileUri = Uri.fromFile(new File(filePath));
133 | Intent shareIntent = new Intent();
134 | shareIntent.setAction(Intent.ACTION_SEND);
135 | shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
136 | shareIntent.setType("text/plain");
137 | startActivity(Intent.createChooser(shareIntent, getString(R.string.tip_share_to)));
138 | break;
139 | }
140 | return true;
141 | }
142 |
143 |
144 | long loadTime = 0;
145 | private void loadModel(String filePath) {
146 |
147 | dialog.show();
148 | ModelFactory.decodeFile(ShowModelActivity.this, filePath, new ModelLoaderListener() {
149 | @Override
150 | public void loadBegin() {
151 | loadTime = System.currentTimeMillis();
152 | }
153 |
154 | @Override
155 | public void loadedUpdate(float progress) {
156 | dialog.setProgress((int) (progress * 100));
157 | }
158 |
159 | @Override
160 | public void loadedFinish(ModelObject modelObject) {
161 | if (modelObject != null) {
162 | sModelView.setModelObject(modelObject);
163 | dialog.dismiss();
164 |
165 | Log.e(TAG, FileUtils.getName(filePath) +
166 | " time:" + TimeUtils.timeTransfer(System.currentTimeMillis() - loadTime) +
167 | " size:" + FileUtils.fileSizeTransfer(FileUtils.getSize(filePath)));
168 | }
169 | }
170 |
171 | @Override
172 | public void loaderCancel() {
173 | Log.i(TAG, "loaderCancel() was be called!");
174 | ToastUtils.showShort(ShowModelActivity.this, "已取消");
175 | dialog.dismiss();
176 | finish();
177 | }
178 |
179 | @Override
180 | public void loaderFailure() {
181 | Log.e(TAG, "decode model file failure!");
182 | ToastUtils.showShort(ShowModelActivity.this, "模型解析失败");
183 | dialog.dismiss();
184 | }
185 | });
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.utils;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 |
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.math.BigInteger;
11 | import java.security.MessageDigest;
12 | import java.text.DecimalFormat;
13 |
14 | /**
15 | * Created by dyman on 16/7/23.
16 | */
17 | public class FileUtils {
18 | private static final String TAG = "FileUtils";
19 |
20 | /**
21 | * 检查文件是否为空
22 | */
23 | public static boolean isNullString(String str) {
24 | if (str == null || str.equals(""))
25 | return true;
26 | return false;
27 | }
28 |
29 |
30 | /**
31 | * 获取文件名
32 | * @param filePathName
33 | * @return 没有返回“”
34 | */
35 | public static String getName(String filePathName) {
36 | try {
37 | return filePathName.substring(filePathName.lastIndexOf('/') + 1);
38 | } catch (Exception e) {
39 | return "";
40 | }
41 | }
42 |
43 | /**
44 | * 获取文件名(无后缀)
45 | * @param filePathName
46 | * @return
47 | */
48 | public static String getNameNoPostfix(String filePathName) {
49 | try {
50 | return filePathName.substring(filePathName.lastIndexOf('/') + 1,
51 | filePathName.lastIndexOf('.'));
52 | } catch (Exception e) {
53 | return "";
54 | }
55 | }
56 |
57 | /**
58 | * 获取文件类型
59 | * @param filePathName
60 | * @return
61 | */
62 | public static String getType(String filePathName) {
63 | try {
64 | return filePathName.substring(filePathName.lastIndexOf('.') + 1).toLowerCase();
65 | } catch (Exception e) {
66 | return "";
67 | }
68 | }
69 |
70 |
71 | /**
72 | * 获取文件大小(B)
73 | * @param filePathName
74 | * @return
75 | */
76 | public static long getSize(String filePathName) {
77 | if (isNullString(filePathName))
78 | return 0;
79 | File file = new File(filePathName);
80 | if (file.isFile())
81 | return file.length();
82 | return 0;
83 | }
84 |
85 | /**
86 | * 获取文件(夹)大小
87 | * @param filepath
88 | * @return
89 | */
90 | public static long getFileOrFileDirSize(String filepath){
91 |
92 | File file = new File(filepath);
93 | long blockSize = 0;
94 | try {
95 | if(file.isDirectory()){
96 | blockSize = getFileDirSize(file);
97 | }else{
98 | blockSize = getSize(filepath);
99 | }
100 | } catch (Exception e) {
101 | e.printStackTrace();
102 | }
103 |
104 | return blockSize;
105 | }
106 |
107 | /**
108 | * 获取文件夹大小
109 | * @param file
110 | * @return
111 | */
112 | private static long getFileDirSize(File file) {
113 | long size = 0;
114 | File flist[] = file.listFiles();
115 | for (int i = 0, len = flist.length; i < len; i++) {
116 | if (flist[i].isDirectory()) {
117 | size += getFileDirSize(flist[i]);
118 | }else{
119 | size += flist[i].length();
120 | }
121 | }
122 | return size;
123 | }
124 |
125 | /**
126 | * 文件大小单位转换
127 | * @param fileSize
128 | * @return
129 | */
130 | public static String fileSizeTransfer(long fileSize) {
131 | String mFileSize;
132 | DecimalFormat df = new DecimalFormat("######0.00");
133 | double size = (double) fileSize;
134 | if (size > 1024 * 1024 * 1024) {
135 | size = size / (1024 * 1024 * 1024);
136 | mFileSize = df.format(size) + " G";
137 | } else if (size > 1024 * 1024) {
138 | size = size / (1024 * 1024);
139 | mFileSize = df.format(size) + " MB";
140 | } else if (size > 1024) {
141 | size = size / 1024;
142 | mFileSize = df.format(size) + " KB";
143 | } else {
144 | mFileSize = df.format(size) + " B";
145 | }
146 |
147 | return mFileSize;
148 | }
149 |
150 |
151 | /**
152 | * 删除文件
153 | * @param filePathName
154 | */
155 | public static void delete(String filePathName) {
156 | if (isNullString(filePathName))
157 | return;
158 | File file = new File(filePathName);
159 | if (file.isFile() && file.exists()) {
160 | file.delete();
161 | }
162 | }
163 |
164 | /**
165 | * 删除文件夹(递归)
166 | * @param file 文件名
167 | * @param isDeleteDir 是否删除主目录
168 | */
169 | public static void deleteDir(File file, boolean isDeleteDir) {
170 | if (file.isFile()) {
171 | file.delete();
172 | return;
173 | }
174 | if (file.isDirectory()) {
175 | File[] childFiles = file.listFiles();
176 | if (childFiles == null || childFiles.length == 0) {
177 | file.delete();
178 | return;
179 | }
180 |
181 | for (int i = 0; i < childFiles.length; i++) {
182 | deleteDir(childFiles[i], true);
183 | }
184 | if (isDeleteDir){
185 | file.delete();
186 | }
187 | }
188 | }
189 |
190 | /**
191 | * 创建文件目录
192 | * @param path
193 | */
194 | public static void createDir(String path) {
195 | File file = new File(path);
196 | if (!file.exists()) {
197 | file.mkdirs();
198 | }
199 | }
200 |
201 |
202 | /**
203 | * 获取文件的MD5值
204 | * @param filepath
205 | * @return
206 | */
207 | public static String getFileMD5(String filepath) {
208 | File f = new File(filepath);
209 | if (!f.isFile()) {
210 | return null;
211 | }
212 | MessageDigest digest = null;
213 | FileInputStream in = null;
214 | byte buffer[] = new byte[1024];
215 | int len;
216 |
217 | try {
218 | digest = MessageDigest.getInstance("MD5");
219 | in = new FileInputStream(f);
220 | while ((len = in.read(buffer, 0, 1024)) != -1) {
221 | digest.update(buffer, 0, len);
222 | }
223 | in.close();
224 | } catch (Exception e) {
225 | e.printStackTrace();
226 | return null;
227 | }
228 | BigInteger bigInt = new BigInteger(1, digest.digest());
229 | return bigInt.toString(16);
230 | }
231 |
232 |
233 | public static byte[] getFileBytes(Context context, Uri uri) throws IOException {
234 | byte[] bytes = null;
235 | InputStream inputStream = null;
236 | inputStream = context.getContentResolver().openInputStream(uri);
237 | bytes = IOUtils.toByteArray(inputStream);
238 | return bytes;
239 | }
240 |
241 |
242 | }
243 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/view/ModelView.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.view;
2 |
3 | import android.content.Context;
4 | import android.opengl.GLES20;
5 | import android.opengl.GLSurfaceView;
6 | import android.util.AttributeSet;
7 |
8 |
9 | import com.dyman.easyshow3d.bean.BaseBuilderObject;
10 | import com.dyman.easyshow3d.bean.ModelObject;
11 | import com.dyman.easyshow3d.utils.MatrixState;
12 |
13 | import javax.microedition.khronos.egl.EGLConfig;
14 | import javax.microedition.khronos.opengles.GL10;
15 |
16 | /**
17 | * Created by dyman on 16/7/25.
18 | */
19 | public class ModelView extends GLSurfaceView {
20 |
21 | private static final String TAG = "ModelView";
22 |
23 | public final float TOUCH_SCALE_FACTOR = 180.0f/320;//角度缩放比例
24 | public float mPreviousY;//上次的触控位置Y坐标
25 | public float mPreviousX;//上次的触控位置X坐标
26 | public float wholeScale = 1f;//整体的缩放比例
27 | public float previousScale = 1f;//上次的缩放比例
28 | public float changeScale = 1f;//缩放改变的比例
29 | public float pinchStartDistance = 0.0f;
30 |
31 | /**
32 | * 触摸模式相关
33 | */
34 | public static final int TOUCH_NONE = 0;//无
35 | public static final int TOUCH_ZOOM = 1;//缩放
36 | public static final int TOUCH_DRAG = 2;//拖拽
37 | public int touchMode = TOUCH_NONE;
38 |
39 | /**
40 | * 打印进度计算相关
41 | */
42 | public float printProgress = 0;
43 | public ModelRenderer mRenderer;
44 |
45 |
46 | public ModelView(Context context) {
47 | this(context, null);
48 | }
49 |
50 |
51 | public ModelView(Context context, AttributeSet attrs) {
52 | super(context, attrs);
53 | init();
54 | }
55 |
56 |
57 | /** 初始化 */
58 | private void init() {
59 | // 设置OpenGL的版本号2.0
60 | this.setEGLContextClientVersion(2);
61 | mRenderer = new ModelRenderer();
62 | setRenderer(mRenderer);
63 | setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
64 | }
65 |
66 |
67 | public void setModelObject(ModelObject modelObject) {
68 | mRenderer.setModelObject(modelObject);
69 | }
70 |
71 |
72 | /**
73 | * 初始化模型缩放倍数,使其完全显示在屏幕上
74 | * @param modelObject
75 | */
76 | private void initModelScale(ModelObject modelObject) {
77 | float maxSize = modelObject.maxX - modelObject.minX;
78 | if (maxSize < modelObject.maxY-modelObject.minY){
79 | maxSize = modelObject.maxY-modelObject.minY;
80 | }
81 | if (maxSize < modelObject.maxZ-modelObject.minZ){
82 | maxSize = modelObject.maxZ-modelObject.minZ;
83 | }
84 | if (maxSize > 20f) { //大于20f,缩小模型
85 | wholeScale = 18f/maxSize;
86 | } else if(maxSize < 10f) { //小于10f,放大模型
87 | wholeScale = 15f/maxSize;
88 | }
89 | }
90 |
91 |
92 | /**
93 | * 渲染器,真正绘制模型的类
94 | */
95 | class ModelRenderer implements Renderer{
96 | public float yAngle;
97 | public float zAngle;
98 | private ModelObject modelObject;
99 | private BaseBuilderObject baseBuilder;
100 |
101 |
102 | public void setModelObject(ModelObject modelObject) {
103 | initModelScale(modelObject);
104 | this.modelObject = modelObject;
105 | }
106 |
107 |
108 | @Override
109 | public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
110 |
111 | // 初始化坐标和网格的构建类
112 | baseBuilder = new BaseBuilderObject(ModelView.this);
113 |
114 | // 设置平模背景色RGBA
115 | GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
116 | // 打开深度检测
117 | GLES20.glEnable(GLES20.GL_DEPTH_TEST);
118 | // 打开背面剪裁
119 | GLES20.glEnable(GLES20.GL_CULL_FACE);
120 | // 初始化变换矩阵
121 | MatrixState.setInitStack();
122 | // 初始化光源位置
123 | MatrixState.setLightLocation(60, 15 ,30);
124 | }
125 |
126 |
127 | @Override
128 | public void onSurfaceChanged(GL10 gl10, int width, int height) {
129 | // 设置视窗大小及位置
130 | GLES20.glViewport(0, 0, width, height);
131 | // 计算GLSurfaceView的宽高比
132 | float ratio = (float) width/height;
133 | // 调用次方法计算产生透视投影矩阵
134 | MatrixState.setProjectFrustum(-ratio, ratio, -1, 1, 2, 100);
135 | // 调用此方法产生摄像机9参数位置矩阵
136 | MatrixState.setCamera(0,0,0, 0f,0f,-1f, 0f,1.0f,0.0f);
137 | }
138 |
139 |
140 | @Override
141 | public void onDrawFrame(GL10 gl10) {
142 | // 清除深度缓冲与颜色缓冲
143 | GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
144 |
145 | if (modelObject == null) {
146 | return;
147 | }
148 |
149 | // 画3D模型
150 | MatrixState.pushMatrix();
151 | MatrixState.translate(0, -2f, -25f);
152 | MatrixState.rotate(modelObject.xRotateAngle, 0, 0, 1);
153 | MatrixState.rotate(yAngle + modelObject.yRotateAngle, 0, 1, 0);
154 | MatrixState.rotate(zAngle + modelObject.zRotateAngle, 1, 0, 0);
155 | MatrixState.scale(
156 | wholeScale * modelObject.printScale,
157 | wholeScale * modelObject.printScale,
158 | wholeScale * modelObject.printScale); // 修改整体显示的大小
159 | // 绘制模型
160 | if (modelObject.drawWay == ModelObject.DRAW_MODEL){
161 | modelObject.drawSelf(ModelView.this);
162 | // 绘制带进度显示的模型
163 | } else if(modelObject.drawWay == ModelObject.DRAW_PROGRESS){
164 | // 因为两种格式的模型的中心点坐标不同,分开处理
165 | if (modelObject.modelType.equals("stl")) {
166 | float maxHeight = modelObject.maxY-modelObject.minY;
167 | float finishHeight = (maxHeight) * printProgress - maxHeight/2;
168 | float clipPlaneUp[] = {0, 1, 0, modelObject.maxY};
169 | float clipPlaneDown[] = {0, -1, 0, finishHeight};
170 | modelObject.drawSelfWithProgress(clipPlaneDown, GLES20.GL_TRIANGLES, ModelView.this);
171 | modelObject.drawSelfWithProgress(clipPlaneUp, GLES20.GL_LINE_LOOP, ModelView.this);
172 |
173 | } else if (modelObject.modelType.equals("obj")){
174 | float finishHeight = (modelObject.maxY-modelObject.minY) * printProgress + modelObject.minY;
175 | float clipPlaneUp[] = {0, 1, 0, modelObject.maxY};
176 | float clipPlaneDown[] = {0, -1, 0, finishHeight};
177 | modelObject.drawSelfWithProgress(clipPlaneDown, GLES20.GL_TRIANGLES, ModelView.this);
178 | modelObject.drawSelfWithProgress(clipPlaneUp, GLES20.GL_LINE_LOOP, ModelView.this);
179 | }
180 | }
181 | MatrixState.popMatrix();
182 | }
183 | }
184 |
185 |
186 | protected void clearFrame() {
187 | // 清除深度缓冲与颜色缓冲
188 | GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
189 | }
190 |
191 |
192 |
193 |
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/ui/OpenFileActivity.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.ui;
2 |
3 | import android.content.Intent;
4 | import android.os.Environment;
5 | import android.os.Handler;
6 | import android.os.Bundle;
7 | import android.support.v7.widget.DefaultItemAnimator;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.support.v7.widget.Toolbar;
11 | import android.util.Log;
12 | import android.view.View;
13 | import android.widget.LinearLayout;
14 |
15 | import com.dyman.show3dmodel.R;
16 | import com.dyman.show3dmodel.adapter.FolderListAdapter;
17 | import com.dyman.show3dmodel.adapter.listener.OnAdapterItemListener;
18 | import com.dyman.show3dmodel.bean.FolderBean;
19 | import com.dyman.show3dmodel.config.IntentKey;
20 | import com.dyman.show3dmodel.config.MyConfig;
21 | import com.dyman.show3dmodel.utils.ToastUtils;
22 |
23 | import java.io.File;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | /**
28 | * 文件列表菜单Activity
29 | */
30 | public class OpenFileActivity extends BaseActivity implements View.OnClickListener{
31 |
32 | private static final String TAG = "OpenFileActivity";
33 | private LinearLayout openRootDir;
34 | private RecyclerView folderRv;
35 | private FolderListAdapter adapter;
36 | private List folderList = new ArrayList<>();
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_open_file);
42 | initToolbar();
43 | initView();
44 | initDatas();
45 | }
46 |
47 |
48 | private void initToolbar() {
49 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_activity_openFile);
50 | setSupportActionBar(toolbar);
51 | toolbar.setNavigationOnClickListener(new View.OnClickListener() {
52 | @Override
53 | public void onClick(View view) {
54 | new Handler().postDelayed(new Runnable() {
55 | @Override
56 | public void run() {
57 | finish();
58 | }
59 | }, MyConfig.POST_DELAYED_TIME);
60 | }
61 | });
62 | }
63 |
64 |
65 | private void initView() {
66 | openRootDir = (LinearLayout) findViewById(R.id.openRootDir_ll_activity_open_file);
67 | folderRv = (RecyclerView) findViewById(R.id.frequentFolder_rv_activity_open_file);
68 | folderRv.setLayoutManager(new LinearLayoutManager(this));
69 | folderRv.setItemAnimator(new DefaultItemAnimator());
70 | folderRv.setHasFixedSize(true);
71 |
72 | openRootDir.setOnClickListener(this);
73 | findViewById(R.id.stlFile_rl_activity_open_file).setOnClickListener(this);
74 | findViewById(R.id.objFile_rl_activity_open_file).setOnClickListener(this);
75 | findViewById(R.id.d3sFile_rl_activity_open_file).setOnClickListener(this);
76 | }
77 |
78 |
79 | private void initDatas() {
80 | //能否改为动态检测常用文件夹?
81 | String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
82 | if (!folderList.isEmpty()) { folderList.clear(); }
83 |
84 | FolderBean folderBean = new FolderBean();
85 | folderBean.setFolderName(getString(R.string.text_qq));
86 | folderBean.setFolderPath(rootPath+File.separator+"tencent"+File.separator+"QQfile_recv");
87 | if (new File(folderBean.getFolderPath()).canRead()) {
88 | folderList.add(folderBean);
89 | }
90 |
91 | folderBean = new FolderBean();
92 | folderBean.setFolderName(getString(R.string.text_download));
93 | folderBean.setFolderPath(rootPath+File.separator+"Download");
94 | if (new File(folderBean.getFolderPath()).canRead()) {
95 | folderList.add(folderBean);
96 | }
97 |
98 | folderBean = new FolderBean();
99 | folderBean.setFolderName(getString(R.string.text_document));
100 | folderBean.setFolderPath(rootPath+File.separator+"documents");
101 | if (new File(folderBean.getFolderPath()).canRead()) {
102 | folderList.add(folderBean);
103 | }
104 |
105 | folderBean = new FolderBean();
106 | folderBean.setFolderName(getString(R.string.text_weixin));
107 | folderBean.setFolderPath(rootPath+File.separator+"tencent"+File.separator+"MicroMsg"+File.separator+"Download");
108 | if (new File(folderBean.getFolderPath()).canRead()) {
109 | folderList.add(folderBean);
110 | }
111 |
112 | adapter = new FolderListAdapter(folderList);
113 | adapter.setOnItemClickListener(new OnAdapterItemListener() {
114 | @Override
115 | public void onItemClick(View v, int position) {
116 | FolderBean bean = folderList.get(position);
117 | Intent it = new Intent(OpenFileActivity.this, FileDirectoryActivity.class);
118 | it.putExtra(IntentKey.TITLE, bean.getFolderName());
119 | it.putExtra(IntentKey.KEY_TYPE, IntentKey.DIRECTORY_KEY);
120 | it.putExtra(IntentKey.DIRECTORY_PATH, bean.getFolderPath());
121 | startActivity(it);
122 | }
123 | });
124 |
125 | folderRv.setAdapter(adapter);
126 | }
127 |
128 |
129 | @Override
130 | public void onClick(View view) {
131 | switch (view.getId()) {
132 | case R.id.openRootDir_ll_activity_open_file:
133 | ToastUtils.showShort(OpenFileActivity.this, getString(R.string.text_open_root_dir));
134 | Intent it = new Intent(OpenFileActivity.this, FileDirectoryActivity.class);
135 | it.putExtra(IntentKey.TITLE, "手机");
136 | it.putExtra(IntentKey.KEY_TYPE, IntentKey.DIRECTORY_KEY);
137 | it.putExtra(IntentKey.DIRECTORY_PATH, Environment.getExternalStorageDirectory().getAbsolutePath());
138 | startActivity(it);
139 | break;
140 | case R.id.stlFile_rl_activity_open_file:
141 | ToastUtils.showShort(OpenFileActivity.this, getString(R.string.text_search_stl));
142 | searchFileWithType("STL文件", "stl_key");
143 | break;
144 | case R.id.objFile_rl_activity_open_file:
145 | ToastUtils.showShort(OpenFileActivity.this, getString(R.string.text_search_obj));
146 | searchFileWithType("STL文件", "obj_key");
147 | break;
148 | case R.id.d3sFile_rl_activity_open_file:
149 | ToastUtils.showShort(OpenFileActivity.this, getString(R.string.text_search_ds));
150 | searchFileWithType("STL文件", "3ds_key");
151 | break;
152 | }
153 | }
154 |
155 |
156 | private void searchFileWithType(String title, String typeKey) {
157 | Intent it = new Intent(OpenFileActivity.this, FileDirectoryActivity.class);
158 | it.putExtra(IntentKey.TITLE, title);
159 | it.putExtra(IntentKey.KEY_TYPE, typeKey);
160 | startActivity(it);
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.utils;
2 |
3 | import com.dyman.show3dmodel.R;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.math.BigInteger;
8 | import java.security.MessageDigest;
9 | import java.text.DecimalFormat;
10 |
11 | /**
12 | * Created by dyman on 16/7/23.
13 | */
14 | public class FileUtils {
15 | private static final String TAG = "FileUtils";
16 |
17 | /**
18 | * 检查文件是否为空
19 | */
20 | public static boolean isNullString(String str) {
21 | if (str == null || str.equals(""))
22 | return true;
23 | return false;
24 | }
25 |
26 |
27 | /**
28 | * 获取文件名
29 | * @param filePathName
30 | * @return 没有返回“”
31 | */
32 | public static String getName(String filePathName) {
33 | try {
34 | return filePathName.substring(filePathName.lastIndexOf('/') + 1);
35 | } catch (Exception e) {
36 | return "";
37 | }
38 | }
39 |
40 | /**
41 | * 获取文件名(无后缀)
42 | * @param filePathName
43 | * @return
44 | */
45 | public static String getNameNoPostfix(String filePathName) {
46 | try {
47 | return filePathName.substring(filePathName.lastIndexOf('/') + 1,
48 | filePathName.lastIndexOf('.'));
49 | } catch (Exception e) {
50 | return "";
51 | }
52 | }
53 |
54 | /**
55 | * 获取文件类型
56 | * @param filePathName
57 | * @return
58 | */
59 | public static String getType(String filePathName) {
60 | try {
61 | return filePathName.substring(filePathName.lastIndexOf('.') + 1).toLowerCase();
62 | } catch (Exception e) {
63 | return "";
64 | }
65 | }
66 |
67 | public static int getImageFromType(String fileType) {
68 | if (fileType.toLowerCase().equals("stl")) {
69 | return R.mipmap.ic_file_stl;
70 | } else if (fileType.toLowerCase().equals("3ds")) {
71 | return R.mipmap.ic_file_ds;
72 | } else if (fileType.toLowerCase().equals("obj")) {
73 | return R.mipmap.ic_file_obj;
74 | } else if (fileType.toLowerCase().equals("dir")) {
75 | return R.mipmap.ic_folder;
76 | }
77 | return 0;
78 | }
79 |
80 | /**
81 | * 获取文件大小(B)
82 | * @param filePathName
83 | * @return
84 | */
85 | public static long getSize(String filePathName) {
86 | if (isNullString(filePathName))
87 | return 0;
88 | File file = new File(filePathName);
89 | if (file.isFile())
90 | return file.length();
91 | return 0;
92 | }
93 |
94 | /**
95 | * 获取文件(夹)大小
96 | * @param filepath
97 | * @return
98 | */
99 | public static long getFileOrFileDirSize(String filepath){
100 |
101 | File file = new File(filepath);
102 | long blockSize = 0;
103 | try {
104 | if(file.isDirectory()){
105 | blockSize = getFileDirSize(file);
106 | }else{
107 | blockSize = getSize(filepath);
108 | }
109 | } catch (Exception e) {
110 | e.printStackTrace();
111 | }
112 |
113 | return blockSize;
114 | }
115 |
116 | /**
117 | * 获取文件夹大小
118 | * @param file
119 | * @return
120 | */
121 | private static long getFileDirSize(File file) {
122 | long size = 0;
123 | File flist[] = file.listFiles();
124 | for (int i = 0, len = flist.length; i < len; i++) {
125 | if (flist[i].isDirectory()) {
126 | size += getFileDirSize(flist[i]);
127 | }else{
128 | size += flist[i].length();
129 | }
130 | }
131 | return size;
132 | }
133 |
134 | /**
135 | * 文件大小单位转换
136 | * @param fileSize
137 | * @return
138 | */
139 | public static String fileSizeTransfer(long fileSize) {
140 | String mFileSize;
141 | DecimalFormat df = new DecimalFormat("######0.00");
142 | double size = (double) fileSize;
143 | if (size > 1024 * 1024 * 1024) {
144 | size = size / (1024 * 1024 * 1024);
145 | mFileSize = df.format(size) + " G";
146 | } else if (size > 1024 * 1024) {
147 | size = size / (1024 * 1024);
148 | mFileSize = df.format(size) + " MB";
149 | } else if (size > 1024) {
150 | size = size / 1024;
151 | mFileSize = df.format(size) + " KB";
152 | } else {
153 | mFileSize = df.format(size) + " B";
154 | }
155 |
156 | return mFileSize;
157 | }
158 |
159 |
160 | /**
161 | * 删除文件
162 | * @param filePathName
163 | */
164 | public static void delete(String filePathName) {
165 | if (isNullString(filePathName))
166 | return;
167 | File file = new File(filePathName);
168 | if (file.isFile() && file.exists()) {
169 | file.delete();
170 | }
171 | }
172 |
173 | /**
174 | * 删除文件夹(递归)
175 | * @param file 文件名
176 | * @param isDeleteDir 是否删除主目录
177 | */
178 | public static void deleteDir(File file, boolean isDeleteDir) {
179 | if (file.isFile()) {
180 | file.delete();
181 | return;
182 | }
183 | if (file.isDirectory()) {
184 | File[] childFiles = file.listFiles();
185 | if (childFiles == null || childFiles.length == 0) {
186 | file.delete();
187 | return;
188 | }
189 |
190 | for (int i = 0; i < childFiles.length; i++) {
191 | deleteDir(childFiles[i], true);
192 | }
193 | if (isDeleteDir){
194 | file.delete();
195 | }
196 | }
197 | }
198 |
199 | /**
200 | * 创建文件目录
201 | * @param path
202 | */
203 | public static void createDir(String path) {
204 | File file = new File(path);
205 | if (!file.exists()) {
206 | file.mkdirs();
207 | }
208 | }
209 |
210 |
211 | /**
212 | * 获取文件的MD5值
213 | * @param filepath
214 | * @return
215 | */
216 | public static String getFileMD5(String filepath) {
217 | File f = new File(filepath);
218 | if (!f.isFile()) {
219 | return null;
220 | }
221 | MessageDigest digest = null;
222 | FileInputStream in = null;
223 | byte buffer[] = new byte[1024];
224 | int len;
225 |
226 | try {
227 | digest = MessageDigest.getInstance("MD5");
228 | in = new FileInputStream(f);
229 | while ((len = in.read(buffer, 0, 1024)) != -1) {
230 | digest.update(buffer, 0, len);
231 | }
232 | in.close();
233 | } catch (Exception e) {
234 | e.printStackTrace();
235 | return null;
236 | }
237 | BigInteger bigInt = new BigInteger(1, digest.digest());
238 | return bigInt.toString(16);
239 | }
240 |
241 |
242 | }
243 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_open_file.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
25 |
26 |
27 |
31 |
35 |
40 |
47 |
53 |
60 |
61 |
65 |
72 |
78 |
85 |
86 |
90 |
97 |
103 |
110 |
111 |
112 |
116 |
119 |
125 |
126 |
130 |
135 |
141 |
146 |
147 |
148 |
152 |
153 |
156 |
162 |
163 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/bean/ModelObject.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.bean;
2 |
3 | import android.content.Context;
4 | import android.opengl.GLES20;
5 |
6 |
7 | import com.dyman.easyshow3d.utils.LoadUtil;
8 | import com.dyman.easyshow3d.utils.MatrixState;
9 | import com.dyman.easyshow3d.utils.ShaderUtil;
10 | import com.dyman.easyshow3d.view.ModelView;
11 |
12 | import java.nio.FloatBuffer;
13 |
14 | /**
15 | * Created by dyman on 16/7/25.
16 | */
17 | public abstract class ModelObject {
18 |
19 |
20 | public abstract void parseModel(byte[] data, Context context);
21 |
22 | public abstract void initVertexData(float[] vertices, float[] normals);
23 |
24 | public abstract void cancelTask();
25 |
26 | private static final String TAG = "ModelObject";
27 |
28 | public static final int DRAW_MODEL = 0;//画基础模型
29 | public static final int DRAW_PROGRESS = 1;//画带进度的模型
30 | public int drawWay = DRAW_MODEL;
31 |
32 | public String modelType;
33 | public float[] color = new float[] {0.8f, 0.8f, 0.8f, 1};
34 |
35 | public float maxX;
36 | public float maxY;
37 | public float maxZ;
38 | public float minX;
39 | public float minY;
40 | public float minZ;
41 |
42 | /**
43 | * 模型打印大小比例
44 | */
45 | public float printScale = 1f;
46 | public float xRotateAngle = 0f;
47 | public float yRotateAngle = 0f;
48 | public float zRotateAngle = 0f;
49 |
50 | int mProgram = -1;//自定义渲染管线着色器程序id
51 | int muMVPMatrixHandle;//总变换矩阵引用
52 | int muMMatrixHandle;//位置、旋转变换矩阵
53 | int maPositionHandle; //顶点位置属性引用
54 | int maNormalHandle; //顶点法向量属性引用
55 | int maLightLocationHandle;//光源位置属性引用
56 | int maCameraHandle; //摄像机位置属性引用
57 | int muColorHandle; //顶点颜色
58 | int muClipHandle; //剪裁平面属性引用
59 |
60 | String mVertexShader;//顶点着色器代码脚本
61 | String mFragmentShader;//片元着色器代码脚本
62 |
63 | FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
64 | FloatBuffer mNormalBuffer;//顶点法向量数据缓冲
65 | int vCount=0;
66 |
67 |
68 | /**
69 | * 修正模型的大小
70 | * @param x
71 | * @param y
72 | * @param z
73 | */
74 | public void adjustMaxMin(float x, float y, float z) {
75 | if (x > maxX) {
76 | maxX = x;
77 | }
78 | if (y > maxY) {
79 | maxY = y;
80 | }
81 | if (z > maxZ) {
82 | maxZ = z;
83 | }
84 | if (x < minX) {
85 | minX = x;
86 | }
87 | if (y < minY) {
88 | minY = y;
89 | }
90 | if (z < minZ) {
91 | minZ = z;
92 | }
93 | }
94 |
95 |
96 | /**
97 | * 初始化基础着色器
98 | * @param objView
99 | */
100 | public void initShader(ModelView objView) {
101 | //加载顶点着色器的脚本内容
102 | mVertexShader= ShaderUtil.loadFromAssetsFile("easy_show_vertex.sh", objView.getResources());
103 | //加载片元着色器的脚本内容
104 | mFragmentShader=ShaderUtil.loadFromAssetsFile("easy_show_frag_color.sh", objView.getResources());
105 | //基于顶点着色器与片元着色器创建程序
106 | mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
107 | //获取程序中顶点位置属性引用
108 | maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
109 | //获取程序中顶点颜色属性引用
110 | maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal");
111 | //获取程序中总变换矩阵引用
112 | muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
113 | //获取位置、旋转变换矩阵引用
114 | muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
115 | //获取程序中光源位置引用
116 | maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation");
117 | //获取程序中摄像机位置引用
118 | maCameraHandle=GLES20.glGetUniformLocation(mProgram, "uCamera");
119 | //获取程序中物体颜色的引用
120 | muColorHandle=GLES20.glGetUniformLocation(mProgram, "aColor");
121 | }
122 |
123 |
124 | /**
125 | * 初始化带剪切面的着色器
126 | * @param objView
127 | */
128 | public void initShaderWithClipPlane(ModelView objView) {
129 | //加载顶点着色器的脚本内容
130 | mVertexShader = ShaderUtil.loadFromAssetsFile("easy_show_vertex_clipplane.sh", objView.getResources());
131 | //加载片元着色器的脚本内容
132 | mFragmentShader = ShaderUtil.loadFromAssetsFile("easy_show_frag_clipplane.sh", objView.getResources());
133 | //基于顶点着色器与片元着色器创建程序
134 | mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
135 | //获取程序中顶点位置属性引用
136 | maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
137 | //获取程序中顶点颜色属性引用
138 | maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal");
139 | //获取程序中总变换矩阵引用
140 | muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
141 | //获取位置、旋转变换矩阵引用
142 | muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
143 | //获取程序中光源位置引用
144 | maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation");
145 | //获取程序中摄像机位置引用
146 | maCameraHandle=GLES20.glGetUniformLocation(mProgram, "uCamera");
147 | //获取程序中剪裁平面引用
148 | muClipHandle=GLES20.glGetUniformLocation(mProgram, "u_clipPlane");
149 | }
150 |
151 |
152 | /**
153 | * 绘制基础模型
154 | */
155 | public void drawSelf(ModelView modelView) {
156 | if (mVertexBuffer == null || mNormalBuffer == null){ return; }
157 |
158 | if (mProgram == -1){
159 | initShader(modelView);
160 | }
161 | GLES20.glLineWidth(1f);
162 | //制定使用某套着色器程序
163 | GLES20.glUseProgram(mProgram);
164 | //将最终变换矩阵传入着色器程序
165 | GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0);
166 | //将位置、旋转变换矩阵传入着色器程序
167 | GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0);
168 | //将光源位置传入着色器程序
169 | GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
170 | //将摄像机位置传入着色器程序
171 | GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
172 | // 将顶点位置数据传入渲染管线
173 | GLES20.glVertexAttribPointer
174 | (
175 | maPositionHandle,
176 | 3,
177 | GLES20.GL_FLOAT,
178 | false,
179 | 3*4,
180 | mVertexBuffer
181 | );
182 |
183 | //传入顶点颜色数据
184 | GLES20.glUniform4fv(muColorHandle, 1, color, 0);
185 |
186 | //将顶点法向量数据传入渲染管线
187 | GLES20.glVertexAttribPointer
188 | (
189 | maNormalHandle,
190 | 3,
191 | GLES20.GL_FLOAT,
192 | false,
193 | 3*4,
194 | mNormalBuffer
195 | );
196 | //启用顶点位置、法向量数据
197 | GLES20.glEnableVertexAttribArray(maPositionHandle);
198 | GLES20.glEnableVertexAttribArray(maNormalHandle);
199 | //绘制加载的物体
200 | GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
201 | }
202 |
203 |
204 | /**
205 | * 绘制带进度显示的模型
206 | */
207 | public void drawSelfWithProgress(float[] clipPlane, int drawMode, ModelView modelView) {
208 | if (mVertexBuffer == null || mNormalBuffer == null){ return; }
209 |
210 | if (mProgram == -1){
211 | initShaderWithClipPlane(modelView);
212 | }
213 |
214 | //制定使用某套着色器程序
215 | GLES20.glUseProgram(mProgram);
216 | //将最终变换矩阵传入着色器程序
217 | GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0);
218 | //将位置、旋转变换矩阵传入着色器程序
219 | GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0);
220 | //将光源位置传入着色器程序
221 | GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
222 | //将摄像机位置传入着色器程序
223 | GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
224 | //将剪裁平面传入shader程序
225 | GLES20.glUniform4fv(muClipHandle, 1, LoadUtil.fromArrayToBuff(clipPlane));
226 | // 将顶点位置数据传入渲染管线
227 | GLES20.glVertexAttribPointer
228 | (
229 | maPositionHandle,
230 | 3,
231 | GLES20.GL_FLOAT,
232 | false,
233 | 3*4,
234 | mVertexBuffer
235 | );
236 | //将顶点法向量数据传入渲染管线
237 | GLES20.glVertexAttribPointer
238 | (
239 | maNormalHandle,
240 | 3,
241 | GLES20.GL_FLOAT,
242 | false,
243 | 3*4,
244 | mNormalBuffer
245 | );
246 | //启用顶点位置、法向量数据
247 | GLES20.glEnableVertexAttribArray(maPositionHandle);
248 | GLES20.glEnableVertexAttribArray(maNormalHandle);
249 | //绘制加载的物体
250 | GLES20.glDrawArrays(drawMode, 0, vCount);
251 | }
252 |
253 | }
254 |
--------------------------------------------------------------------------------
/easyshow3d/src/main/java/com/dyman/easyshow3d/view/Show3dsMd2View.java:
--------------------------------------------------------------------------------
1 | package com.dyman.easyshow3d.view;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.opengl.GLES20;
6 | import android.opengl.GLSurfaceView;
7 | import android.view.MotionEvent;
8 |
9 | import com.threed.jpct.Camera;
10 | import com.threed.jpct.FrameBuffer;
11 | import com.threed.jpct.Light;
12 | import com.threed.jpct.Loader;
13 | import com.threed.jpct.Matrix;
14 | import com.threed.jpct.Object3D;
15 | import com.threed.jpct.Primitives;
16 | import com.threed.jpct.RGBColor;
17 | import com.threed.jpct.SimpleVector;
18 | import com.threed.jpct.World;
19 | import com.threed.jpct.util.MemoryHelper;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 |
25 | import javax.microedition.khronos.egl.EGL10;
26 | import javax.microedition.khronos.egl.EGLConfig;
27 | import javax.microedition.khronos.egl.EGLDisplay;
28 | import javax.microedition.khronos.opengles.GL10;
29 |
30 | /**
31 | * Created by dyman on 16/8/13.
32 | */
33 | public class Show3dsMd2View extends GLSurfaceView{
34 | private static final String TAG = "Show3dsMd2View";
35 | private MyRender mRender;
36 |
37 | private static int TOUCH_NONE = 0;
38 | private static int TOUCH_DRAG = 1;
39 | private static int TOUCH_ZOOM = 2;
40 | private int touchMode = TOUCH_NONE;
41 |
42 | private float xpos = -1;
43 | private float ypos = -1;
44 | private float pinchStartDistance = 0;
45 | private float currScale = 1;
46 | private float saveScale = 1;
47 |
48 | public Show3dsMd2View(Context context, String filePath) {
49 | super(context);
50 | setEGLConfigChooser(new EGLConfigChooser(){
51 |
52 | @Override
53 | public EGLConfig chooseConfig(EGL10 egl10, EGLDisplay eglDisplay) {
54 | //Ensure that we get a 16bit framebuffer. Otherwise, we'll fall back to Pixelflinger on some device (read:
55 | // Samsung I7500)
56 | int[] attributes = new int[] {EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE};
57 | EGLConfig[] configs = new EGLConfig[1];
58 | int[] result = new int[1];
59 | egl10.eglChooseConfig(eglDisplay, attributes, configs, 1, result);
60 | return configs[0];
61 | }
62 | });
63 | mRender = new MyRender(context, filePath);
64 | setRenderer(mRender);
65 | }
66 |
67 |
68 | @Override
69 | public boolean onTouchEvent(MotionEvent e) {
70 | switch (e.getAction() & MotionEvent.ACTION_MASK) {
71 | case MotionEvent.ACTION_DOWN:
72 | if (touchMode == TOUCH_NONE && e.getPointerCount() == 1){
73 | touchMode = TOUCH_DRAG;
74 | xpos = e.getX();
75 | ypos = e.getY();
76 | }
77 | break;
78 |
79 | case MotionEvent.ACTION_POINTER_DOWN:
80 | if (e.getPointerCount() >= 2) {
81 | pinchStartDistance = getPinchDistance(e);
82 | if (pinchStartDistance >= 50f){
83 | touchMode = TOUCH_ZOOM;
84 | }
85 | }
86 | break;
87 |
88 | case MotionEvent.ACTION_MOVE:
89 | if (touchMode == TOUCH_ZOOM && pinchStartDistance > 0) {
90 | float temp = getPinchDistance(e) / pinchStartDistance;
91 | currScale = temp * saveScale;
92 | mRender.setZoomSize(currScale);
93 | }else if(touchMode == TOUCH_DRAG) {
94 | float xd = e.getX() - xpos;
95 | float yd = e.getY() - ypos;
96 |
97 | mRender.setRotateY(xd / -100f);
98 | mRender.setRotateZ(yd / -100f);
99 |
100 | xpos = e.getX();
101 | ypos = e.getY();
102 |
103 | }
104 | break;
105 |
106 | case MotionEvent.ACTION_POINTER_UP:
107 | if (touchMode == TOUCH_ZOOM) {
108 | touchMode = TOUCH_NONE;
109 | saveScale = currScale;
110 | }
111 | break;
112 |
113 | case MotionEvent.ACTION_UP:
114 | if (touchMode == TOUCH_DRAG) {
115 | touchMode = TOUCH_NONE;
116 | }
117 | break;
118 | }
119 |
120 | return true;
121 | }
122 |
123 | /**
124 | * 计算两指间的距离
125 | * @param event
126 | * @return
127 | */
128 | private float getPinchDistance(MotionEvent event) {
129 | float x=0;
130 | float y=0;
131 | try {
132 | x = event.getX(0) - event.getX(1);
133 | y = event.getY(0) - event.getY(1);
134 | } catch (IllegalArgumentException e) {
135 | e.printStackTrace();
136 | }
137 | return (float) Math.sqrt(x * x + y * y);
138 | }
139 |
140 |
141 |
142 | class MyRender implements Renderer{
143 |
144 | private Context mContext;
145 | private String filePath;
146 |
147 | private Object3D model = null;
148 | private Object3D cube = null;
149 | private World world;
150 | private Light sun = null;
151 | private FrameBuffer fb = null;
152 |
153 | private float rotateY = 0;//水平旋转角度
154 | private float rotateZ = 0;//垂直旋转角度
155 | private float zoomSize = 1;//整体缩放度
156 |
157 | public MyRender(Context c, String filePath){
158 | mContext = c;
159 | this.filePath = filePath;
160 | }
161 |
162 | @Override
163 | public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
164 | gl10.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
165 |
166 | world = new World();
167 | world.setAmbientLight(500, 500, 500);
168 |
169 | cube = Primitives.getCube(10);
170 | cube.calcTextureWrapSpherical();
171 | cube.strip();
172 | cube.build();
173 |
174 | model = loadModel(filePath, 1);
175 | model.strip();
176 | model.build();
177 |
178 | world.addObject(model);
179 | //光源设置
180 | sun = new Light(world);
181 | sun.setIntensity(250,250,250);
182 | //镜头设置
183 | Camera cam = world.getCamera();
184 | cam.moveCamera(Camera.CAMERA_MOVEOUT, 10);
185 | cam.lookAt(cube.getTransformedCenter());
186 |
187 | SimpleVector sv = new SimpleVector();
188 | sv.set(cube.getTransformedCenter());
189 | sv.y -= 100;
190 | sv.z -= 100;
191 | sun.setPosition(sv);
192 | MemoryHelper.compact();
193 | }
194 |
195 | @Override
196 | public void onSurfaceChanged(GL10 gl10, int w, int h) {
197 | if (fb != null){
198 | fb.dispose();
199 | }
200 | fb = new FrameBuffer(gl10, w, h);
201 | GLES20.glViewport(0, 0, w, h);
202 | }
203 |
204 | @Override
205 | public void onDrawFrame(GL10 gl10) {
206 | gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
207 | fb.clear(RGBColor.BLACK);
208 | world.renderScene(fb);
209 | world.draw(fb);
210 | fb.display();
211 |
212 | if (rotateY != 0) {
213 | model.rotateY(rotateY);
214 | rotateY = 0;
215 | }
216 |
217 | if (rotateZ != 0){
218 | model.rotateZ(rotateZ);
219 | rotateZ = 0;
220 | }
221 |
222 | if (zoomSize != 1) {
223 | model.setScale(zoomSize);
224 | zoomSize = 1;
225 | }
226 | }
227 |
228 |
229 | /**
230 | * 加载模型,3DS格式的
231 | * @param filename
232 | * @param scale
233 | * @return
234 | */
235 | public Object3D loadModel(String filename, float scale){
236 | InputStream is = null;
237 | try {
238 | Uri uri = Uri.fromFile(new File(filePath));
239 | is = mContext.getContentResolver().openInputStream(uri);
240 | } catch (IOException e) {
241 | // TODO Auto-generated catch block
242 | e.printStackTrace();
243 | }
244 | Object3D[] model = Loader.load3DS(is, scale);
245 | Object3D o3d = new Object3D(0);
246 | Object3D temp = null;
247 | for (int i = 0; i < model.length; i++) {
248 | temp = model[i];
249 | temp.setCenter(SimpleVector.ORIGIN);
250 | temp.rotateX((float)( -.5*Math.PI));
251 | temp.rotateMesh();
252 | temp.setRotationMatrix(new Matrix());
253 | o3d = Object3D.mergeObjects(o3d, temp);
254 | o3d.build();
255 | }
256 | return o3d;
257 | }
258 |
259 | public void setRotateY(float count) {this.rotateY = count;}
260 |
261 | public void setRotateZ(float count) {this.rotateZ = count;}
262 |
263 | public void setZoomSize(float count) {this.zoomSize = count;}
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dyman/show3dmodel/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.dyman.show3dmodel.ui;
2 |
3 | import android.content.DialogInterface;
4 | import android.content.Intent;
5 | import android.graphics.Color;
6 | import android.graphics.drawable.ColorDrawable;
7 | import android.net.Uri;
8 | import android.os.Bundle;
9 | import android.os.Handler;
10 | import android.support.v7.widget.Toolbar;
11 | import android.util.Log;
12 | import android.util.TypedValue;
13 | import android.view.View;
14 | import android.view.Menu;
15 | import android.view.MenuItem;
16 | import android.widget.AdapterView;
17 | import android.widget.LinearLayout;
18 |
19 | import com.baoyz.swipemenulistview.SwipeMenu;
20 | import com.baoyz.swipemenulistview.SwipeMenuCreator;
21 | import com.baoyz.swipemenulistview.SwipeMenuItem;
22 | import com.baoyz.swipemenulistview.SwipeMenuListView;
23 | import com.dyman.show3dmodel.R;
24 | import com.dyman.show3dmodel.adapter.FileListAdapter;
25 | import com.dyman.show3dmodel.db.DatabaseHelper;
26 | import com.dyman.show3dmodel.bean.FileBean;
27 | import com.dyman.show3dmodel.manager.SharePreferenceManager;
28 | import com.dyman.show3dmodel.utils.DialogUtils;
29 | import com.dyman.show3dmodel.utils.FileUtils;
30 | import com.dyman.show3dmodel.utils.SnackBarUtils;
31 | import com.dyman.show3dmodel.utils.TimeUtils;
32 | import com.dyman.show3dmodel.utils.ToastUtils;
33 |
34 | import java.io.File;
35 | import java.util.ArrayList;
36 | import java.util.List;
37 |
38 | public class MainActivity extends BaseActivity{
39 |
40 | private static final String TAG = "MainActivity";
41 |
42 | private LinearLayout openFileLl;
43 | private SwipeMenuListView swipeLv;
44 | private FileListAdapter adapter;
45 | private List fileList = new ArrayList<>();
46 |
47 | DatabaseHelper databaseHelper;
48 | private boolean sureDelete;
49 |
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 | setContentView(R.layout.activity_main);
55 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_activity_main);
56 | setSupportActionBar(toolbar);
57 |
58 | databaseHelper = new DatabaseHelper(MainActivity.this);
59 |
60 | initView();
61 | initDatas();
62 | checkAcceptFile();
63 | }
64 |
65 |
66 | private void initView() {
67 | openFileLl = (LinearLayout) findViewById(R.id.openFile_ll_content_main);
68 | openFileLl.setOnClickListener(new View.OnClickListener() {
69 | @Override
70 | public void onClick(View view) {
71 | Intent it = new Intent(MainActivity.this, OpenFileActivity.class);
72 | startActivity(it);
73 | }
74 | });
75 |
76 | swipeLv = (SwipeMenuListView) findViewById(R.id.recentFileList_swipeLv_content_main);
77 | swipeLv.setDividerHeight(0);
78 | SwipeMenuCreator creator = new SwipeMenuCreator() {
79 | @Override
80 | public void create(SwipeMenu menu) {
81 | SwipeMenuItem shareItem = new SwipeMenuItem(getApplicationContext());
82 | shareItem.setBackground(new ColorDrawable((Color.rgb(0x3E, 0x82, 0xE9))));
83 | shareItem.setWidth(dp2px(90));
84 | shareItem.setTitle("Share");
85 | shareItem.setTitleSize(18);
86 | shareItem.setTitleColor(Color.WHITE);
87 | menu.addMenuItem(shareItem);
88 |
89 | SwipeMenuItem deleteItem = new SwipeMenuItem(getApplicationContext());
90 | deleteItem.setBackground(new ColorDrawable(Color.rgb(0xF9, 0x3F, 0x25)));
91 | deleteItem.setWidth(dp2px(90));
92 | deleteItem.setIcon(R.mipmap.ic_delete);
93 | menu.addMenuItem(deleteItem);
94 | }
95 | };
96 | swipeLv.setMenuCreator(creator);
97 | swipeLv.setOnMenuItemClickListener(new SwipeMenuListView.OnMenuItemClickListener() {
98 | @Override
99 | public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
100 | final FileBean fileBean = fileList.get(position);
101 | switch (index) {
102 | case 0://share
103 | Uri fileUri = Uri.fromFile(new File(fileBean.getFilePath()));
104 | Intent shareIntent = new Intent();
105 | shareIntent.setAction(Intent.ACTION_SEND);
106 | shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
107 | shareIntent.setType("text/plain");
108 | startActivity(Intent.createChooser(shareIntent, getString(R.string.tip_share_to)));
109 |
110 | break;
111 | case 1://delete
112 | sureDelete = true;
113 | final int fileIndex = fileList.indexOf(fileBean);
114 | fileList.remove(fileIndex);
115 | adapter.notifyDataSetChanged();
116 | SnackBarUtils.makeLong(swipeLv, getString(R.string.tip_already_delete_record))
117 | .show(getString(R.string.tip_cancel), new View.OnClickListener() {
118 | @Override
119 | public void onClick(View view) {
120 | sureDelete = false;
121 | fileList.add(fileIndex, fileBean);
122 | adapter.notifyDataSetChanged();
123 | }
124 | });
125 | new Handler().postDelayed(new Runnable() {
126 | @Override
127 | public void run() {
128 | if (sureDelete) {
129 | databaseHelper.delete(fileBean.getId());
130 | }
131 | }
132 | }, 4000);
133 |
134 | break;
135 | }
136 | return false;
137 | }
138 | });
139 | swipeLv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
140 | @Override
141 | public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
142 | final FileBean fileBean = fileList.get(i);
143 | File file = new File(fileBean.getFilePath());
144 | if (file.canRead()) {
145 | open3DFile(file);
146 | } else {
147 | DialogUtils.showAlerDialog(MainActivity.this, getString(R.string.tip_is_delete_invalid_file),
148 | new DialogInterface.OnClickListener() {
149 | @Override
150 | public void onClick(DialogInterface dialogInterface, int i) {
151 | databaseHelper.delete(fileBean.getId());
152 | dialogInterface.dismiss();
153 | }
154 | });
155 | }
156 | }
157 | });
158 | }
159 |
160 |
161 | private void initDatas() {
162 | //从数据库中读出打开过的3D文件
163 | fileList = databaseHelper.selectAll();
164 | adapter = new FileListAdapter(MainActivity.this, fileList);
165 | swipeLv.setAdapter(adapter);
166 | }
167 |
168 |
169 | /**
170 | * 检测是否接收到关联文件
171 | */
172 | private void checkAcceptFile() {
173 | Uri uri = (Uri) getIntent().getData();
174 | if (uri != null) {
175 | File file = new File(uri.getPath());
176 | open3DFile(file);
177 | }
178 | }
179 |
180 |
181 | @Override
182 | protected void onResume() {
183 | super.onResume();
184 | initDatas();
185 | }
186 |
187 | private void open3DFile(File file) {
188 | // 将文件信息存放数据库中
189 | DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
190 | FileBean fileBean = new FileBean();
191 | fileBean.setFilePath(file.getAbsolutePath());
192 | fileBean.setFileName(file.getName());
193 | fileBean.setFileType(FileUtils.getType(file.getName()));
194 | fileBean.setCreateTime(TimeUtils.getTimeFormat(file.lastModified()));
195 | databaseHelper.insert(fileBean);
196 |
197 | Log.i(TAG, "openFile: 3D文件的大小= "+file.length()/1024/1024+"M");
198 | if (file.length()>10*1024*1024) {
199 | ToastUtils.showLong(MainActivity.this, getString(R.string.tip_file_maybe_analysis_fail));
200 | }
201 | Intent it = new Intent(MainActivity.this, ShowModelActivity.class);
202 | // Intent it = new Intent(MainActivity.this, ShowModelActivity.class);
203 | it.putExtra("filePath", file.getAbsolutePath());
204 | startActivity(it);
205 | }
206 |
207 | /**
208 | * dp转px
209 | * @param dp
210 | * @return
211 | */
212 | private int dp2px(int dp) {
213 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
214 | getResources().getDisplayMetrics());
215 | }
216 |
217 |
218 | @Override
219 | public boolean onCreateOptionsMenu(Menu menu) {
220 | getMenuInflater().inflate(R.menu.menu_main, menu);
221 | return true;
222 | }
223 |
224 |
225 | @Override
226 | public boolean onOptionsItemSelected(MenuItem item) {
227 | int id = item.getItemId();
228 |
229 | if (id == R.id.action_about){
230 | Intent it_about = new Intent(MainActivity.this, AboutActivity.class);
231 | startActivity(it_about);
232 | return true;
233 | }
234 | return super.onOptionsItemSelected(item);
235 | }
236 | }
--------------------------------------------------------------------------------