现在的位置: 首页 > 综合 > 正文

Gallery3d 学习笔记(4)

2013年07月16日 ⁄ 综合 ⁄ 共 7674字 ⁄ 字号 评论关闭

发现每次都是要下岗前写博客,真不吉利啊,平时都没有空写。言归正传,上次的讲到RenderView和RootLayer,看起来必须把OpenGL的相关类弄清楚才能理解。

我们知道要使用的OpenGL 是画图的一堆接口(当然包括3D 纹理 渲染很多东西),在用这些的同时,我们的Activity本身还有响应触摸按键事件,重现绘制界面,这两者必须同时良好的运行,怎么做到这些?不用着急,android写好了一个类:android.opengl.GLSurfaceView

这个类有什么用,应该怎么使用呢?

这个类可以调用OpenGL API的接口,并添加自己的渲染器,如果要实现触摸监听等事件,就需要扩展这个类实现触摸监听。

public final class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer, SensorEventListener {
}

看下Gallery3d是如何做的 

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		if (mRenderView != null)
		{
			return mRenderView.onKeyDown(keyCode, event)
					|| super.onKeyDown(keyCode, event);
		} else
		{
			return super.onKeyDown(keyCode, event);
		}
	}

不知道大家明白没有,这样的代码意味着,如果mRenderView在的话,只要在mRenderView里面就可以处理这个Activity的按键消息。

为什么?因为Activity的按键消息(onKeyDown的消息) 都被转到mRenderView.onKeyDown中了。

同样的原理在

onPause

onResume

也有类似的代码,意味着这个View和Activity是同步动作的。

但是没有看到触摸的处理,触摸是在哪里处理的呢?

那么需要我们仔细看下我们继承后生成的RenderView里面的实现。

    public void setRootLayer(RootLayer layer) {
        if (mRootLayer != layer) {
            mRootLayer = layer;
            mListsDirty = true;
            if (layer != null) {
                mRootLayer.setSize(mViewWidth, mViewHeight);
            }
        }
    }

这里实现了mGridLayer变成了mRootLayer,实现了二者的关联。

    @Override
    public void onResume() {
        super.onResume();
        Sensor sensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (sensorAccelerometer != null) {
            mSensorManager.registerListener(this, sensorAccelerometer, SensorManager.SENSOR_DELAY_UI);
        }
        if (mRootLayer != null) {
            mRootLayer.onResume();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i(TAG, "OnPause RenderView " + this);
        mSensorManager.unregisterListener(this);
        if (mRootLayer != null) {
            mRootLayer.onPause();
        }
    }

在这里面,通过上面这些代码,又将Root层的动作和View层同步。

RenderView要负责刷新和用户输入,而且要处理渲染,他是要新开线程的,看他的构造函数。

    public RenderView(final Context context) {
        super(context);
        setBackgroundDrawable(null);
        setFocusable(true);
        setRenderer(this);
        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        if (sCachedTextureLoadThread == null) {
            for (int i = 0; i != NUM_TEXTURE_LOAD_THREADS; ++i) {
                TextureLoadThread thread = new TextureLoadThread();
                if (i == 0) {
                    sCachedTextureLoadThread = thread;
                }
                if (i == 1) {
                    sVideoTextureLoadThread = thread;
                }
                sTextureLoadThreads[i] = thread;
                thread.start();
            }
        }
    }

这里是循环,NUM_TEXTURE_LOAD_THREADS 值为4,而看到 在0和1时,分别是sCacheTextureLoadThread和sVideoTextureLoadThread,并且又将四个线程放在了sTextureLoadTheads数组里面。

就是用线程刷纹理,什么是纹理,就是一张图片,贴到物体的表面,比如一个木头的纹理图片,贴到一个正方形上,看起来就感觉这个正方形是个木头了。

        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            Deque<Texture> inputQueue = (sVideoTextureLoadThread == this) ? sLoadInputQueueVideo
                    : ((sCachedTextureLoadThread == this) ? sLoadInputQueueCached : sLoadInputQueue);
            Deque<Texture> outputQueue = sLoadOutputQueue;
            try {
                for (;;) {
                    // Pop the next texture from the input queue.
                    Texture texture = null;
                    synchronized (inputQueue) {
                        while ((texture = inputQueue.pollFirst()) == null) {
                            inputQueue.wait();
                        }
                    }
                    if (sCachedTextureLoadThread != this)
                        mIsLoading = true;
                    // Load the texture bitmap.
                    load(texture);
                    mIsLoading = false;

                    // Push the texture onto the output queue.
                    synchronized (outputQueue) {
                        outputQueue.addLast(texture);
                    }
                }
            } catch (InterruptedException e) {
                // Terminate the thread.
            }
        }

加载函数

        private void load(Texture texture) {
            // Generate the texture bitmap.
            RenderView view = RenderView.this;
            view.loadTextureAsync(texture);
            view.requestRender();
        }

可以看到,纹理的输入队列只有三种情况 

1 sLoadInputQueueVideo

2 sLoadInputQueueCached

3 sLoadInputQueue 

    private void queueLoad(final Texture texture, boolean highPriority) {
    	// Allow the texture to defer queuing.
        if (!texture.shouldQueue()) {
            return;
        }       

        // Change the texture state to loading.
        texture.mState = Texture.STATE_LOADING;

        // Push the texture onto the load input queue.
        Deque<Texture> inputQueue = (texture.isUncachedVideo()) ? sLoadInputQueueVideo
                : (texture.isCached()) ? sLoadInputQueueCached : sLoadInputQueue;
        ;
        synchronized (inputQueue) {
            if (highPriority) {
                inputQueue.addFirst(texture);
                // Enforce the maximum loading count by removing something from the end of
                // the loading queue, if necessary.
                if (mLoadingCount >= MAX_LOADING_COUNT) {
                	Texture unloadTexture = inputQueue.pollLast();
                	unloadTexture.mState = Texture.STATE_UNLOADED;
                	--mLoadingCount;
                }
            } else {
                inputQueue.addLast(texture);
            }
            inputQueue.notify();
        }
        ++mLoadingCount;
    }

onDrawFrame():

  每帧都通过该方法进行绘制。绘制时通常先调用 glClear函数来清空 framebuffer,然后在调用 OpenGL ES 的接口进行绘制。

    // @Override
    public void onDrawFrame(GL10 gl1) {
        GL11 gl = (GL11) gl1;
        if (!mFirstDraw) {
            Log.i(TAG, "First Draw");
        }
        mFirstDraw = true;
        // setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        // Rebuild the display lists if the render tree has changed.
        if (mListsDirty) {
            updateLists();
        }

        boolean wasLoadingExpensiveTextures = isLoadingExpensiveTextures();
        boolean loadingExpensiveTextures = false;
        int numTextureThreads = sTextureLoadThreads.length;
        for (int i = 2; i < numTextureThreads; ++i) {
            if (sTextureLoadThreads[i].mIsLoading) {
                loadingExpensiveTextures = true;
                break;
            }
        }
        if (loadingExpensiveTextures != wasLoadingExpensiveTextures) {
            mLoadingExpensiveTexturesStartTime = loadingExpensiveTextures ? SystemClock.uptimeMillis() : 0;
        }

        // Upload new textures.
        processTextures(false);

        // Update the current time and frame time interval.
        long now = SystemClock.uptimeMillis();
        final float dt = 0.001f * Math.min(50, now - mFrameTime);
        mFrameInterval = dt;
        mFrameTime = now;

        // Dispatch the current touch event.
        processCurrentEvent();
        processTouchEvent();
        // Run the update pass.
        final Lists lists = sLists;
        synchronized (lists) {
            final ArrayList<Layer> updateList = lists.updateList;
            boolean isDirty = false;
            for (int i = 0, size = updateList.size(); i != size; ++i) {
                boolean retVal = updateList.get(i).update(this, mFrameInterval);
                isDirty |= retVal;
            }
            if (isDirty) {
                requestRender();
            }

            // Clear the depth buffer.
            gl.glClear(GL11.GL_DEPTH_BUFFER_BIT);
            gl.glEnable(GL11.GL_SCISSOR_TEST);
            gl.glScissor(0, 0, getWidth(), getHeight());

            // Run the opaque pass.
            gl.glDisable(GL11.GL_BLEND);
            final ArrayList<Layer> opaqueList = lists.opaqueList;
            for (int i = opaqueList.size() - 1; i >= 0; --i) {
                final Layer layer = opaqueList.get(i);
                if (!layer.mHidden) {
                    layer.renderOpaque(this, gl);
                }
            }

            // Run the blended pass.
            gl.glEnable(GL11.GL_BLEND);
            final ArrayList<Layer> blendedList = lists.blendedList;
            for (int i = 0, size = blendedList.size(); i != size; ++i) {
                final Layer layer = blendedList.get(i);
                if (!layer.mHidden) {
                    layer.renderBlended(this, gl);
                }
            }
            gl.glDisable(GL11.GL_BLEND);
        }
    }

这段代码有点长,我们分析下,首先是列表如果有改动,会要更新层列表 update opaque blended,为什么只刷这三个层啊?因为只有在这三个列表的层才和刷新相关,更新层,半透明层和不透明层。

然后是处理纹理,将纹理上传到GPU(如果这里不清楚,看OpenGL的概念)

终于我们在这里找到了触摸的处理。

        // Dispatch the current touch event.
        processCurrentEvent();
        processTouchEvent();

大家应该还记得,上次我们已经把按键存起来了,触摸的事件保存在一个队列中了,现在可以处理了。

    private void processKeyEvent() {
        // Get the event.
        final KeyEvent event = mCurrentKeyEvent;
        boolean result = false;
        mCurrentKeyEvent = null;

        // Dispatch the event to the root layer.
        if (mRootLayer != null) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                result = mRootLayer.onKeyDown(event.getKeyCode(), event);
            } else {
                result = mRootLayer.onKeyUp(event.getKeyCode(), event);
            }
        }
        mCurrentKeyEventResult = result;
    }

以上代码说明了一个问题,所有的按键事件都是在Root Layer层中处理的。

再来看触摸的处理

    private void processTouchEvent() {
        MotionEvent event = null;
        int numEvents = mTouchEventQueue.size();
        int i = 0;
        do {
            // We look at the touch event queue and process one event at a time
            synchronized (mTouchEventQueue) {
                event = mTouchEventQueue.pollFirst();
            }
            if (event == null)
                return;

            // Detect the hit layer.
            final int action = event.getAction();
            Layer target;
            if (action == MotionEvent.ACTION_DOWN) {
                target = hitTest(event.getX(), event.getY());
                mTouchEventTarget = target;
            } else {
                target = mTouchEventTarget;
            }

            // Dispatch event to the hit layer.
            if (target != null) {
                target.onTouchEvent(event);
            }

            // Clear the hit layer.
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                mTouchEventTarget = null;
            }
            event.recycle();
            ++i;
        } while (event != null && i < numEvents);
        synchronized (this) {
            this.notify();
        }
    }

    private Layer hitTest(float x, float y) {
        final ArrayList<Layer> hitTestList = sLists.hitTestList;
        for (int i = hitTestList.size() - 1; i >= 0; --i) {
            final Layer layer = hitTestList.get(i);
            if (layer != null && !layer.mHidden) {
                final float layerX = layer.mX;
                final float layerY = layer.mY;
                if (x >= layerX && y >= layerY && x < layerX + layer.mWidth && y < layerY + layer.mHeight
                        && layer.containsPoint(x, y)) {
                    return layer;
                }
            }
        }
        return null;
    }

这两段代码看完,我们会发现触摸的处理方法。

但是,这个触摸的处理只是针对

hitTestList

这个层的LIST。但这个层的LIST在什么地方关联的?在什么地方添加的其他的层?呵呵,下次讲解。

抱歉!评论已关闭.