/*
* Copyright (C) 2016 eschao <esc.chao@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.eschao.android.widget.pageflip;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import static android.opengl.GLES10.GL_SRC_ALPHA;
import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
import static android.opengl.GLES20.glBlendFunc;
import static android.opengl.GLES20.glDisable;
import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glUniform1f;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glVertexAttribPointer;
/**
* Shadow vertex which is used to store vertex data of fold shadow and draw
* shadow with openGL
* <p>Every vertex has 4 float data which are:</p>
* <ul>
* <li>x coordinate</li>
* <li>y coordinate</li>
* <li>color (start color or end color)</li>
* <li>alpha (start alpha or end alpha)</li>
* </ul>
*
* @author eschao
*/
class ShadowVertexes {
// how many vertexes in vertex float buffer will be drawn on screen
int mVertexesSize;
// universal Z coordinate for all shadow vertex
// we will enable DEPTH_TEST while drawing fold shadow to avoid some drawing
// issue
float vertexZ;
// float array and float buffer for storing vertexes
float[] mVertexes;
FloatBuffer mVertexesBuffer;
// shadow color
ShadowColor mColor;
// the start position of backward vertexes
int mMaxBackward;
// reserve space between backward and forward index
// need to preserver space for fold top edge shadow when compute fold edge
// shadow since the top edge shadow will be computed at last
//
// +--------------------+------------+--------------------+
// | <-- mBackward | reserved | mForward --> |
// +--------------------+------------+--------------------+
private int mSpaceOfFrontRear;
// forward and backward index for adding vertex
private int mBackward;
private int mForward;
/**
* Default constructor
*/
public ShadowVertexes() {
release();
mColor = new ShadowColor();
}
/**
* Constructor
*
* @param spaceOfFrontRear reserve space for special usage
* @param startColor shadow start color, range is [0 .. 1]
* @param startAlpha shadow alpha, range is [0 .. 1]
* @param endColor shadow end color, range is [0 .. 1]
* @param endAlpha shadow end alpah, range is [0 .. 1]
*/
public ShadowVertexes(int spaceOfFrontRear,
float startColor, float startAlpha,
float endColor, float endAlpha) {
release();
mSpaceOfFrontRear = spaceOfFrontRear;
mColor = new ShadowColor(startColor, startAlpha, endColor, endAlpha);
}
/**
* Set with vertex count
*
* @param meshCount mesh count
* @return self
*/
public ShadowVertexes set(int meshCount) {
// every mesh need two vertexes:
// (startX, startY , startColor, startAlpha) and
// (endX, endY, endColor, endAlpha), that is why it is meshCount * 8
mMaxBackward = meshCount << 3;
// double meshCount since fold shadow has two sides, for example:
// fold edge shadow has left and right edge along the fold triangle
int size = (meshCount << 4) + (mSpaceOfFrontRear << 2);
mVertexes = new float[size];
mVertexesBuffer = ByteBuffer.allocateDirect(size << 2)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
reset();
return this;
}
/**
* Release all resources
*/
public void release() {
mBackward = 0;
mForward = 0;
mMaxBackward = 0;
mSpaceOfFrontRear = 0;
mVertexes = null;
mVertexesBuffer = null;
}
/**
* Reset index of float array before adding vertex to buffer
* <p>There are two index: forward and backward, all of them have to be
* reset to middle position(exclude reserved space) before adding vertexes
* </p>
*/
public void reset() {
vertexZ = 0;
mBackward = mMaxBackward;
mForward = mMaxBackward + (mSpaceOfFrontRear << 2);
}
/**
* Set vertex in given offset
*
* @param offset where to start saving vertex
* @param startX start x coordinate
* @param startY start y coordinate
* @param endX end x coordinate
* @param endY end y coordinate
* @return self
*/
public ShadowVertexes setVertexes(int offset,
float startX, float startY,
float endX, float endY) {
mVertexes[offset++] = startX;
mVertexes[offset++] = startY;
mVertexes[offset++] = mColor.startColor;
mVertexes[offset++] = mColor.startAlpha;
mVertexes[offset++] = endX;
mVertexes[offset++] = endY;
mVertexes[offset++] = mColor.endColor;
mVertexes[offset] = mColor.endAlpha;
return this;
}
/**
* Backward add vertex to float buffer
* <p></p>Call {@link #reset()} before start calling any add operations</p>
*
* @param startX start x coordinate
* @param startY start y coordinate
* @param endX end x coordinate
* @param endY end y coordinate
* @return self
*/
public ShadowVertexes addVertexesBackward(float startX, float startY,
float endX, float endY) {
mVertexes[--mBackward] = mColor.endAlpha;
mVertexes[--mBackward] = mColor.endColor;
mVertexes[--mBackward] = endY;
mVertexes[--mBackward] = endX;
mVertexes[--mBackward] = mColor.startAlpha;
mVertexes[--mBackward] = mColor.startColor;
mVertexes[--mBackward] = startY;
mVertexes[--mBackward] = startX;
return this;
}
/**
* Forward add vertex to float buffer
* <p></p>Call {@link #reset()} before start calling any add operations</p>
*
* @param startX start x coordinate
* @param startY start y coordinate
* @param endX end x coordinate
* @param endY end y coordinate
* @return self
*/
public ShadowVertexes addVertexesForward(float startX, float startY,
float endX, float endY) {
mVertexes[mForward++] = startX;
mVertexes[mForward++] = startY;
mVertexes[mForward++] = mColor.startColor;
mVertexes[mForward++] = mColor.startAlpha;
mVertexes[mForward++] = endX;
mVertexes[mForward++] = endY;
mVertexes[mForward++] = mColor.endColor;
mVertexes[mForward++] = mColor.endAlpha;
return this;
}
/**
* Add vertex to float buffer
* Call {@link #reset()} before calling any add operations
*
* @param isForward is backward or forward adding
* @param startX start x coordinate
* @param startY start y coordinate
* @param endX end x coordinate
* @param endY end y coordinate
* @return self
*/
public ShadowVertexes addVertexes(boolean isForward,
float startX, float startY,
float endX, float endY) {
return isForward ?
addVertexesForward(startX, startY, endX, endY) :
addVertexesBackward(startX, startY, endX, endY);
}
/**
* Put data from float array to float buffer
*/
public void toFloatBuffer() {
mVertexesSize = (mForward - mBackward) / 4;
mVertexesBuffer.put(mVertexes, mBackward, mForward - mBackward)
.position(0);
}
/**
* put given length data from float array to float buffer
*
* @param length data length
*/
public void toFloatBuffer(int length) {
mVertexesBuffer.put(mVertexes, 0, length).position(0);
mVertexesSize = length / 4;
}
/**
* Draw shadow
*
* @param program shadow vertex shader program
*/
public void draw(ShadowVertexProgram program) {
if (mVertexesSize > 0) {
glUniformMatrix4fv(program.mMVPMatrixLoc, 1, false,
VertexProgram.MVPMatrix, 0);
glUniform1f(program.mVertexZLoc, vertexZ);
// disable texture, and enable blend
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// draw shadow
glVertexAttribPointer(program.mVertexPosLoc, 4, GL_FLOAT, false, 0,
mVertexesBuffer);
glEnableVertexAttribArray(program.mVertexPosLoc);
glDrawArrays(GL_TRIANGLE_STRIP, 0, mVertexesSize);
glDisable(GL_BLEND);
}
}
}