Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/225.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java OpenGL 2.0 glGetUniformLocation:draw()中的glError 1282_Java_Android_Opengl Es - Fatal编程技术网

Java OpenGL 2.0 glGetUniformLocation:draw()中的glError 1282

Java OpenGL 2.0 glGetUniformLocation:draw()中的glError 1282,java,android,opengl-es,Java,Android,Opengl Es,我已经制作了一个游戏,它可以正确地绘制openGL图形,以在大部分游戏中显示屏幕。但由于某种原因,在游戏中随机间隔我会得到1282 GLGETNIFORMLLOCATION错误。奇怪的是,它工作了一段时间却失败了 这是堆栈跟踪 12-12 12:31:54.781: E/AndroidRuntime(2531): FATAL EXCEPTION: GLThread 86993 12-12 12:31:54.781: E/AndroidRuntime(2531): Process: com.lay

我已经制作了一个游戏,它可以正确地绘制openGL图形,以在大部分游戏中显示屏幕。但由于某种原因,在游戏中随机间隔我会得到1282 GLGETNIFORMLLOCATION错误。奇怪的是,它工作了一段时间却失败了

这是堆栈跟踪

12-12 12:31:54.781: E/AndroidRuntime(2531): FATAL EXCEPTION: GLThread 86993
12-12 12:31:54.781: E/AndroidRuntime(2531): Process: com.laytonlabs.android.levelup, PID: 2531
12-12 12:31:54.781: E/AndroidRuntime(2531): java.lang.RuntimeException: glGetUniformLocation: glError 1282
12-12 12:31:54.781: E/AndroidRuntime(2531):     at com.laytonlabs.android.levelup.MyGLRenderer.checkGlError(MyGLRenderer.java:460)
12-12 12:31:54.781: E/AndroidRuntime(2531):     at com.laytonlabs.android.levelup.shapes.Shape.draw(Shape.java:240)
12-12 12:31:54.781: E/AndroidRuntime(2531):     at com.laytonlabs.android.levelup.MyGLRenderer.drawShapes(MyGLRenderer.java:350)
12-12 12:31:54.781: E/AndroidRuntime(2531):     at com.laytonlabs.android.levelup.MyGLRenderer.drawFixedShapes(MyGLRenderer.java:324)
12-12 12:31:54.781: E/AndroidRuntime(2531):     at com.laytonlabs.android.levelup.MyGLRenderer.onDrawFrame(MyGLRenderer.java:205)
12-12 12:31:54.781: E/AndroidRuntime(2531):     at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1531)
12-12 12:31:54.781: E/AndroidRuntime(2531):     at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1248)
我想这就是在Shape.java draw()函数中生成错误的部分

Shape.java

mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
package com.laytonlabs.android.levelup;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.laytonlabs.android.levelup.game.Cell;
import com.laytonlabs.android.levelup.game.CurrentAnswer;
import com.laytonlabs.android.levelup.game.Equation;
import com.laytonlabs.android.levelup.game.Game;
import com.laytonlabs.android.levelup.game.Level;
import com.laytonlabs.android.levelup.game.Score;
import com.laytonlabs.android.levelup.game.Stage;
import com.laytonlabs.android.levelup.game.Time;
import com.laytonlabs.android.levelup.shapes.Color;
import com.laytonlabs.android.levelup.shapes.EquationRectangle;
import com.laytonlabs.android.levelup.shapes.Hexagon;
import com.laytonlabs.android.levelup.shapes.InputSquare;
import com.laytonlabs.android.levelup.shapes.Shape;
import com.laytonlabs.android.levelup.shapes.StatsRectangle;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;

public class MyGLRenderer implements GLSurfaceView.Renderer {

    private static final String TAG = "MyGLRenderer";
    private static StatsRectangle levelRectangle;
    private static StatsRectangle timeRectangle;
    private static StatsRectangle scoreRectangle;
    private static EquationRectangle equationRectangle;
    private static EquationRectangle answerRectangle;
    private ArrayList<Shape> gridShapes;
    private ArrayList<Shape> bottomRowShapes;
    private static ArrayList<Shape> inputShapes;

    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private final float[] mMVPMatrix = new float[16];
    private final float[] mMVPFixed = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    private final float[] mGridModelMatrix = new float[16];
    private float[] mFixedModelMatrix = new float[16];
    private float[] mTempMatrix = new float[16];

    private float mMovementY;
    private float mBottomRowScale; 
    private static String mAnswerText = CurrentAnswer.getLabel();
    private boolean isCorrectGuess = false;
    private boolean renderCorrectGuess = false;
    private boolean isWrongGuess = false;
    private boolean renderOutput = false;

    //To limit the number of renders per second
    private long startTime;
    private long endTime;
    private long timeElapsed;
    private int currentFrame = 0;              // active frame
    private int outputCurrentFrame = 0;              // active frame
    private final int FPS = 33;                // Frames per second
    private final int FPS_ANIMATION_10 = 10;
    private final int FPS_ANIMATION_20 = 20;

    //Reducer values, these are used for animation scenes
    private float mFPSMovementY;
    private float mFPSBottomRowScale;

    private int gridLevel = 0; //The cell layout level in the grid.
    private int rowLevel = 1; //The current row in the grid user is selected.

    //Constants for grid presentation 
    private final float CELL_SCALE = 0.3f;
    private final float CELL_OFFSET_Y = 0.7f;

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {

        // Set the background frame color
        GLES20.glClearColor(Color.DARK_GREY[0], Color.DARK_GREY[1], Color.DARK_GREY[2], Color.DARK_GREY[3]);

        startTime = System.currentTimeMillis();

        //TODO Add code that sets current answer to whatever the current answer is.

        //Initialise fixed shapes
        equationRectangle = new EquationRectangle(-0.35f);
        answerRectangle = new EquationRectangle(-0.5f);

        equationRectangle.setShapes(0.2f, Equation.get());
        answerRectangle.setShapes(0.3f, mAnswerText);

        //TODO - Change the below calculations to be align_left, align_centre, align_right, etc.
        levelRectangle = new StatsRectangle(0 - (Screen.DEFAULT_WIDTH/3), Color.TURQUOISE, Color.TURQUOISE);
        timeRectangle = new StatsRectangle(0, Color.PURPLE, Color.PURPLE);
        scoreRectangle = new StatsRectangle(0 + (Screen.DEFAULT_WIDTH/3), Color.TURQUOISE, Color.TURQUOISE);

        levelRectangle.setShapes(-1f, Level.getLabel());
        scoreRectangle.setShapes(-1f, Score.getScoreLabel());
        timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());

        setGridShapes();
        //Complete the bottom row for inputting guesses
        bottomRowShapes = new ArrayList<Shape>();
        setBottomRowShapes();
        buildInputGrid();
        setBottomRowScale(1.0f);
        setFPSBottomRowScale(getBottomRowScale() / FPS_ANIMATION_20);
        setFPSMovementY((CELL_OFFSET_Y*CELL_SCALE) / FPS_ANIMATION_20);
    }

    private void buildInputGrid() {
        inputShapes = new ArrayList<Shape>();

        inputShapes.add(new InputSquare(0.16f, -3.0f, 1.15f, "1")); //1
        inputShapes.add(new InputSquare(0.16f, -1.8f, 1.15f, "2")); //2
        inputShapes.add(new InputSquare(0.16f, -0.6f, 1.15f, "3")); //3
        inputShapes.add(new InputSquare(0.16f,  0.6f, 1.15f, "4")); //4
        inputShapes.add(new InputSquare(0.16f,  1.8f, 1.15f, "5")); //5
        inputShapes.add(new InputSquare(0.16f,  3.0f, 1.15f, "6")); //6
        inputShapes.add(new InputSquare(0.16f, -2.4f,     0, "7")); //7
        inputShapes.add(new InputSquare(0.16f, -1.2f,     0, "8")); //8
        inputShapes.add(new InputSquare(0.16f,     0,     0, "9")); //9
        inputShapes.add(new InputSquare(0.16f,  1.2f,     0, "0")); //0
        inputShapes.add(new InputSquare(0.16f,  2.4f,     0, "x")); //X - This is to clear input
    }

    @Override
    public void onDrawFrame(GL10 unused) {

        //We dont need continuous rendering, only needed for animation and time switching
        endTime = System.currentTimeMillis();
        timeElapsed = endTime - startTime;
        if (timeElapsed < FPS) {
            try {
                Log.d(TAG, "Sleeping until "+FPS+" millsecs pass");
                Thread.sleep(FPS - timeElapsed);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        startTime = endTime;

        //Update the timers by deducting timeRemaining
        Time.update();

        Matrix.setIdentityM(mGridModelMatrix, 0); // initialize to identity matrix
        //Setup the equation display before we start moving the grid around
        Matrix.setIdentityM(mFixedModelMatrix, 0); // initialize to identity matrix

        // Draw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // Enable transparency options for colors.
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        // Set the camera position (View matrix)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

        // Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        //Clone this for use with fixed and grid MVPs
        mTempMatrix = mMVPMatrix.clone();

        // Create a rotation for the triangle

        // Use the following code to generate constant rotation.
        // Leave this code out when using TouchEvents.
        // long time = SystemClock.uptimeMillis() % 4000L;
        // float angle = 0.090f * ((int) time);

        drawGridShapes();

        drawFixedShapes();
    }

    private void drawGridShapes() {     
        //Start the grid drawing at bottom of screen.
        Matrix.translateM(mGridModelMatrix, 0, 0, -0.1f, 0);

        //Move the grid down or up the screen depending on touch events.
        Matrix.translateM(mGridModelMatrix, 0, 0, mMovementY, 0);

        // Combine the rotation matrix with the projection and camera view
        // Note that the mMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        //Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

        //Add the movement to the matrix
        Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mGridModelMatrix, 0);

        if (isCorrectGuess()) {
            renderCorrectGuess = true; //This is to change the answer text to green inside drawFixedShapes
            currentFrame++;                    // step to next frame
            setMovementY(getMovementY() - getFPSMovementY());
            setBottomRowScale(getBottomRowScale() - getFPSBottomRowScale());
            if (currentFrame >= FPS_ANIMATION_20) {            // if end of sequence
                currentFrame = 0;               // restart sequence
                setBottomRowScale(1.0f);        // Reset the scale
                removeBottomRow();
                setCorrectGuess(false);         //Mark as false so animation stops and user can make new guess
            }
        }

        //Draw all grid shapes
        if (isCorrectGuess()) {
            drawAllShapesAndShrinkBottomRow(mMVPMatrix);
        } else {
            drawAllShapes(getGridShapes(), mMVPMatrix);
        }
    }

    private void drawAllShapes(ArrayList<Shape> shapes, float[] mMVPMatrix) {
        for (Shape shape : shapes) {
            Log.d(TAG, "Scale Hexagon ("+shape.toString()+") Aft ("+shape.getCentreX()+", "+shape.getCentreY()+")");
            drawShapes(shape, mMVPMatrix);
        }
    }

    private void drawAllShapesAndShrinkBottomRow(float[] mMVPMatrix) {
        float[] mMVPScaled = mMVPMatrix.clone();

        Matrix.scaleM(mMVPScaled, 0, getBottomRowScale(), getBottomRowScale(), 0);

        int lastCellIndex = getBottomRowLastCellIndex();

        //Apply scaling to the bottom row and just move the other rows.
        for (int i = 0; i < getGridShapes().size(); i++) {
            if (i <= lastCellIndex) {
                drawShapes(getGridShapes().get(i), mMVPScaled);
            } else {
                drawShapes(getGridShapes().get(i), mMVPMatrix);
            }
        }
    }

    private void drawFixedShapes() {

        Matrix.multiplyMM(mMVPFixed, 0, mTempMatrix, 0, mFixedModelMatrix, 0);

        if (isRenderOutput()) {
            //Show the equation using the values from the selected cell.
            equationRectangle.setShapes(0.2f, Equation.get());
            answerRectangle.setShapes(0.3f, mAnswerText);

            setRenderOutput(false);
        }

        //Update the time if time has changed
        if (!timeRectangle.toString().equals(Time.getTimeRemainingLabel())) {
            //If the time remaining is almost up then change text to red.
            if (Time.isTimeAlmostUp()) {
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel(), Color.RED);
            } else {
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
            }
        }

        //Animation for changing color of the text
        if (isWrongGuess()) {
            if (outputCurrentFrame == 0) {
                answerRectangle.setShapes(0.3f, mAnswerText, Color.RED);
            }
            outputCurrentFrame++;
            if (outputCurrentFrame >= FPS_ANIMATION_10) {
                outputCurrentFrame = 0;
                setAnswerText("");
                answerRectangle.setShapes(0.3f, mAnswerText);
                setWrongGuess(false);           
            }
        } else if (renderCorrectGuess) {
            if (outputCurrentFrame == 0) {
                answerRectangle.setShapes(0.3f, mAnswerText, Color.GREEN);
                levelRectangle.setShapes(-1f, Level.getLabel(), Color.YELLOW);
                scoreRectangle.setShapes(-1f, Score.getScoreLabel(), Color.YELLOW);
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel(), Color.YELLOW);
            }
            outputCurrentFrame++;
            if (outputCurrentFrame >= FPS_ANIMATION_20) {
                outputCurrentFrame = 0;
                answerRectangle.setShapes(0.3f, mAnswerText);
                levelRectangle.setShapes(-1f, Level.getLabel());
                scoreRectangle.setShapes(-1f, Score.getScoreLabel());
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
                setGridShapes();
                renderCorrectGuess = false;     
            }
        }

        drawShapes(answerRectangle, mMVPFixed);
        drawShapes(equationRectangle, mMVPFixed);
        drawShapes(levelRectangle, mMVPFixed);
        drawShapes(timeRectangle, mMVPFixed);
        drawShapes(scoreRectangle, mMVPFixed);

        //Draw all input grid shapess
        drawAllShapes(inputShapes, mMVPFixed);
    }

    public static void printStack() {
        Log.e(TAG,"Level: " + Level.getLabel());
        Log.e(TAG,"Score: " + Score.getScoreLabel());
        Log.e(TAG,"Time: " + Time.getTimeRemainingLabel());
        Log.e(TAG,"mAnswerText: " + mAnswerText);
        Log.e(TAG,"Equation: " + Equation.get());
        for (int i = 0; i < inputShapes.size(); i++) {
            Log.e(TAG,"inputShapes[" + i + "]: " + inputShapes.get(i).toString());
        }
    }

    private void drawShapes(Shape parentShape, float[] mMVPMatrix) {
        parentShape.draw(mMVPMatrix);

        if (parentShape.getShapes() == null) {
            return;
        }

        for (Shape nestedShapes : parentShape.getShapes()) {
            nestedShapes.draw(mMVPMatrix);
        }
    }

    private ArrayList<Shape> getGridShapes() {
        return gridShapes;
    }

    private int getBottomRowLastCellIndex() {
        int lastCellIndex = 0;
        Shape prevShape = null;
        for (int i = 0; i < getGridShapes().size(); i++) {
            if (prevShape == null || getGridShapes().get(i).getCentreY() == prevShape.getCentreY()) {
                lastCellIndex = i;
                prevShape = getGridShapes().get(i);
            } else {
                return lastCellIndex;
            }
        }
        return lastCellIndex;
    }

    private void removeBottomRow() {
        for (int i = getBottomRowLastCellIndex(); i >= 0 ; i--) {
            getGridShapes().remove(i);
        }
        //Reset the bottom row shapes
        setBottomRowShapes();
    }

    public ArrayList<Shape> getBottomRowShapes() {
        return bottomRowShapes;
    }

    private void setBottomRowShapes() {
        ArrayList<Shape> tempRowShapes = new ArrayList<Shape>();

        //Apply scaling to the bottom row and just move the other rows.
        for (int i = 0; i <= getBottomRowLastCellIndex(); i++) {
            tempRowShapes.add(getGridShapes().get(i));
        }
        bottomRowShapes = tempRowShapes;
    }

    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        // Adjust the viewport based on geometry changes,
        // such as screen rotation
        GLES20.glViewport(0, 0, width, height);

        //Log.d("MyGLRenderer", "Width: " + width + " Height: " + height);

        float ratio = (float) width / height;

        Log.d("Screen","Width: "+ width +" - Height: "+ height +" - Ratio: "+ ratio);

        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
        if (ratio > 1) {
            ratio = Screen.DEFAULT_LANDSCAPE_RATIO;
        } else {
            ratio = Screen.DEFAULT_PORTRAIT_RATIO;
        }

        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

        //TODO Store the current answer, the current answer Text and the current Equation. 
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

    public static void checkGlError(String glOperation) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e(TAG, glOperation + ": 1glError " + error);
            printStack();
            //TODO - Print out the fixed shapes values to see if something wierd is being displayed after a while.
            throw new RuntimeException(glOperation + ": glError " + error);
        }
    }

    public float getMovementY() {
        return mMovementY;
    }

    public void setMovementY(float movementY) {
        mMovementY = movementY;
    }

    public float[] getProjectionMatrix() {
        return mProjectionMatrix;
    }

    public float[] getGridModelMatrix() {
        return mGridModelMatrix;
    }

    public float[] getFixedModelMatrix() {
        return mFixedModelMatrix;
    }

    public String getAnswerText() {
        return mAnswerText;
    }

    public void setAnswerText(String guessInput) {
        if (guessInput == "") {
            this.mAnswerText = getInputUnderscores();
            return;
        }

        this.mAnswerText = this.mAnswerText.replaceFirst("_", guessInput);
    }

    public void resetAnswerText() {
        this.mAnswerText = CurrentAnswer.getLabel();
    }

    private String getInputUnderscores() {
        if (Equation.getExpectedAnswer() <= 0) {
            return "";
        }

        return Equation.getExpectedAnswerLabel().replaceAll("[0-9]", "_");
    }

    public ArrayList<Shape> getInputShapes() {
        return inputShapes;
    }
package com.laytonlabs.android.levelup.shapes;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;

import android.opengl.GLES20;

import com.laytonlabs.android.levelup.MyGLRenderer;
import com.laytonlabs.android.levelup.Vec2;
import com.laytonlabs.android.levelup.game.Cell;

/**
 * A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
 */
public abstract class Shape {

    private final String TAG = "Shape";

    private final String vertexShaderCode =
            // This matrix member variable provides a hook to manipulate
            // the coordinates of the objects that use this vertex shader
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            // The matrix must be included as a modifier of gl_Position.
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            "  gl_Position = uMVPMatrix * vPosition;" +
            "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "  gl_FragColor = vColor;" +
            "}";

    private final FloatBuffer vertexBuffer;
    private final ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mColorHandle;
    private int mMVPMatrixHandle;
    protected float[] shapeCoords;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;

    //These will be initiated by the abstract class
    private final float[] ORIGINAL_COORDS;
    private final short[] DRAW_ORDER; // order to draw vertices

    //#RGB: white (255, 255, 255)
    private final float[] COLOR;

    //Sets the scale of the shape and where the X centre is.
    private final float SCALE;
    private final float CENTRE_X;
    private final float CENTRE_Y;

    public abstract float getCentreX();
    public abstract float getCentreY();

    public float[] getNestedTextColor() {
        return null;
    }

    public void setNestedTextColor(float[] textColor) {}

    public Cell getCell() {
        return null;
    }

    public ArrayList<Shape> getShapes() {
        return null;
    }

    public void setShapes(float scale, String nestedText) {}

    public void setShapes(float scale, String nestedText, float[] textColor) {}

    public boolean intersects(Vec2 touchCoords) {
        return false;
    }

    public float getMinX() {return getMin(getArraySubset(0));}
    public float getMaxX() {return getMax(getArraySubset(0));}
    public float getMinY() {return getMin(getArraySubset(1));}
    public float getMaxY() {return getMax(getArraySubset(1));}

    private float getMin(float[] values) {
        float minVal = 1000f;
        for (float value : values) {
            if (value < minVal) {
                minVal = value;
            }
        }
        return minVal;
    }

    private float getMax(float[] values) {
        float maxVal = -1000f;
        for (float value : values) {
            if (value > maxVal) {
                maxVal = value;
            }
        }
        return maxVal;
    }

    private float[] getArraySubset(int offset) {
        if (shapeCoords == null || shapeCoords.length == 0) {
            return null;
        }

        float[] subsetArray = new float[shapeCoords.length / COORDS_PER_VERTEX];
        int subsetIndex = 0;

        for (int i = offset; i < shapeCoords.length; i=(i+COORDS_PER_VERTEX)) {
            subsetArray[subsetIndex] = shapeCoords[i];
            subsetIndex++;
        }
        return subsetArray;
    }

    /**
     * Sets up the drawing object data for use in an OpenGL ES context.
     */
    public Shape(float[] originalCoords, short[] drawOrder, float[] color, 
            float scale, float centreX, float centreY) {

        this.ORIGINAL_COORDS = originalCoords;
        this.DRAW_ORDER = drawOrder;
        this.COLOR = color;
        this.SCALE = scale;
        this.CENTRE_X = centreX;
        this.CENTRE_Y = centreY;

        this.shapeCoords = ORIGINAL_COORDS.clone();

        adjustShape(scale, centreX, centreY);
        //Resize based on the scale
        //adjustSize(scale);

        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                shapeCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(shapeCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
                // (# of coordinate values * 2 bytes per short)
                DRAW_ORDER.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(DRAW_ORDER);
        drawListBuffer.position(0);

        // prepare shaders and OpenGL program
        int vertexShader = MyGLRenderer.loadShader(
                GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = MyGLRenderer.loadShader(
                GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
    }

    //Adjust the original scale of the shape and position
    private void adjustShape(float scale, float centreX, float centreY) {
        for (int i = 0; i < shapeCoords.length; i++) {
            //Apply the scale
            shapeCoords[i] = (ORIGINAL_COORDS[i] * scale);

            //Apply the x offset
            shapeCoords[i] += (i % 3 == 0 ? centreX : 0);

            //Apply the y offset
            shapeCoords[i] += (i % 3 == 1 ? centreY : 0);
        }
    }

    /**
     * Encapsulates the OpenGL ES instructions for drawing this shape.
     *
     * @param mvpMatrix - The Model View Project matrix in which to draw
     * this shape.
     */
    public void draw(float[] mvpMatrix) {
        // Add program to OpenGL environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, COLOR, 0);

        // get handle to shape's transformation matrix
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        MyGLRenderer.checkGlError("glGetUniformLocation");

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
        MyGLRenderer.checkGlError("glUniformMatrix4fv");

        // Draw the square
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, DRAW_ORDER.length,
                GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}
下面是错误堆栈中引用的java类的完整代码

MyGLRenderer.java

mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
package com.laytonlabs.android.levelup;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.laytonlabs.android.levelup.game.Cell;
import com.laytonlabs.android.levelup.game.CurrentAnswer;
import com.laytonlabs.android.levelup.game.Equation;
import com.laytonlabs.android.levelup.game.Game;
import com.laytonlabs.android.levelup.game.Level;
import com.laytonlabs.android.levelup.game.Score;
import com.laytonlabs.android.levelup.game.Stage;
import com.laytonlabs.android.levelup.game.Time;
import com.laytonlabs.android.levelup.shapes.Color;
import com.laytonlabs.android.levelup.shapes.EquationRectangle;
import com.laytonlabs.android.levelup.shapes.Hexagon;
import com.laytonlabs.android.levelup.shapes.InputSquare;
import com.laytonlabs.android.levelup.shapes.Shape;
import com.laytonlabs.android.levelup.shapes.StatsRectangle;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;

public class MyGLRenderer implements GLSurfaceView.Renderer {

    private static final String TAG = "MyGLRenderer";
    private static StatsRectangle levelRectangle;
    private static StatsRectangle timeRectangle;
    private static StatsRectangle scoreRectangle;
    private static EquationRectangle equationRectangle;
    private static EquationRectangle answerRectangle;
    private ArrayList<Shape> gridShapes;
    private ArrayList<Shape> bottomRowShapes;
    private static ArrayList<Shape> inputShapes;

    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private final float[] mMVPMatrix = new float[16];
    private final float[] mMVPFixed = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    private final float[] mGridModelMatrix = new float[16];
    private float[] mFixedModelMatrix = new float[16];
    private float[] mTempMatrix = new float[16];

    private float mMovementY;
    private float mBottomRowScale; 
    private static String mAnswerText = CurrentAnswer.getLabel();
    private boolean isCorrectGuess = false;
    private boolean renderCorrectGuess = false;
    private boolean isWrongGuess = false;
    private boolean renderOutput = false;

    //To limit the number of renders per second
    private long startTime;
    private long endTime;
    private long timeElapsed;
    private int currentFrame = 0;              // active frame
    private int outputCurrentFrame = 0;              // active frame
    private final int FPS = 33;                // Frames per second
    private final int FPS_ANIMATION_10 = 10;
    private final int FPS_ANIMATION_20 = 20;

    //Reducer values, these are used for animation scenes
    private float mFPSMovementY;
    private float mFPSBottomRowScale;

    private int gridLevel = 0; //The cell layout level in the grid.
    private int rowLevel = 1; //The current row in the grid user is selected.

    //Constants for grid presentation 
    private final float CELL_SCALE = 0.3f;
    private final float CELL_OFFSET_Y = 0.7f;

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {

        // Set the background frame color
        GLES20.glClearColor(Color.DARK_GREY[0], Color.DARK_GREY[1], Color.DARK_GREY[2], Color.DARK_GREY[3]);

        startTime = System.currentTimeMillis();

        //TODO Add code that sets current answer to whatever the current answer is.

        //Initialise fixed shapes
        equationRectangle = new EquationRectangle(-0.35f);
        answerRectangle = new EquationRectangle(-0.5f);

        equationRectangle.setShapes(0.2f, Equation.get());
        answerRectangle.setShapes(0.3f, mAnswerText);

        //TODO - Change the below calculations to be align_left, align_centre, align_right, etc.
        levelRectangle = new StatsRectangle(0 - (Screen.DEFAULT_WIDTH/3), Color.TURQUOISE, Color.TURQUOISE);
        timeRectangle = new StatsRectangle(0, Color.PURPLE, Color.PURPLE);
        scoreRectangle = new StatsRectangle(0 + (Screen.DEFAULT_WIDTH/3), Color.TURQUOISE, Color.TURQUOISE);

        levelRectangle.setShapes(-1f, Level.getLabel());
        scoreRectangle.setShapes(-1f, Score.getScoreLabel());
        timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());

        setGridShapes();
        //Complete the bottom row for inputting guesses
        bottomRowShapes = new ArrayList<Shape>();
        setBottomRowShapes();
        buildInputGrid();
        setBottomRowScale(1.0f);
        setFPSBottomRowScale(getBottomRowScale() / FPS_ANIMATION_20);
        setFPSMovementY((CELL_OFFSET_Y*CELL_SCALE) / FPS_ANIMATION_20);
    }

    private void buildInputGrid() {
        inputShapes = new ArrayList<Shape>();

        inputShapes.add(new InputSquare(0.16f, -3.0f, 1.15f, "1")); //1
        inputShapes.add(new InputSquare(0.16f, -1.8f, 1.15f, "2")); //2
        inputShapes.add(new InputSquare(0.16f, -0.6f, 1.15f, "3")); //3
        inputShapes.add(new InputSquare(0.16f,  0.6f, 1.15f, "4")); //4
        inputShapes.add(new InputSquare(0.16f,  1.8f, 1.15f, "5")); //5
        inputShapes.add(new InputSquare(0.16f,  3.0f, 1.15f, "6")); //6
        inputShapes.add(new InputSquare(0.16f, -2.4f,     0, "7")); //7
        inputShapes.add(new InputSquare(0.16f, -1.2f,     0, "8")); //8
        inputShapes.add(new InputSquare(0.16f,     0,     0, "9")); //9
        inputShapes.add(new InputSquare(0.16f,  1.2f,     0, "0")); //0
        inputShapes.add(new InputSquare(0.16f,  2.4f,     0, "x")); //X - This is to clear input
    }

    @Override
    public void onDrawFrame(GL10 unused) {

        //We dont need continuous rendering, only needed for animation and time switching
        endTime = System.currentTimeMillis();
        timeElapsed = endTime - startTime;
        if (timeElapsed < FPS) {
            try {
                Log.d(TAG, "Sleeping until "+FPS+" millsecs pass");
                Thread.sleep(FPS - timeElapsed);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        startTime = endTime;

        //Update the timers by deducting timeRemaining
        Time.update();

        Matrix.setIdentityM(mGridModelMatrix, 0); // initialize to identity matrix
        //Setup the equation display before we start moving the grid around
        Matrix.setIdentityM(mFixedModelMatrix, 0); // initialize to identity matrix

        // Draw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // Enable transparency options for colors.
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        // Set the camera position (View matrix)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

        // Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        //Clone this for use with fixed and grid MVPs
        mTempMatrix = mMVPMatrix.clone();

        // Create a rotation for the triangle

        // Use the following code to generate constant rotation.
        // Leave this code out when using TouchEvents.
        // long time = SystemClock.uptimeMillis() % 4000L;
        // float angle = 0.090f * ((int) time);

        drawGridShapes();

        drawFixedShapes();
    }

    private void drawGridShapes() {     
        //Start the grid drawing at bottom of screen.
        Matrix.translateM(mGridModelMatrix, 0, 0, -0.1f, 0);

        //Move the grid down or up the screen depending on touch events.
        Matrix.translateM(mGridModelMatrix, 0, 0, mMovementY, 0);

        // Combine the rotation matrix with the projection and camera view
        // Note that the mMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        //Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

        //Add the movement to the matrix
        Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mGridModelMatrix, 0);

        if (isCorrectGuess()) {
            renderCorrectGuess = true; //This is to change the answer text to green inside drawFixedShapes
            currentFrame++;                    // step to next frame
            setMovementY(getMovementY() - getFPSMovementY());
            setBottomRowScale(getBottomRowScale() - getFPSBottomRowScale());
            if (currentFrame >= FPS_ANIMATION_20) {            // if end of sequence
                currentFrame = 0;               // restart sequence
                setBottomRowScale(1.0f);        // Reset the scale
                removeBottomRow();
                setCorrectGuess(false);         //Mark as false so animation stops and user can make new guess
            }
        }

        //Draw all grid shapes
        if (isCorrectGuess()) {
            drawAllShapesAndShrinkBottomRow(mMVPMatrix);
        } else {
            drawAllShapes(getGridShapes(), mMVPMatrix);
        }
    }

    private void drawAllShapes(ArrayList<Shape> shapes, float[] mMVPMatrix) {
        for (Shape shape : shapes) {
            Log.d(TAG, "Scale Hexagon ("+shape.toString()+") Aft ("+shape.getCentreX()+", "+shape.getCentreY()+")");
            drawShapes(shape, mMVPMatrix);
        }
    }

    private void drawAllShapesAndShrinkBottomRow(float[] mMVPMatrix) {
        float[] mMVPScaled = mMVPMatrix.clone();

        Matrix.scaleM(mMVPScaled, 0, getBottomRowScale(), getBottomRowScale(), 0);

        int lastCellIndex = getBottomRowLastCellIndex();

        //Apply scaling to the bottom row and just move the other rows.
        for (int i = 0; i < getGridShapes().size(); i++) {
            if (i <= lastCellIndex) {
                drawShapes(getGridShapes().get(i), mMVPScaled);
            } else {
                drawShapes(getGridShapes().get(i), mMVPMatrix);
            }
        }
    }

    private void drawFixedShapes() {

        Matrix.multiplyMM(mMVPFixed, 0, mTempMatrix, 0, mFixedModelMatrix, 0);

        if (isRenderOutput()) {
            //Show the equation using the values from the selected cell.
            equationRectangle.setShapes(0.2f, Equation.get());
            answerRectangle.setShapes(0.3f, mAnswerText);

            setRenderOutput(false);
        }

        //Update the time if time has changed
        if (!timeRectangle.toString().equals(Time.getTimeRemainingLabel())) {
            //If the time remaining is almost up then change text to red.
            if (Time.isTimeAlmostUp()) {
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel(), Color.RED);
            } else {
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
            }
        }

        //Animation for changing color of the text
        if (isWrongGuess()) {
            if (outputCurrentFrame == 0) {
                answerRectangle.setShapes(0.3f, mAnswerText, Color.RED);
            }
            outputCurrentFrame++;
            if (outputCurrentFrame >= FPS_ANIMATION_10) {
                outputCurrentFrame = 0;
                setAnswerText("");
                answerRectangle.setShapes(0.3f, mAnswerText);
                setWrongGuess(false);           
            }
        } else if (renderCorrectGuess) {
            if (outputCurrentFrame == 0) {
                answerRectangle.setShapes(0.3f, mAnswerText, Color.GREEN);
                levelRectangle.setShapes(-1f, Level.getLabel(), Color.YELLOW);
                scoreRectangle.setShapes(-1f, Score.getScoreLabel(), Color.YELLOW);
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel(), Color.YELLOW);
            }
            outputCurrentFrame++;
            if (outputCurrentFrame >= FPS_ANIMATION_20) {
                outputCurrentFrame = 0;
                answerRectangle.setShapes(0.3f, mAnswerText);
                levelRectangle.setShapes(-1f, Level.getLabel());
                scoreRectangle.setShapes(-1f, Score.getScoreLabel());
                timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
                setGridShapes();
                renderCorrectGuess = false;     
            }
        }

        drawShapes(answerRectangle, mMVPFixed);
        drawShapes(equationRectangle, mMVPFixed);
        drawShapes(levelRectangle, mMVPFixed);
        drawShapes(timeRectangle, mMVPFixed);
        drawShapes(scoreRectangle, mMVPFixed);

        //Draw all input grid shapess
        drawAllShapes(inputShapes, mMVPFixed);
    }

    public static void printStack() {
        Log.e(TAG,"Level: " + Level.getLabel());
        Log.e(TAG,"Score: " + Score.getScoreLabel());
        Log.e(TAG,"Time: " + Time.getTimeRemainingLabel());
        Log.e(TAG,"mAnswerText: " + mAnswerText);
        Log.e(TAG,"Equation: " + Equation.get());
        for (int i = 0; i < inputShapes.size(); i++) {
            Log.e(TAG,"inputShapes[" + i + "]: " + inputShapes.get(i).toString());
        }
    }

    private void drawShapes(Shape parentShape, float[] mMVPMatrix) {
        parentShape.draw(mMVPMatrix);

        if (parentShape.getShapes() == null) {
            return;
        }

        for (Shape nestedShapes : parentShape.getShapes()) {
            nestedShapes.draw(mMVPMatrix);
        }
    }

    private ArrayList<Shape> getGridShapes() {
        return gridShapes;
    }

    private int getBottomRowLastCellIndex() {
        int lastCellIndex = 0;
        Shape prevShape = null;
        for (int i = 0; i < getGridShapes().size(); i++) {
            if (prevShape == null || getGridShapes().get(i).getCentreY() == prevShape.getCentreY()) {
                lastCellIndex = i;
                prevShape = getGridShapes().get(i);
            } else {
                return lastCellIndex;
            }
        }
        return lastCellIndex;
    }

    private void removeBottomRow() {
        for (int i = getBottomRowLastCellIndex(); i >= 0 ; i--) {
            getGridShapes().remove(i);
        }
        //Reset the bottom row shapes
        setBottomRowShapes();
    }

    public ArrayList<Shape> getBottomRowShapes() {
        return bottomRowShapes;
    }

    private void setBottomRowShapes() {
        ArrayList<Shape> tempRowShapes = new ArrayList<Shape>();

        //Apply scaling to the bottom row and just move the other rows.
        for (int i = 0; i <= getBottomRowLastCellIndex(); i++) {
            tempRowShapes.add(getGridShapes().get(i));
        }
        bottomRowShapes = tempRowShapes;
    }

    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        // Adjust the viewport based on geometry changes,
        // such as screen rotation
        GLES20.glViewport(0, 0, width, height);

        //Log.d("MyGLRenderer", "Width: " + width + " Height: " + height);

        float ratio = (float) width / height;

        Log.d("Screen","Width: "+ width +" - Height: "+ height +" - Ratio: "+ ratio);

        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
        if (ratio > 1) {
            ratio = Screen.DEFAULT_LANDSCAPE_RATIO;
        } else {
            ratio = Screen.DEFAULT_PORTRAIT_RATIO;
        }

        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

        //TODO Store the current answer, the current answer Text and the current Equation. 
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

    public static void checkGlError(String glOperation) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e(TAG, glOperation + ": 1glError " + error);
            printStack();
            //TODO - Print out the fixed shapes values to see if something wierd is being displayed after a while.
            throw new RuntimeException(glOperation + ": glError " + error);
        }
    }

    public float getMovementY() {
        return mMovementY;
    }

    public void setMovementY(float movementY) {
        mMovementY = movementY;
    }

    public float[] getProjectionMatrix() {
        return mProjectionMatrix;
    }

    public float[] getGridModelMatrix() {
        return mGridModelMatrix;
    }

    public float[] getFixedModelMatrix() {
        return mFixedModelMatrix;
    }

    public String getAnswerText() {
        return mAnswerText;
    }

    public void setAnswerText(String guessInput) {
        if (guessInput == "") {
            this.mAnswerText = getInputUnderscores();
            return;
        }

        this.mAnswerText = this.mAnswerText.replaceFirst("_", guessInput);
    }

    public void resetAnswerText() {
        this.mAnswerText = CurrentAnswer.getLabel();
    }

    private String getInputUnderscores() {
        if (Equation.getExpectedAnswer() <= 0) {
            return "";
        }

        return Equation.getExpectedAnswerLabel().replaceAll("[0-9]", "_");
    }

    public ArrayList<Shape> getInputShapes() {
        return inputShapes;
    }
package com.laytonlabs.android.levelup.shapes;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;

import android.opengl.GLES20;

import com.laytonlabs.android.levelup.MyGLRenderer;
import com.laytonlabs.android.levelup.Vec2;
import com.laytonlabs.android.levelup.game.Cell;

/**
 * A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
 */
public abstract class Shape {

    private final String TAG = "Shape";

    private final String vertexShaderCode =
            // This matrix member variable provides a hook to manipulate
            // the coordinates of the objects that use this vertex shader
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            // The matrix must be included as a modifier of gl_Position.
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            "  gl_Position = uMVPMatrix * vPosition;" +
            "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "  gl_FragColor = vColor;" +
            "}";

    private final FloatBuffer vertexBuffer;
    private final ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mColorHandle;
    private int mMVPMatrixHandle;
    protected float[] shapeCoords;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;

    //These will be initiated by the abstract class
    private final float[] ORIGINAL_COORDS;
    private final short[] DRAW_ORDER; // order to draw vertices

    //#RGB: white (255, 255, 255)
    private final float[] COLOR;

    //Sets the scale of the shape and where the X centre is.
    private final float SCALE;
    private final float CENTRE_X;
    private final float CENTRE_Y;

    public abstract float getCentreX();
    public abstract float getCentreY();

    public float[] getNestedTextColor() {
        return null;
    }

    public void setNestedTextColor(float[] textColor) {}

    public Cell getCell() {
        return null;
    }

    public ArrayList<Shape> getShapes() {
        return null;
    }

    public void setShapes(float scale, String nestedText) {}

    public void setShapes(float scale, String nestedText, float[] textColor) {}

    public boolean intersects(Vec2 touchCoords) {
        return false;
    }

    public float getMinX() {return getMin(getArraySubset(0));}
    public float getMaxX() {return getMax(getArraySubset(0));}
    public float getMinY() {return getMin(getArraySubset(1));}
    public float getMaxY() {return getMax(getArraySubset(1));}

    private float getMin(float[] values) {
        float minVal = 1000f;
        for (float value : values) {
            if (value < minVal) {
                minVal = value;
            }
        }
        return minVal;
    }

    private float getMax(float[] values) {
        float maxVal = -1000f;
        for (float value : values) {
            if (value > maxVal) {
                maxVal = value;
            }
        }
        return maxVal;
    }

    private float[] getArraySubset(int offset) {
        if (shapeCoords == null || shapeCoords.length == 0) {
            return null;
        }

        float[] subsetArray = new float[shapeCoords.length / COORDS_PER_VERTEX];
        int subsetIndex = 0;

        for (int i = offset; i < shapeCoords.length; i=(i+COORDS_PER_VERTEX)) {
            subsetArray[subsetIndex] = shapeCoords[i];
            subsetIndex++;
        }
        return subsetArray;
    }

    /**
     * Sets up the drawing object data for use in an OpenGL ES context.
     */
    public Shape(float[] originalCoords, short[] drawOrder, float[] color, 
            float scale, float centreX, float centreY) {

        this.ORIGINAL_COORDS = originalCoords;
        this.DRAW_ORDER = drawOrder;
        this.COLOR = color;
        this.SCALE = scale;
        this.CENTRE_X = centreX;
        this.CENTRE_Y = centreY;

        this.shapeCoords = ORIGINAL_COORDS.clone();

        adjustShape(scale, centreX, centreY);
        //Resize based on the scale
        //adjustSize(scale);

        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                shapeCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(shapeCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
                // (# of coordinate values * 2 bytes per short)
                DRAW_ORDER.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(DRAW_ORDER);
        drawListBuffer.position(0);

        // prepare shaders and OpenGL program
        int vertexShader = MyGLRenderer.loadShader(
                GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = MyGLRenderer.loadShader(
                GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
    }

    //Adjust the original scale of the shape and position
    private void adjustShape(float scale, float centreX, float centreY) {
        for (int i = 0; i < shapeCoords.length; i++) {
            //Apply the scale
            shapeCoords[i] = (ORIGINAL_COORDS[i] * scale);

            //Apply the x offset
            shapeCoords[i] += (i % 3 == 0 ? centreX : 0);

            //Apply the y offset
            shapeCoords[i] += (i % 3 == 1 ? centreY : 0);
        }
    }

    /**
     * Encapsulates the OpenGL ES instructions for drawing this shape.
     *
     * @param mvpMatrix - The Model View Project matrix in which to draw
     * this shape.
     */
    public void draw(float[] mvpMatrix) {
        // Add program to OpenGL environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, COLOR, 0);

        // get handle to shape's transformation matrix
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        MyGLRenderer.checkGlError("glGetUniformLocation");

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
        MyGLRenderer.checkGlError("glUniformMatrix4fv");

        // Draw the square
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, DRAW_ORDER.length,
                GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}
package com.laytonlabs.android.levelup;
导入java.util.ArrayList;
导入java.util.array;
导入java.util.regex.Matcher;
导入java.util.regex.Pattern;
导入javax.microedition.khronos.egl.EGLConfig;
导入javax.microedition.khronos.opengles.GL10;
导入com.laytonlabs.android.levelup.game.Cell;
导入com.laytonlabs.android.levelup.game.CurrentAnswer;
导入com.laytonlabs.android.levelup.game.Equation;
导入com.laytonlabs.android.levelup.game.game;
导入com.laytonlabs.android.levelup.game.Level;
导入com.laytonlabs.android.levelup.game.Score;
导入com.laytonlabs.android.levelup.game.Stage;
导入com.laytonlabs.android.levelup.game.Time;
导入com.laytonlabs.android.levelup.shapes.Color;
导入com.laytonlabs.android.levelup.shapes.EquationRectangle;
导入com.laytonlabs.android.levelup.shapes.Hexagon;
导入com.laytonlabs.android.levelup.shapes.InputSquare;
导入com.laytonlabs.android.levelup.shapes.Shape;
导入com.laytonlabs.android.levelup.shapes.StatsRectangle;
导入android.opengl.GLES20;
导入android.opengl.GLSurfaceView;
导入android.opengl.Matrix;
导入android.util.Log;
公共类MyGLRenderer实现GLSurfaceView.Renderer{
私有静态最终字符串标记=“MyGLRenderer”;
私有静态StatsRectangle-levelRectangle;
私有静态StatsRectangle时间矩形;
私有静态StatsRectangle;
私有静态方程矩形方程矩形;
私有静态方程矩形应答器矩形;
私有数组列表网格形状;
私有ArrayList底部行形状;
私有静态ArrayList inputShapes;
//mMVPMatrix是“模型视图投影矩阵”的缩写
私有最终浮动[]mMVPMatrix=新浮动[16];
私有最终浮动[]mMVPFixed=新浮动[16];
私有最终浮动[]mProjectionMatrix=新浮动[16];
私有最终浮动[]mViewMatrix=新浮动[16];
私有最终浮动[]mGridModelMatrix=新浮动[16];
私有浮点[]mFixedModelMatrix=新浮点[16];
私有浮点[]mTempMatrix=新浮点[16];
私人运动;
私人浮点数;
私有静态字符串mAnswerText=CurrentAnswer.getLabel();
私有布尔值isCorrectGuess=false;
私有布尔renderCorrectGuess=false;
私有布尔值IsErrorGuess=false;
私有布尔renderOutput=false;
//限制每秒渲染的数量的步骤
私人长启动时间;
私人长时间;
私密时间长;
private int currentFrame=0;//活动帧
private int outputCurrentFrame=0;//活动帧
private final int FPS=33;//每秒帧数
私人最终整数FPS_动画_10=10;
私人最终整数FPS_动画_20=20;
//减速器值,这些值用于动画场景
私人浮动mFPSMovementY;
私人浮动mFPSBottomRowScale;
private int gridLevel=0;//网格中的单元格布局级别。
private int rowLevel=1;//已选择网格用户中的当前行。
//网格表示的常量
专用最终浮子电池\u刻度=0.3f;
专用最终浮子单元偏移量Y=0.7f;
@凌驾
已创建Surface上的公共void(GL10未使用,EGLConfig配置){
//设置背景框颜色
GLES20.glClearColor(颜色.深灰色[0],颜色.深灰色[1],颜色.深灰色[2],颜色.深灰色[3]);
startTime=System.currentTimeMillis();
//TODO添加将当前答案设置为当前答案的代码。
//初始化固定形状
equationRectangle=新的equationRectangle(-0.35f);
answerRectangle=新方程矩形(-0.5f);
equationRectangle.setShapes(0.2f,Equation.get());
应答矩形。设置形状(0.3f,mAnswerText);
//TODO-将以下计算更改为左对齐、中对齐、右对齐等。
levelRectangle=新的StatsRectangle(0-(屏幕默认宽度/3),Color.TURQUOISE,Color.TURQUOISE);
timeRectangle=新的StatsRectangle(0,Color.PURPLE,Color.PURPLE);
scoreRectangle=新的StatsRectangle(0+(屏幕默认宽度/3),Color.TURQUOISE,Color.TURQUOISE);
levelRectangle.setShapes(-1f,Level.getLabel());
scoreRectangle.setShapes(-1f,Score.getScoreLabel());
timeRectangle.setShapes(-1f,Time.getTimeRemainingLabel());
setGridShapes();
//完成底部的一行以输入猜测
bottomRowShapes=新的ArrayList();
setBottomRowShapes();
buildInputGrid();
立根生长量表(1.0f);
setFPSBottomRowScale(getBottomRowScale()/FPS_ANIMATION_20);
setFPSMovementY((单元偏移量*单元比例)/FPS\u动画\u 20);
}
私有void buildInputGrid(){
inputShapes=新的ArrayList();
添加(新的InputSquare(0.16f,-3.0f,1.15f,“1”);//1
添加(新的InputSquare(0.16f,-1.8f,1.15f,“2”);//2
添加(新的InputSquare(0.16f,-0.6f,1.15f,“3”);//3
添加(新的InputSquare(0.16f,0.6f,1.15f,“4”);//4
添加(新的InputSquare(0.16f,1.8f,1.15f,“5”);//5
添加(新的InputSquare(0.16f,3.0f,1.15f,“6”);//6
添加(新的InputSquare(0.16f,-2.4f,0,“7”);//7
添加(新的InputSquare(0.16f,-1