转载请注明出处:【huachao1001的专栏:http://blog.csdn.net/huachao1001】

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

前面两篇文章我们介绍了OpenGL相关的基本知识,现在我们已经会绘制基本的图案了,但是还远远不能满足我们的需求。我们要做的是显示任意的模型,这也是本文所要做的事情。在阅读本文之前,请先确保你已经看过我前面两篇文章:

  • 【Android OpenGL入门 】
  • 【Android OpenGL 显示基本图形及相关概念解读 】

虽然标题是说显示任意3D文件,但是本文主要是以STL格式文件为例。其他的格式本质上都是一样的,只是解析部分的代码不同而已。接下来我们开始学习~

1 STL文件

它是标准的3D文件格式,一般3D打印机都是支持打印STL文件,关于STL文件的格式、以及相关介绍请参考百度百科:【stl格式】。当然了,我在代码的注释中也会进行相关解释。

1.1 解析准备

首先,在解析STL文件格式之前,我们需要进行构思。我们无非就是把STL文件中的三角形的顶点信息提取出来。因此我们的主要目标就是把所有点信息读取出来。

但是,3D模型的坐标位置很随机,大小也随机。而不同的模型所处的位置不同,为了能够让模型处于手机显示中心,我们必须对模型进行移动、放缩处理。使得任意大小、任意位置的模型都能在我们的GLSurfaceView中以“相同”的大小显示。

因此,我们不仅仅要读取顶点信息,而且还要获取模型的边界信息。我们想象成一个立方体,这个立方体刚好包裹住模型。即我们要读取x、y、z三个方向上的最大值最小值。

1.2 开始解析

首先,我们定义一个Model类,用于表示一个模型对象:

package com.hc.opengl;import java.nio.FloatBuffer;/*** Package com.hc.opengl* Created by HuaChao on 2016/7/28.*/
public class Model {//三角面个数private int facetCount;//顶点坐标数组private float[] verts;//每个顶点对应的法向量数组private float[] vnorms;//每个三角面的属性信息private short[] remarks;//顶点数组转换而来的Bufferprivate FloatBuffer vertBuffer;//每个顶点对应的法向量转换而来的Bufferprivate FloatBuffer vnormBuffer;//以下分别保存所有点在x,y,z方向上的最大值、最小值float maxX;float minX;float maxY;float minY;float maxZ;float minZ;//返回模型的中心点//注意,下载的源码中,此函数修改修正如下public Point getCentrePoint() {float cx = minX + (maxX - minX) / 2;float cy = minY + (maxY - minY) / 2;float cz = minZ + (maxZ - minZ) / 2;return new Point(cx, cy, cz);}//包裹模型的最大半径public float getR() {float dx = (maxX - minX);float dy = (maxY - minY);float dz = (maxZ - minZ);float max = dx;if (dy > max)max = dy;if (dz > max)max = dz;return max;}//设置顶点数组的同时,设置对应的Bufferpublic void setVerts(float[] verts) {this.verts = verts;vertBuffer = Util.floatToBuffer(verts);}//设置顶点数组法向量的同时,设置对应的Bufferpublic void setVnorms(float[] vnorms) {this.vnorms = vnorms;vnormBuffer = Util.floatToBuffer(vnorms);}//···//其他属性对应的setter、getter函数//···}
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

接下来就是将stl文件转换成Model对象,我们定义一个STLReader类:

package com.hc.opengl;import android.content.Context;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;/*** Package com.hc.opengl* Created by HuaChao on 2016/7/28.*/
public class STLReader {private StlLoadListener stlLoadListener;public Model parserBinStlInSDCard(String path)throws IOException {File file = new File(path);FileInputStream fis = new FileInputStream(file);return parserBinStl(fis);}public Model parserBinStlInAssets(Context context, String fileName) throws IOException {InputStream is = context.getAssets().open(fileName);return parserBinStl(is);}//解析二进制的Stl文件public Model parserBinStl(InputStream in) throws IOException {if (stlLoadListener != null)stlLoadListener.onstart();Model model = new Model();//前面80字节是文件头,用于存贮文件名;in.skip(80);//紧接着用 4 个字节的整数来描述模型的三角面片个数byte[] bytes = new byte[4];in.read(bytes);// 读取三角面片个数int facetCount = Util.byte4ToInt(bytes, 0);model.setFacetCount(facetCount);if (facetCount == 0) {in.close();return model;}// 每个三角面片占用固定的50个字节byte[] facetBytes = new byte[50 * facetCount];// 将所有的三角面片读取到字节数组in.read(facetBytes);//数据读取完毕后,可以把输入流关闭in.close();parseModel(model, facetBytes);if (stlLoadListener != null)stlLoadListener.onFinished();return model;}/*** 解析模型数据,包括顶点数据、法向量数据、所占空间范围等*/private void parseModel(Model model, byte[] facetBytes) {int facetCount = model.getFacetCount();/***  每个三角面片占用固定的50个字节,50字节当中:*  三角片的法向量:(1个向量相当于一个点)*(3维/点)*(4字节浮点数/维)=12字节*  三角片的三个点坐标:(3个点)*(3维/点)*(4字节浮点数/维)=36字节*  最后2个字节用来描述三角面片的属性信息* **/// 保存所有顶点坐标信息,一个三角形3个顶点,一个顶点3个坐标轴float[] verts = new float[facetCount * 3 * 3];// 保存所有三角面对应的法向量位置,// 一个三角面对应一个法向量,一个法向量有3个点// 而绘制模型时,是针对需要每个顶点对应的法向量,因此存储长度需要*3// 又同一个三角面的三个顶点的法向量是相同的,// 因此后面写入法向量数据的时候,只需连续写入3个相同的法向量即可float[] vnorms = new float[facetCount * 3 * 3];//保存所有三角面的属性信息short[] remarks = new short[facetCount];int stlOffset = 0;try {for (int i = 0; i < facetCount; i++) {if (stlLoadListener != null) {stlLoadListener.onLoading(i, facetCount);}for (int j = 0; j < 4; j++) {float x = Util.byte4ToFloat(facetBytes, stlOffset);float y = Util.byte4ToFloat(facetBytes, stlOffset + 4);float z = Util.byte4ToFloat(facetBytes, stlOffset + 8);stlOffset += 12;if (j == 0) {//法向量 vnorms[i * 9] = x;vnorms[i * 9 + 1] = y;vnorms[i * 9 + 2] = z;vnorms[i * 9 + 3] = x;vnorms[i * 9 + 4] = y;vnorms[i * 9 + 5] = z;vnorms[i * 9 + 6] = x;vnorms[i * 9 + 7] = y;vnorms[i * 9 + 8] = z;} else {//三个顶点verts[i * 9 + (j - 1) * 3] = x;verts[i * 9 + (j - 1) * 3 + 1] = y;verts[i * 9 + (j - 1) * 3 + 2] = z;//记录模型中三个坐标轴方向的最大最小值if (i == 0 && j == 1) {model.minX = model.maxX = x;model.minY = model.maxY = y;model.minZ = model.maxZ = z;} else {model.minX = Math.min(model.minX, x);model.minY = Math.min(model.minY, y);model.minZ = Math.min(model.minZ, z);model.maxX = Math.max(model.maxX, x);model.maxY = Math.max(model.maxY, y);model.maxZ = Math.max(model.maxZ, z);}}}short r = Util.byte2ToShort(facetBytes, stlOffset);stlOffset = stlOffset + 2;remarks[i] = r;}} catch (Exception e) {if (stlLoadListener != null) {stlLoadListener.onFailure(e);} else {e.printStackTrace();}}//将读取的数据设置到Model对象中model.setVerts(verts);model.setVnorms(vnorms);model.setRemarks(remarks);}public static interface StlLoadListener {void onstart();void onLoading(int cur, int total);void onFinished();void onFailure(Exception e);}
}
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157

注意到,我们需要频繁的将byte数组转为short、float类型,我们直接把这些函数装到一个工具类Util中:

package com.hc.opengl;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;/*** Package com.hc.opengl* Created by HuaChao on 2016/7/28.*/
public class Util {public static FloatBuffer floatToBuffer(float[] a) {//先初始化buffer,数组的长度*4,因为一个float占4个字节ByteBuffer bb = ByteBuffer.allocateDirect(a.length * 4);//数组排序用nativeOrderbb.order(ByteOrder.nativeOrder());FloatBuffer buffer = bb.asFloatBuffer();buffer.put(a);buffer.position(0);return buffer;}public static int byte4ToInt(byte[] bytes, int offset) {int b3 = bytes[offset + 3] & 0xFF;int b2 = bytes[offset + 2] & 0xFF;int b1 = bytes[offset + 1] & 0xFF;int b0 = bytes[offset + 0] & 0xFF;return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;}public static short byte2ToShort(byte[] bytes, int offset) {int b1 = bytes[offset + 1] & 0xFF;int b0 = bytes[offset + 0] & 0xFF;return (short) ((b1 << 8) | b0);}public static float byte4ToFloat(byte[] bytes, int offset) {return Float.intBitsToFloat(byte4ToInt(bytes, offset));}}
  • 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

为了更好的表示三维坐标系下的一个点,我们定义Point类:

/*** Package com.hc.opengl* Created by HuaChao on 2016/7/28.*/
public class Point {public float x;public float y;public float z;public Point(float x, float y, float z) {this.x = x;this.y = y;this.z = z;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2 编写Render

上一节我们只是拿数据而已,还没开始绘制,真正的大招现在才开始。因为我们目标是显示任意模型,因此,必须把模型移动到我们的“视野”中,才能看得到(当然了,如果图形本身就是在我们的视野中,那就不一定需要这样的操作了)。废话不多说,直接看源码:

 /*** Package com.hc.opengl* Created by HuaChao on 2016/7/28.*/
public class GLRenderer implements GLSurfaceView.Renderer {private Model model;private Point mCenterPoint;private Point eye = new Point(0, 0, -3);private Point up = new Point(0, 1, 0);private Point center = new Point(0, 0, 0);private float mScalef = 1;private float mDegree = 0;public GLRenderer(Context context) {try {model = new STLReader().parserBinStlInAssets(context, "huba.stl");} catch (IOException e) {e.printStackTrace();}}public void rotate(float degree) {mDegree = degree;}@Overridepublic void onDrawFrame(GL10 gl) {// 清除屏幕和深度缓存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);gl.glLoadIdentity();// 重置当前的模型观察矩阵//眼睛对着原点看 GLU.gluLookAt(gl, eye.x, eye.y, eye.z, center.x,center.y, center.z, up.x, up.y, up.z);//为了能有立体感觉,通过改变mDegree值,让模型不断旋转gl.glRotatef(mDegree, 0, 1, 0);//将模型放缩到View刚好装下gl.glScalef(mScalef, mScalef, mScalef);//把模型移动到原点gl.glTranslatef(-mCenterPoint.x, -mCenterPoint.y,-mCenterPoint.z);//===================begin==============================////允许给每个顶点设置法向量gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);// 允许设置顶点gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// 允许设置颜色//设置法向量数据源gl.glNormalPointer(GL10.GL_FLOAT, 0, model.getVnormBuffer());// 设置三角形顶点数据源gl.glVertexPointer(3, GL10.GL_FLOAT, 0, model.getVertBuffer());// 绘制三角形gl.glDrawArrays(GL10.GL_TRIANGLES, 0, model.getFacetCount() * 3);// 取消顶点设置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);//取消法向量设置gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);//=====================end============================//}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {// 设置OpenGL场景的大小,(0,0)表示窗口内部视口的左下角,(width, height)指定了视口的大小gl.glViewport(0, 0, width, height);gl.glMatrixMode(GL10.GL_PROJECTION); // 设置投影矩阵gl.glLoadIdentity(); // 设置矩阵为单位矩阵,相当于重置矩阵GLU.gluPerspective(gl, 45.0f, ((float) width) / height, 1f, 100f);// 设置透视范围//以下两句声明,以后所有的变换都是针对模型(即我们绘制的图形)gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity();}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {gl.glEnable(GL10.GL_DEPTH_TEST); // 启用深度缓存gl.glClearDepthf(1.0f); // 设置深度缓存值gl.glDepthFunc(GL10.GL_LEQUAL); // 设置深度缓存比较函数gl.glShadeModel(GL10.GL_SMOOTH);// 设置阴影模式GL_SMOOTHfloat r = model.getR();//r是半径,不是直径,因此用0.5/r可以算出放缩比例mScalef = 0.5f / r;mCenterPoint = model.getCentrePoint();}
}
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105

在MainActivity中不断调用旋转函数:

package com.hc.opengl;public class MainActivity extends AppCompatActivity {private boolean supportsEs2;private GLSurfaceView glView;private float rotateDegreen = 0;private GLRenderer glRenderer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);checkSupported();if (supportsEs2) {glView = new GLSurfaceView(this);glRenderer = new GLRenderer(this);glView.setRenderer(glRenderer);setContentView(glView);} else {setContentView(R.layout.activity_main);Toast.makeText(this, "当前设备不支持OpenGL ES 2.0!", Toast.LENGTH_SHORT).show();}}public void rotate(float degree) {glRenderer.rotate(degree);glView.invalidate();}private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {rotate(rotateDegreen);}};@Overrideprotected void onResume() {super.onResume();if (glView != null) {glView.onResume();//不断改变rotateDegreen值,实现旋转new Thread() {@Overridepublic void run() {while (true) {try {sleep(100);rotateDegreen += 5;handler.sendEmptyMessage(0x001);} catch (Exception e) {e.printStackTrace();}}}}.start();}}private void checkSupported() {ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();supportsEs2 = configurationInfo.reqGlEsVersion >= 0x2000;boolean isEmulator = Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1&& (Build.FINGERPRINT.startsWith("generic")|| Build.FINGERPRINT.startsWith("unknown")|| Build.MODEL.contains("google_sdk")|| Build.MODEL.contains("Emulator")|| Build.MODEL.contains("Android SDK built for x86"));supportsEs2 = supportsEs2 || isEmulator;}@Overrideprotected void onPause() {super.onPause();if (glView != null) {glView.onPause();}}}
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

3 最后一步

一切看起来都已经完成了,但似乎少了点什么。啊哈~,少了STL文件,其实网上有很多STL模型文件免费下载,大家可以随便搜索。我下载了一个胡巴的模型:

下载完成后,运行如下:

看到结果是不是觉得很失望?貌似看不到轮廓,其实,主要是跟灯光有关,我们程序中没有设置灯光。我们知道,我们在真实世界中看到物体主要是物体表面发生漫反射。我们所看到的物体跟光源的位置、物体的材质等等有关。另外,也可以通过贴纹理来做到。但是到目前为止,我们还没有这些知识,代码里面也没有涉及到这些,因此我们这能看到当前这个样子。后面我们会继续深入学习相关知识,欢迎关注~。

好啦,最后献上源码吧~,注意,下载的源码中Model类的getCentrePoint函数需要修改,请以本文中的Model类为主。

源码地址:http://download.csdn.net/detail/huachao1001/9588619

Android OpenGL显示任意3D模型文件相关推荐

  1. android OpenGL渲染3D模型文件

    码字不易,转载请注明出处喔 https://blog.csdn.net/newchenxf/article/details/121402859 1 前言 大部分OpenGL示例代码,要么播放个视频,要 ...

  2. 现代opengl 设计 3D模型文件导入显示

    利用3D模型导入库assimp, 可以读取很多种格式的3D模型文件,现在我们就用opengl 显示出来.而这需要一个桥,就是数据和opengl 显示,前文我们介绍了网格Mesh类,现在介绍model ...

  3. 移动端利用OpenGL展示3D模型文件STL

    移动端利用OpenGL展示3D模型文件STL 突然发现上次写博客都是一年前了,没养成分享的习惯挺郁闷的,所以分享下个人感觉好玩的东西吧.纯理工科生笔杆子不硬,写的不好,哪里有看不懂的或者写的不好的希望 ...

  4. Kicad如何导入封装库、符号库(元件库)以及3D模型文件?

    简介 Kicad自带了一些基础的符号库,但是如果我们想使用的一些指定类型的元器件不在其中怎么办? 我们可以去一些开源的符号官网中去下载并导入到Kicad中就可以了,如果你使用的是指定厂家的元器件那么理 ...

  5. Panda3D如何加载obj格式的3D模型文件

    ​ 在上文中,讲了如何将max.obj.mb.fbx等主流格式的3D模型文件转换为egg.gltf格式,Panda3D可以加载的格式.在上文末尾,博主临时看到了一种方式,可以让Panda3d直接加载o ...

  6. solidworks 3D模型文件导出到tanner ledit版图软件中

    solidworks 3D模型文件导出到tanner ledit版图软件中 简介 操作 1. 文件格式问题 2. SW直接导出DXF文件到LEDIT的问题 3.布尔操作,得到所需版图 注意事项 补充 ...

  7. 3ds Max、Maya安装Babylon.js插件导出gltf格式的3D模型文件方法

    3ds Max.Maya安装Babylon.js插件导出gltf格式的3D模型文件方法 **1.去Github project Releases下载Babylon.js插件. 找到和自己安装的3ds ...

  8. C++解析3d模型文件jt

    1.JT格式介绍 JT(Jupiter Tessellation)是一种高效.专注于行业且灵活的 ISO 标准化 3D 数据格式.西门子PLM Software开发的轻量级3D模型格式: 设计为产品数 ...

  9. 3D模型文件--STL,OBJ,3DS

    STL格式 全称是Stereolithographic,是3DSystem公司提出的3D模型文件格式,它采用三角形离散地近似表示三维模型,目前已被工业界认为是快速成形领域的标准描述文件格式. STL文 ...

最新文章

  1. DEDECMS教程:上/下一篇文章标题长度的截取方法
  2. react native基本调试技巧
  3. Fabric--node测试
  4. flash as3 android air 插入视频,FLASH加载外部影片as3代码
  5. 川崎机器人c#通讯(转)
  6. 判断闰年 php,PHP怎么判断一年是否为闰年?
  7. 《C语言深度剖析》学习笔记三
  8. 依赖注入和控制反转的理解(转载)
  9. 你了解语音识别技术吗?
  10. datagrip连接oracle
  11. Android Toast 总结
  12. bulk interface驱动_USB驱动程序(较详细)三
  13. AI换脸骗过App,林俊杰和我们都怕
  14. 理解Marx-5 从巴黎到布鲁塞尔,创立历史唯物主义
  15. 基于 SSR 的预渲染首屏直出方案
  16. Ubuntu14.04虚拟机下基本操作(typical安装)
  17. Unity安装配置Android环境 SDK,JDK,JIR
  18. Linux学习-21-yum命令(查询、安装、升级和卸载软件包)和软件组管理
  19. 【可用】vivoX7升级安卓7.1系统刷机包下载地址
  20. omnet++Aloha案例解析

热门文章

  1. poj 1847 Tram 最短路 dijkstra、floyed
  2. numpy.linspace()的使用方法
  3. source:读取文件 “/etc/profile” 时发生错误解决办法
  4. day24 java的集合(1)collection
  5. 链路两段不同网段怎么通信_交换机互联不同vlan及不同网段通信问题总结
  6. 实现树状结构_钢结构设计 | “生命之树”景观案例赏析
  7. 禅道的安装与简单使用
  8. was环境访问https_Go语言笔记|02-开发环境搭建
  9. python如何对文件进行批量命名-利用Python对文件批量重命名
  10. android 不同项目代码合并在一块,android - 是否可以将两个(正在积极开发中的)Android应用程序模块合并到同一个Android Studio项目中? - 堆栈内存溢出...