Hi everyone, I have a project that need to achieve the following requirements:
- Show android camera preview
- Load a 3d model on top of it
- Record the current display and save to a video file
And I tried to implement 1&2 with the following code, it throws an ArrayIndexOutOfBoundsException.
java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at com.threed.jpct.CompiledInstance._fill(CompiledInstance.java:1206)
at com.threed.jpct.CompiledInstance.fill(CompiledInstance.java:746)
at com.threed.jpct.Object3DCompiler.compile(Object3DCompiler.java:148)
at com.threed.jpct.World.compile(World.java:1951)
at com.threed.jpct.World.renderScene(World.java:1046)
at test.com.cameratest.MainRenderer.onDrawFrame(MainActivity.java:196)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1522)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1239)
Here is my code:
public class MainActivity extends Activity {
private MainView mView;
private PowerManager.WakeLock mWL;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// full screen & full brightness
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mWL = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.FULL_WAKE_LOCK, "WakeLock");
mWL.acquire();
mView = new MainView(this);
setContentView(mView);
}
@Override
protected void onPause() {
if (mWL.isHeld())
mWL.release();
mView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mView.onResume();
if (!mWL.isHeld()) mWL.acquire();
}
}
// View
class MainView extends GLSurfaceView {
MainRenderer mRenderer;
MainView(Context context) {
super(context);
mRenderer = new MainRenderer(this);
setEGLContextClientVersion(2);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
public void surfaceCreated(SurfaceHolder holder) {
super.surfaceCreated(holder);
}
public void surfaceDestroyed(SurfaceHolder holder) {
mRenderer.close();
super.surfaceDestroyed(holder);
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
super.surfaceChanged(holder, format, w, h);
}
}
// Renderer
class MainRenderer implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
private final String vss =
"attribute vec2 vPosition;\n" +
"attribute vec2 vTexCoord;\n" +
"varying vec2 texCoord;\n" +
"void main() {\n" +
" texCoord = vTexCoord;\n" +
" gl_Position = vec4 ( vPosition.x, vPosition.y, 0.0, 1.0 );\n" +
"}";
private final String fss =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"uniform samplerExternalOES sTexture;\n" +
"varying vec2 texCoord;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture,texCoord);\n" +
"}";
private int[] hTex;
private FloatBuffer pVertex;
private FloatBuffer pTexCoord;
private int hProgram;
private Camera mCamera;
private SurfaceTexture mSTexture;
private boolean mUpdateST = false;
private MainView mView;
private FrameBuffer fb = null;
private World world = null;
private Object3D model = null;
private Light sun = null;
MainRenderer(MainView view) {
mView = view;
float[] vtmp = {1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f};
float[] ttmp = {1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f};
pVertex = ByteBuffer.allocateDirect(8 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
pVertex.put(vtmp);
pVertex.position(0);
pTexCoord = ByteBuffer.allocateDirect(8 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
pTexCoord.put(ttmp);
pTexCoord.position(0);
}
public void close() {
mUpdateST = false;
mSTexture.release();
mCamera.stopPreview();
mCamera.release();
mCamera = null;
deleteTex();
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
System.out.println("onSurfaceCreated");
//String extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS);
//Log.i("mr", "Gl extensions: " + extensions);
//Assert.assertTrue(extensions.contains("OES_EGL_image_external"));
initTex();
mSTexture = new SurfaceTexture(hTex[0]);
mSTexture.setOnFrameAvailableListener(this);
mCamera = Camera.open();
try {
mCamera.setPreviewTexture(mSTexture);
} catch (IOException ioe) {
}
GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
hProgram = loadShader(vss, fss);
initModel();
}
public void onDrawFrame(GL10 unused) {
System.out.println("onDrawFrame");
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
fb.clear();
world.renderScene(fb);
world.draw(fb);
fb.display();
synchronized (this) {
if (mUpdateST) {
mSTexture.updateTexImage();
mUpdateST = false;
}
}
GLES20.glUseProgram(hProgram);
int ph = GLES20.glGetAttribLocation(hProgram, "vPosition");
int tch = GLES20.glGetAttribLocation(hProgram, "vTexCoord");
int th = GLES20.glGetUniformLocation(hProgram, "sTexture");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, hTex[0]);
GLES20.glUniform1i(th, 0);
GLES20.glVertexAttribPointer(ph, 2, GLES20.GL_FLOAT, false, 4 * 2, pVertex);
GLES20.glVertexAttribPointer(tch, 2, GLES20.GL_FLOAT, false, 4 * 2, pTexCoord);
GLES20.glEnableVertexAttribArray(ph);
GLES20.glEnableVertexAttribArray(tch);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
// GLES20.glFlush();
}
private void initModel() {
world = new World();
world.setAmbientLight(20, 20, 20);
sun = new Light(world);
sun.setIntensity(250, 250, 250);
Texture texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(App.context.getResources().getDrawable(R.drawable.monster)), 512, 512));
TextureManager.getInstance().addTexture("texture", texture);
model = loadModel(R.raw.monster, 1);
model.setTexture("texture");
model.build();
world.addObject(model);
com.threed.jpct.Camera cam = world.getCamera();
cam.moveCamera(com.threed.jpct.Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(model.getTransformedCenter());
SimpleVector sv = new SimpleVector();
sv.set(model.getTransformedCenter());
sv.y -= 100;
sv.z -= 100;
sun.setPosition(sv);
MemoryHelper.compact();
}
private Object3D loadModel(int filename, float scale) {
InputStream stream = App.context.getResources().openRawResource(filename);
Object3D[] model = Loader.load3DS(stream, scale);
Object3D o3d = new Object3D(0);
Object3D temp = null;
for (int i = 0; i < model.length; i++) {
temp = model[i];
System.out.println("model:" + temp.getName());
temp.setCenter(SimpleVector.ORIGIN);
temp.rotateX((float) (-.5 * Math.PI));
temp.rotateMesh();
temp.setRotationMatrix(new Matrix());
o3d = Object3D.mergeObjects(o3d, temp);
o3d.build();
}
return o3d;
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
System.out.println("onSurfaceChanged");
if (fb != null) {
fb.dispose();
}
fb = new FrameBuffer(unused, width, height);
GLES20.glViewport(0, 0, width, height);
Camera.Parameters param = mCamera.getParameters();
List<Camera.Size> psize = param.getSupportedPreviewSizes();
if (psize.size() > 0) {
int i;
for (i = 0; i < psize.size(); i++) {
if (psize.get(i).width < width || psize.get(i).height < height)
break;
}
if (i > 0)
i--;
param.setPreviewSize(psize.get(i).width, psize.get(i).height);
//Log.i("mr","ssize: "+psize.get(i).width+", "+psize.get(i).height);
}
param.set("orientation", "landscape");
mCamera.setParameters(param);
mCamera.startPreview();
}
private void initTex() {
hTex = new int[1];
GLES20.glGenTextures(1, hTex, 0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, hTex[0]);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
}
private void deleteTex() {
GLES20.glDeleteTextures(1, hTex, 0);
}
public synchronized void onFrameAvailable(SurfaceTexture st) {
mUpdateST = true;
mView.requestRender();
}
private static int loadShader(String vss, String fss) {
int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
GLES20.glShaderSource(vshader, vss);
GLES20.glCompileShader(vshader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e("Shader", "Could not compile vshader");
Log.v("Shader", "Could not compile vshader:" + GLES20.glGetShaderInfoLog(vshader));
GLES20.glDeleteShader(vshader);
vshader = 0;
}
int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(fshader, fss);
GLES20.glCompileShader(fshader);
GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e("Shader", "Could not compile fshader");
Log.v("Shader", "Could not compile fshader:" + GLES20.glGetShaderInfoLog(fshader));
GLES20.glDeleteShader(fshader);
fshader = 0;
}
int program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vshader);
GLES20.glAttachShader(program, fshader);
GLES20.glLinkProgram(program);
return program;
}
}
Sample project:
https://www.dropbox.com/s/d85q88bx27m71oj/CameraTest.zip?dl=0Is my approach feasible? Please give me some advice, thank you!