diff --git a/src/fi/harism/curl/CurlActivity.java b/src/fi/harism/curl/CurlActivity.java index d9e3b02..1261585 100644 --- a/src/fi/harism/curl/CurlActivity.java +++ b/src/fi/harism/curl/CurlActivity.java @@ -1,5 +1,5 @@ /* - Copyright 2012 Harri Smatt + Copyright 2013 Harri Smatt Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ public void onCreate(Bundle savedInstanceState) { mCurlView.setPageProvider(new PageProvider()); mCurlView.setSizeChangedObserver(new SizeChangedObserver()); mCurlView.setCurrentIndex(index); - mCurlView.setBackgroundColor(0xFF202830); + mCurlView.setBackgroundColor(0xFFC0C0C0); // This is something somewhat experimental. Before uncommenting next // line, please see method comments in CurlView. @@ -131,14 +131,14 @@ public void updatePage(CurlPage page, int width, int height, int index) { // First case is image on front side, solid colored back. case 0: { Bitmap front = loadBitmap(width, height, 0); - page.setTexture(front, CurlPage.SIDE_FRONT); + page.setBitmap(front, CurlPage.SIDE_FRONT); page.setColor(Color.rgb(180, 180, 180), CurlPage.SIDE_BACK); break; } // Second case is image on back side, solid colored front. case 1: { Bitmap back = loadBitmap(width, height, 2); - page.setTexture(back, CurlPage.SIDE_BACK); + page.setBitmap(back, CurlPage.SIDE_BACK); page.setColor(Color.rgb(127, 140, 180), CurlPage.SIDE_FRONT); break; } @@ -146,8 +146,8 @@ public void updatePage(CurlPage page, int width, int height, int index) { case 2: { Bitmap front = loadBitmap(width, height, 1); Bitmap back = loadBitmap(width, height, 3); - page.setTexture(front, CurlPage.SIDE_FRONT); - page.setTexture(back, CurlPage.SIDE_BACK); + page.setBitmap(front, CurlPage.SIDE_FRONT); + page.setBitmap(back, CurlPage.SIDE_BACK); break; } // Fourth case is images on both sides - plus they are blend against @@ -155,8 +155,8 @@ public void updatePage(CurlPage page, int width, int height, int index) { case 3: { Bitmap front = loadBitmap(width, height, 2); Bitmap back = loadBitmap(width, height, 1); - page.setTexture(front, CurlPage.SIDE_FRONT); - page.setTexture(back, CurlPage.SIDE_BACK); + page.setBitmap(front, CurlPage.SIDE_FRONT); + page.setBitmap(back, CurlPage.SIDE_BACK); page.setColor(Color.argb(127, 170, 130, 255), CurlPage.SIDE_FRONT); page.setColor(Color.rgb(255, 190, 150), CurlPage.SIDE_BACK); @@ -166,7 +166,7 @@ public void updatePage(CurlPage page, int width, int height, int index) { // scenario only one texture is used and shared for both sides. case 4: Bitmap front = loadBitmap(width, height, 0); - page.setTexture(front, CurlPage.SIDE_BOTH); + page.setBitmap(front, CurlPage.SIDE_BOTH); page.setColor(Color.argb(127, 255, 255, 255), CurlPage.SIDE_BACK); break; diff --git a/src/fi/harism/curl/CurlMesh.java b/src/fi/harism/curl/CurlMesh.java index b2886b2..1e3111e 100644 --- a/src/fi/harism/curl/CurlMesh.java +++ b/src/fi/harism/curl/CurlMesh.java @@ -1,5 +1,5 @@ /* - Copyright 2012 Harri Smatt + Copyright 2013 Harri Smatt Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,12 +20,9 @@ import java.nio.ByteOrder; import java.nio.FloatBuffer; -import javax.microedition.khronos.opengles.GL10; - -import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.PointF; import android.graphics.RectF; +import android.opengl.GLES20; import android.opengl.GLUtils; /** @@ -35,70 +32,44 @@ */ public class CurlMesh { - // Flag for rendering some lines used for developing. Shows - // curl position and one for the direction from the - // position given. Comes handy once playing around with different - // ways for following pointer. - private static final boolean DRAW_CURL_POSITION = false; - // Flag for drawing polygon outlines. Using this flag crashes on emulator - // due to reason unknown to me. Leaving it here anyway as seeing polygon - // outlines gives good insight how original rectangle is divided. - private static final boolean DRAW_POLYGON_OUTLINES = false; - // Flag for enabling shadow rendering. - private static final boolean DRAW_SHADOW = true; - // Flag for texture rendering. While this is likely something you - // don't want to do it's been used for development purposes as texture - // rendering is rather slow on emulator. - private static final boolean DRAW_TEXTURE = true; - - // Colors for shadow. Inner one is the color drawn next to surface where - // shadowed area starts and outer one is color shadow ends to. - private static final float[] SHADOW_INNER_COLOR = { 0f, 0f, 0f, .5f }; - private static final float[] SHADOW_OUTER_COLOR = { 0f, 0f, 0f, .0f }; - // Let's avoid using 'new' as much as possible. Meaning we introduce arrays // once here and reuse them on runtime. Doesn't really have very much effect // but avoids some garbage collections from happening. - private Array mArrDropShadowVertices; private Array mArrIntersections; private Array mArrOutputVertices; private Array mArrRotatedVertices; private Array mArrScanLines; - private Array mArrSelfShadowVertices; - private Array mArrTempShadowVertices; + private Array mArrShadowDropVertices; + private Array mArrShadowSelfVertices; + private Array mArrShadowTempVertices; private Array mArrTempVertices; // Buffers for feeding rasterizer. - private FloatBuffer mBufColors; - private FloatBuffer mBufCurlPositionLines; - private FloatBuffer mBufShadowColors; + private FloatBuffer mBufNormals; + private FloatBuffer mBufShadowPenumbra; private FloatBuffer mBufShadowVertices; private FloatBuffer mBufTexCoords; private FloatBuffer mBufVertices; - private int mCurlPositionLinesCount; - private int mDropShadowCount; + // Counter values. + private int mCountShadowDrop; + private int mCountShadowSelf; + private int mCountVertices; // Boolean for 'flipping' texture sideways. private boolean mFlipTexture = false; // Maximum number of split lines used for creating a curl. private int mMaxCurlSplits; + // Page instance. + private final CurlPage mPage = new CurlPage(); + // Bounding rectangle for this mesh. mRectagle[0] = top-left corner, // mRectangle[1] = bottom-left, mRectangle[2] = top-right and mRectangle[3] // bottom-right. private final Vertex[] mRectangle = new Vertex[4]; - private int mSelfShadowCount; - - private boolean mTextureBack = false; // Texture ids and other variables. private int[] mTextureIds = null; - private final CurlPage mTexturePage = new CurlPage(); - private final RectF mTextureRectBack = new RectF(); - private final RectF mTextureRectFront = new RectF(); - - private int mVerticesCountBack; - private int mVerticesCountFront; /** * Constructor for mesh object. @@ -121,16 +92,14 @@ public CurlMesh(int maxCurlSplits) { mArrTempVertices.add(new Vertex()); } - if (DRAW_SHADOW) { - mArrSelfShadowVertices = new Array( - (mMaxCurlSplits + 2) * 2); - mArrDropShadowVertices = new Array( - (mMaxCurlSplits + 2) * 2); - mArrTempShadowVertices = new Array( - (mMaxCurlSplits + 2) * 2); - for (int i = 0; i < (mMaxCurlSplits + 2) * 2; ++i) { - mArrTempShadowVertices.add(new ShadowVertex()); - } + mArrShadowSelfVertices = new Array( + (mMaxCurlSplits + 2) * 2); + mArrShadowDropVertices = new Array( + (mMaxCurlSplits + 2) * 2); + mArrShadowTempVertices = new Array( + (mMaxCurlSplits + 2) * 2); + for (int i = 0; i < (mMaxCurlSplits + 2) * 2; ++i) { + mArrShadowTempVertices.add(new ShadowVertex()); } // Rectangle consists of 4 vertices. Index 0 = top-left, index 1 = @@ -143,53 +112,32 @@ public CurlMesh(int maxCurlSplits) { mRectangle[0].mPenumbraX = mRectangle[1].mPenumbraX = mRectangle[1].mPenumbraY = mRectangle[3].mPenumbraY = -1; mRectangle[0].mPenumbraY = mRectangle[2].mPenumbraX = mRectangle[2].mPenumbraY = mRectangle[3].mPenumbraX = 1; - if (DRAW_CURL_POSITION) { - mCurlPositionLinesCount = 3; - ByteBuffer hvbb = ByteBuffer - .allocateDirect(mCurlPositionLinesCount * 2 * 2 * 4); - hvbb.order(ByteOrder.nativeOrder()); - mBufCurlPositionLines = hvbb.asFloatBuffer(); - mBufCurlPositionLines.position(0); - } - // There are 4 vertices from bounding rect, max 2 from adding split line // to two corners and curl consists of max mMaxCurlSplits lines each // outputting 2 vertices. int maxVerticesCount = 4 + 2 + (2 * mMaxCurlSplits); - ByteBuffer vbb = ByteBuffer.allocateDirect(maxVerticesCount * 3 * 4); - vbb.order(ByteOrder.nativeOrder()); - mBufVertices = vbb.asFloatBuffer(); + mBufVertices = ByteBuffer.allocateDirect(maxVerticesCount * 3 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); mBufVertices.position(0); - if (DRAW_TEXTURE) { - ByteBuffer tbb = ByteBuffer - .allocateDirect(maxVerticesCount * 2 * 4); - tbb.order(ByteOrder.nativeOrder()); - mBufTexCoords = tbb.asFloatBuffer(); - mBufTexCoords.position(0); - } + mBufTexCoords = ByteBuffer.allocateDirect(maxVerticesCount * 2 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mBufTexCoords.position(0); - ByteBuffer cbb = ByteBuffer.allocateDirect(maxVerticesCount * 4 * 4); - cbb.order(ByteOrder.nativeOrder()); - mBufColors = cbb.asFloatBuffer(); - mBufColors.position(0); - - if (DRAW_SHADOW) { - int maxShadowVerticesCount = (mMaxCurlSplits + 2) * 2 * 2; - ByteBuffer scbb = ByteBuffer - .allocateDirect(maxShadowVerticesCount * 4 * 4); - scbb.order(ByteOrder.nativeOrder()); - mBufShadowColors = scbb.asFloatBuffer(); - mBufShadowColors.position(0); - - ByteBuffer sibb = ByteBuffer - .allocateDirect(maxShadowVerticesCount * 3 * 4); - sibb.order(ByteOrder.nativeOrder()); - mBufShadowVertices = sibb.asFloatBuffer(); - mBufShadowVertices.position(0); - - mDropShadowCount = mSelfShadowCount = 0; - } + mBufNormals = ByteBuffer.allocateDirect(maxVerticesCount * 3 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mBufNormals.position(0); + + int maxShadowVerticesCount = (mMaxCurlSplits + 2) * 2 * 2; + mBufShadowVertices = ByteBuffer + .allocateDirect(maxShadowVerticesCount * 3 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mBufShadowVertices.position(0); + + mBufShadowPenumbra = ByteBuffer + .allocateDirect(maxShadowVerticesCount * 2 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mBufShadowPenumbra.position(0); } /** @@ -199,14 +147,11 @@ private void addVertex(Vertex vertex) { mBufVertices.put((float) vertex.mPosX); mBufVertices.put((float) vertex.mPosY); mBufVertices.put((float) vertex.mPosZ); - mBufColors.put(vertex.mColorFactor * Color.red(vertex.mColor) / 255f); - mBufColors.put(vertex.mColorFactor * Color.green(vertex.mColor) / 255f); - mBufColors.put(vertex.mColorFactor * Color.blue(vertex.mColor) / 255f); - mBufColors.put(Color.alpha(vertex.mColor) / 255f); - if (DRAW_TEXTURE) { - mBufTexCoords.put((float) vertex.mTexX); - mBufTexCoords.put((float) vertex.mTexY); - } + mBufTexCoords.put((float) vertex.mTexX); + mBufTexCoords.put((float) vertex.mTexY); + mBufNormals.put((float) vertex.mNormalX); + mBufNormals.put((float) vertex.mNormalY); + mBufNormals.put((float) vertex.mNormalZ); } /** @@ -220,35 +165,11 @@ private void addVertex(Vertex vertex) { * @param radius * Radius of curl. */ - public synchronized void curl(PointF curlPos, PointF curlDir, double radius) { - - // First add some 'helper' lines used for development. - if (DRAW_CURL_POSITION) { - mBufCurlPositionLines.position(0); - - mBufCurlPositionLines.put(curlPos.x); - mBufCurlPositionLines.put(curlPos.y - 1.0f); - mBufCurlPositionLines.put(curlPos.x); - mBufCurlPositionLines.put(curlPos.y + 1.0f); - mBufCurlPositionLines.put(curlPos.x - 1.0f); - mBufCurlPositionLines.put(curlPos.y); - mBufCurlPositionLines.put(curlPos.x + 1.0f); - mBufCurlPositionLines.put(curlPos.y); - - mBufCurlPositionLines.put(curlPos.x); - mBufCurlPositionLines.put(curlPos.y); - mBufCurlPositionLines.put(curlPos.x + curlDir.x * 2); - mBufCurlPositionLines.put(curlPos.y + curlDir.y * 2); - - mBufCurlPositionLines.position(0); - } + public void curl(PointF curlPos, PointF curlDir, double radius) { - // Actual 'curl' implementation starts here. mBufVertices.position(0); - mBufColors.position(0); - if (DRAW_TEXTURE) { - mBufTexCoords.position(0); - } + mBufTexCoords.position(0); + mBufNormals.position(0); // Calculate curl angle from direction. double curlAngle = Math.acos(curlDir.x); @@ -307,14 +228,12 @@ public synchronized void curl(PointF curlPos, PointF curlDir, double radius) { } } - mVerticesCountFront = mVerticesCountBack = 0; + mCountVertices = 0; - if (DRAW_SHADOW) { - mArrTempShadowVertices.addAll(mArrDropShadowVertices); - mArrTempShadowVertices.addAll(mArrSelfShadowVertices); - mArrDropShadowVertices.clear(); - mArrSelfShadowVertices.clear(); - } + mArrShadowTempVertices.addAll(mArrShadowDropVertices); + mArrShadowTempVertices.addAll(mArrShadowSelfVertices); + mArrShadowDropVertices.clear(); + mArrShadowSelfVertices.clear(); // Length of 'curl' curve. double curlLength = Math.PI * radius; @@ -390,6 +309,7 @@ public synchronized void curl(PointF curlPos, PointF curlDir, double radius) { if (intersections.size() == 2) { // There were two intersections, add them based on y // -coordinate, higher first, lower last. + Vertex v1 = intersections.get(0); Vertex v2 = intersections.get(1); if (v1.mPosY < v2.mPosY) { @@ -415,21 +335,22 @@ public synchronized void curl(PointF curlPos, PointF curlDir, double radius) { mArrTempVertices.add(v); // Local texture front-facing flag. - boolean textureFront; + // boolean textureFront; // Untouched vertices. if (i == 0) { - textureFront = true; - mVerticesCountFront++; + v.mNormalX = 0; + v.mNormalY = 0; + v.mNormalZ = 1; } // 'Completely' rotated vertices. else if (i == mArrScanLines.size() - 1 || curlLength == 0) { v.mPosX = -(curlLength + v.mPosX); v.mPosZ = 2 * radius; + v.mNormalX = 0; + v.mNormalY = 0; + v.mNormalZ = -1; v.mPenumbraX = -v.mPenumbraX; - - textureFront = false; - mVerticesCountBack++; } // Vertex lies within 'curl'. else { @@ -439,63 +360,41 @@ else if (i == mArrScanLines.size() - 1 || curlLength == 0) { double rotY = Math.PI * (v.mPosX / curlLength); v.mPosX = radius * Math.sin(rotY); v.mPosZ = radius - (radius * Math.cos(rotY)); + v.mNormalX = Math.sin(rotY); + v.mNormalY = 0; + v.mNormalZ = Math.cos(rotY); v.mPenumbraX *= Math.cos(rotY); - // Map color multiplier to [.1f, 1f] range. - v.mColorFactor = (float) (.1f + .9f * Math.sqrt(Math - .sin(rotY) + 1)); - - if (v.mPosZ >= radius) { - textureFront = false; - mVerticesCountBack++; - } else { - textureFront = true; - mVerticesCountFront++; - } - } - - // We use local textureFront for flipping backside texture - // locally. Plus additionally if mesh is in flip texture mode, - // we'll make the procedure "backwards". Also, until this point, - // texture coordinates are within [0, 1] range so we'll adjust - // them to final texture coordinates too. - if (textureFront != mFlipTexture) { - v.mTexX *= mTextureRectFront.right; - v.mTexY *= mTextureRectFront.bottom; - v.mColor = mTexturePage.getColor(CurlPage.SIDE_FRONT); - } else { - v.mTexX = (1f - v.mTexX) * mTextureRectBack.right; - v.mTexY *= mTextureRectBack.bottom; - v.mColor = mTexturePage.getColor(CurlPage.SIDE_BACK); } // Move vertex back to 'world' coordinates. v.rotateZ(curlAngle); v.translate(curlPos.x, curlPos.y); addVertex(v); + ++mCountVertices; // Drop shadow is cast 'behind' the curl. - if (DRAW_SHADOW && v.mPosZ > 0 && v.mPosZ <= radius) { - ShadowVertex sv = mArrTempShadowVertices.remove(0); + if (v.mPosZ > 0 && v.mPosZ <= radius) { + ShadowVertex sv = mArrShadowTempVertices.remove(0); sv.mPosX = v.mPosX; sv.mPosY = v.mPosY; sv.mPosZ = v.mPosZ; - sv.mPenumbraX = (v.mPosZ / 2) * -curlDir.x; - sv.mPenumbraY = (v.mPosZ / 2) * -curlDir.y; - sv.mPenumbraColor = v.mPosZ / radius; - int idx = (mArrDropShadowVertices.size() + 1) / 2; - mArrDropShadowVertices.add(idx, sv); + sv.mPenumbraX = (v.mPosZ * 0.8) * -curlDir.x; + sv.mPenumbraY = (v.mPosZ * 0.8) * -curlDir.y; + // sv.mPenumbraX += (v.mPosZ * 0.8) * v.mPenumbraX; + // sv.mPenumbraY += (v.mPosZ * 0.8) * v.mPenumbraY; + int idx = (mArrShadowDropVertices.size() + 1) / 2; + mArrShadowDropVertices.add(idx, sv); } // Self shadow is cast partly over mesh. - if (DRAW_SHADOW && v.mPosZ > radius) { - ShadowVertex sv = mArrTempShadowVertices.remove(0); + if (v.mPosZ > radius) { + ShadowVertex sv = mArrShadowTempVertices.remove(0); sv.mPosX = v.mPosX; sv.mPosY = v.mPosY; sv.mPosZ = v.mPosZ; - sv.mPenumbraX = ((v.mPosZ - radius) / 3) * v.mPenumbraX; - sv.mPenumbraY = ((v.mPosZ - radius) / 3) * v.mPenumbraY; - sv.mPenumbraColor = (v.mPosZ - radius) / (2 * radius); - int idx = (mArrSelfShadowVertices.size() + 1) / 2; - mArrSelfShadowVertices.add(idx, sv); + sv.mPenumbraX = ((v.mPosZ - radius) * 0.2) * v.mPenumbraX; + sv.mPenumbraY = ((v.mPosZ - radius) * 0.2) * v.mPenumbraY; + int idx = (mArrShadowSelfVertices.size() + 1) / 2; + mArrShadowSelfVertices.add(idx, sv); } } @@ -504,55 +403,58 @@ else if (i == mArrScanLines.size() - 1 || curlLength == 0) { } mBufVertices.position(0); - mBufColors.position(0); - if (DRAW_TEXTURE) { - mBufTexCoords.position(0); - } + mBufTexCoords.position(0); + mBufNormals.position(0); // Add shadow Vertices. - if (DRAW_SHADOW) { - mBufShadowColors.position(0); - mBufShadowVertices.position(0); - mDropShadowCount = 0; - - for (int i = 0; i < mArrDropShadowVertices.size(); ++i) { - ShadowVertex sv = mArrDropShadowVertices.get(i); - mBufShadowVertices.put((float) sv.mPosX); - mBufShadowVertices.put((float) sv.mPosY); - mBufShadowVertices.put((float) sv.mPosZ); - mBufShadowVertices.put((float) (sv.mPosX + sv.mPenumbraX)); - mBufShadowVertices.put((float) (sv.mPosY + sv.mPenumbraY)); - mBufShadowVertices.put((float) sv.mPosZ); - for (int j = 0; j < 4; ++j) { - double color = SHADOW_OUTER_COLOR[j] - + (SHADOW_INNER_COLOR[j] - SHADOW_OUTER_COLOR[j]) - * sv.mPenumbraColor; - mBufShadowColors.put((float) color); - } - mBufShadowColors.put(SHADOW_OUTER_COLOR); - mDropShadowCount += 2; - } - mSelfShadowCount = 0; - for (int i = 0; i < mArrSelfShadowVertices.size(); ++i) { - ShadowVertex sv = mArrSelfShadowVertices.get(i); - mBufShadowVertices.put((float) sv.mPosX); - mBufShadowVertices.put((float) sv.mPosY); - mBufShadowVertices.put((float) sv.mPosZ); - mBufShadowVertices.put((float) (sv.mPosX + sv.mPenumbraX)); - mBufShadowVertices.put((float) (sv.mPosY + sv.mPenumbraY)); - mBufShadowVertices.put((float) sv.mPosZ); - for (int j = 0; j < 4; ++j) { - double color = SHADOW_OUTER_COLOR[j] - + (SHADOW_INNER_COLOR[j] - SHADOW_OUTER_COLOR[j]) - * sv.mPenumbraColor; - mBufShadowColors.put((float) color); - } - mBufShadowColors.put(SHADOW_OUTER_COLOR); - mSelfShadowCount += 2; - } - mBufShadowColors.position(0); - mBufShadowVertices.position(0); - } + mBufShadowVertices.position(0); + mBufShadowPenumbra.position(0); + mCountShadowDrop = mCountShadowSelf = 0; + + for (int i = 0; i < mArrShadowDropVertices.size(); ++i) { + ShadowVertex sv = mArrShadowDropVertices.get(i); + mBufShadowVertices.put((float) sv.mPosX); + mBufShadowVertices.put((float) sv.mPosY); + mBufShadowVertices.put((float) sv.mPosZ); + mBufShadowPenumbra.put(0); + mBufShadowPenumbra.put(0); + mBufShadowVertices.put((float) sv.mPosX); + mBufShadowVertices.put((float) sv.mPosY); + mBufShadowVertices.put((float) sv.mPosZ); + mBufShadowPenumbra.put((float) sv.mPenumbraX); + mBufShadowPenumbra.put((float) sv.mPenumbraY); + mCountShadowDrop += 2; + } + for (int i = 0; i < mArrShadowSelfVertices.size(); ++i) { + ShadowVertex sv = mArrShadowSelfVertices.get(i); + mBufShadowVertices.put((float) sv.mPosX); + mBufShadowVertices.put((float) sv.mPosY); + mBufShadowVertices.put((float) sv.mPosZ); + mBufShadowPenumbra.put(0); + mBufShadowPenumbra.put(0); + mBufShadowVertices.put((float) sv.mPosX); + mBufShadowVertices.put((float) sv.mPosY); + mBufShadowVertices.put((float) sv.mPosZ); + mBufShadowPenumbra.put((float) sv.mPenumbraX); + mBufShadowPenumbra.put((float) sv.mPenumbraY); + mCountShadowSelf += 2; + } + mBufShadowVertices.position(0); + mBufShadowPenumbra.position(0); + } + + /** + * Getter for drop shadow vertices count. + */ + public int getDropShadowCount() { + return mCountShadowDrop; + } + + /** + * Getter for whether page should be flipped. + */ + public boolean getFlipTexture() { + return mFlipTexture; } /** @@ -576,14 +478,10 @@ private Array getIntersections(Array vertices, n.set(v2); n.mPosX = scanX; n.mPosY += (v1.mPosY - v2.mPosY) * c; - if (DRAW_TEXTURE) { - n.mTexX += (v1.mTexX - v2.mTexX) * c; - n.mTexY += (v1.mTexY - v2.mTexY) * c; - } - if (DRAW_SHADOW) { - n.mPenumbraX += (v1.mPenumbraX - v2.mPenumbraX) * c; - n.mPenumbraY += (v1.mPenumbraY - v2.mPenumbraY) * c; - } + n.mTexX += (v1.mTexX - v2.mTexX) * c; + n.mTexY += (v1.mTexY - v2.mTexY) * c; + n.mPenumbraX += (v1.mPenumbraX - v2.mPenumbraX) * c; + n.mPenumbraY += (v1.mPenumbraY - v2.mPenumbraY) * c; mArrIntersections.add(n); } } @@ -591,203 +489,116 @@ private Array getIntersections(Array vertices, } /** - * Getter for textures page for this mesh. + * Getter for normal Buffer. */ - public synchronized CurlPage getTexturePage() { - return mTexturePage; + public FloatBuffer getNormals() { + return mBufNormals; } /** - * Renders our page curl mesh. + * Getter for page for this mesh. */ - public synchronized void onDrawFrame(GL10 gl) { - // First allocate texture if there is not one yet. - if (DRAW_TEXTURE && mTextureIds == null) { - // Generate texture. - mTextureIds = new int[2]; - gl.glGenTextures(2, mTextureIds, 0); - for (int textureId : mTextureIds) { - // Set texture attributes. - gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, - GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, - GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, - GL10.GL_CLAMP_TO_EDGE); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, - GL10.GL_CLAMP_TO_EDGE); - } - } - - if (DRAW_TEXTURE && mTexturePage.getTexturesChanged()) { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[0]); - Bitmap texture = mTexturePage.getTexture(mTextureRectFront, - CurlPage.SIDE_FRONT); - GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, texture, 0); - texture.recycle(); - - mTextureBack = mTexturePage.hasBackTexture(); - if (mTextureBack) { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[1]); - texture = mTexturePage.getTexture(mTextureRectBack, - CurlPage.SIDE_BACK); - GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, texture, 0); - texture.recycle(); - } else { - mTextureRectBack.set(mTextureRectFront); - } - - mTexturePage.recycle(); - reset(); - } - - // Some 'global' settings. - gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); - - // TODO: Drop shadow drawing is done temporarily here to hide some - // problems with its calculation. - if (DRAW_SHADOW) { - gl.glDisable(GL10.GL_TEXTURE_2D); - gl.glEnable(GL10.GL_BLEND); - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - gl.glEnableClientState(GL10.GL_COLOR_ARRAY); - gl.glColorPointer(4, GL10.GL_FLOAT, 0, mBufShadowColors); - gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufShadowVertices); - gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mDropShadowCount); - gl.glDisableClientState(GL10.GL_COLOR_ARRAY); - gl.glDisable(GL10.GL_BLEND); - } - - if (DRAW_TEXTURE) { - gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); - gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mBufTexCoords); - } - gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufVertices); - // Enable color array. - gl.glEnableClientState(GL10.GL_COLOR_ARRAY); - gl.glColorPointer(4, GL10.GL_FLOAT, 0, mBufColors); - - // Draw front facing blank vertices. - gl.glDisable(GL10.GL_TEXTURE_2D); - gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticesCountFront); - - // Draw front facing texture. - if (DRAW_TEXTURE) { - gl.glEnable(GL10.GL_BLEND); - gl.glEnable(GL10.GL_TEXTURE_2D); - - if (!mFlipTexture || !mTextureBack) { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[0]); - } else { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[1]); - } + public CurlPage getPage() { + return mPage; + } - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticesCountFront); + /** + * Getter for self shadow vertices count. + */ + public int getSelfShadowCount() { + return mCountShadowSelf; + } - gl.glDisable(GL10.GL_BLEND); - gl.glDisable(GL10.GL_TEXTURE_2D); - } + /** + * Getter for shadow penumbra buffer. + */ + public FloatBuffer getShadowPenumbra() { + return mBufShadowPenumbra; + } - int backStartIdx = Math.max(0, mVerticesCountFront - 2); - int backCount = mVerticesCountFront + mVerticesCountBack - backStartIdx; + /** + * Getter for shadow vertices buffer. + */ + public FloatBuffer getShadowVertices() { + return mBufShadowVertices; + } - // Draw back facing blank vertices. - gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, backStartIdx, backCount); + /** + * Getter for texture coordinate Buffer. + */ + public FloatBuffer getTexCoords() { + return mBufTexCoords; + } - // Draw back facing texture. - if (DRAW_TEXTURE) { - gl.glEnable(GL10.GL_BLEND); - gl.glEnable(GL10.GL_TEXTURE_2D); + /** + * Getter for texture ids. Must be called from GL thread. + */ + public int[] getTextures() { - if (mFlipTexture || !mTextureBack) { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[0]); - } else { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[1]); + // First allocate texture if there is not one yet. + if (mTextureIds == null) { + // Generate texture. + mTextureIds = new int[2]; + GLES20.glGenTextures(2, mTextureIds, 0); + for (int textureId : mTextureIds) { + // Set texture attributes. + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); } - - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, backStartIdx, backCount); - - gl.glDisable(GL10.GL_BLEND); - gl.glDisable(GL10.GL_TEXTURE_2D); } - // Disable textures and color array. - gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); - gl.glDisableClientState(GL10.GL_COLOR_ARRAY); - - if (DRAW_POLYGON_OUTLINES) { - gl.glEnable(GL10.GL_BLEND); - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - gl.glLineWidth(1.0f); - gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f); - gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufVertices); - gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, mVerticesCountFront); - gl.glDisable(GL10.GL_BLEND); + if (mPage.getBitmapsChanged()) { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0]); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, + mPage.getBitmap(CurlPage.SIDE_FRONT), 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1]); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, + mPage.getBitmap(CurlPage.SIDE_BACK), 0); + mPage.recycle(); } - if (DRAW_CURL_POSITION) { - gl.glEnable(GL10.GL_BLEND); - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - gl.glLineWidth(1.0f); - gl.glColor4f(1.0f, 0.5f, 0.5f, 1.0f); - gl.glVertexPointer(2, GL10.GL_FLOAT, 0, mBufCurlPositionLines); - gl.glDrawArrays(GL10.GL_LINES, 0, mCurlPositionLinesCount * 2); - gl.glDisable(GL10.GL_BLEND); - } + return mTextureIds; + } - if (DRAW_SHADOW) { - gl.glEnable(GL10.GL_BLEND); - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - gl.glEnableClientState(GL10.GL_COLOR_ARRAY); - gl.glColorPointer(4, GL10.GL_FLOAT, 0, mBufShadowColors); - gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufShadowVertices); - gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, mDropShadowCount, - mSelfShadowCount); - gl.glDisableClientState(GL10.GL_COLOR_ARRAY); - gl.glDisable(GL10.GL_BLEND); - } + /** + * Getter for vertex count. + */ + public int getVertexCount() { + return mCountVertices; + } - gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); + /** + * Getter for vertex Buffer. + */ + public FloatBuffer getVertices() { + return mBufVertices; } /** * Resets mesh to 'initial' state. Meaning this mesh will draw a plain * textured rectangle after call to this method. */ - public synchronized void reset() { + public void reset() { mBufVertices.position(0); - mBufColors.position(0); - if (DRAW_TEXTURE) { - mBufTexCoords.position(0); - } + mBufTexCoords.position(0); + mBufNormals.position(0); for (int i = 0; i < 4; ++i) { Vertex tmp = mArrTempVertices.get(0); tmp.set(mRectangle[i]); - - if (mFlipTexture) { - tmp.mTexX = (1f - tmp.mTexX) * mTextureRectBack.right; - tmp.mTexY *= mTextureRectBack.bottom; - tmp.mColor = mTexturePage.getColor(CurlPage.SIDE_BACK); - } else { - tmp.mTexX *= mTextureRectFront.right; - tmp.mTexY *= mTextureRectFront.bottom; - tmp.mColor = mTexturePage.getColor(CurlPage.SIDE_FRONT); - } - addVertex(tmp); } - mVerticesCountFront = 4; - mVerticesCountBack = 0; + mCountVertices = 4; mBufVertices.position(0); - mBufColors.position(0); - if (DRAW_TEXTURE) { - mBufTexCoords.position(0); - } - - mDropShadowCount = mSelfShadowCount = 0; + mBufTexCoords.position(0); + mBufNormals.position(0); + mCountShadowDrop = mCountShadowSelf = 0; } /** @@ -797,14 +608,14 @@ public synchronized void reset() { * method does not release previous texture id, only makes sure new one is * requested on next render. */ - public synchronized void resetTexture() { + public void resetTextures() { mTextureIds = null; } /** * If true, flips texture sideways. */ - public synchronized void setFlipTexture(boolean flipTexture) { + public void setFlipTexture(boolean flipTexture) { mFlipTexture = flipTexture; if (flipTexture) { setTexCoords(1f, 0f, 0f, 1f); @@ -830,8 +641,7 @@ public void setRect(RectF r) { /** * Sets texture coordinates to mRectangle vertices. */ - private synchronized void setTexCoords(float left, float top, float right, - float bottom) { + private void setTexCoords(float left, float top, float right, float bottom) { mRectangle[0].mTexX = left; mRectangle[0].mTexY = top; mRectangle[1].mTexX = left; @@ -917,7 +727,6 @@ public int size() { * Holder for shadow vertex information. */ private class ShadowVertex { - public double mPenumbraColor; public double mPenumbraX; public double mPenumbraY; public double mPosX; @@ -929,8 +738,9 @@ private class ShadowVertex { * Holder for vertex information. */ private class Vertex { - public int mColor; - public float mColorFactor; + public double mNormalX; + public double mNormalY; + public double mNormalZ; public double mPenumbraX; public double mPenumbraY; public double mPosX; @@ -940,8 +750,9 @@ private class Vertex { public double mTexY; public Vertex() { + mNormalX = mNormalY = 0; + mNormalZ = 1; mPosX = mPosY = mPosZ = mTexX = mTexY = 0; - mColorFactor = 1.0f; } public void rotateZ(double theta) { @@ -951,6 +762,10 @@ public void rotateZ(double theta) { double y = mPosX * -sin + mPosY * cos; mPosX = x; mPosY = y; + double nx = mNormalX * cos + mNormalY * sin; + double ny = mNormalX * -sin + mNormalY * cos; + mNormalX = nx; + mNormalY = ny; double px = mPenumbraX * cos + mPenumbraY * sin; double py = mPenumbraX * -sin + mPenumbraY * cos; mPenumbraX = px; @@ -963,10 +778,11 @@ public void set(Vertex vertex) { mPosZ = vertex.mPosZ; mTexX = vertex.mTexX; mTexY = vertex.mTexY; + mNormalX = vertex.mNormalX; + mNormalY = vertex.mNormalY; + mNormalZ = vertex.mNormalZ; mPenumbraX = vertex.mPenumbraX; mPenumbraY = vertex.mPenumbraY; - mColor = vertex.mColor; - mColorFactor = vertex.mColorFactor; } public void translate(double dx, double dy) { diff --git a/src/fi/harism/curl/CurlPage.java b/src/fi/harism/curl/CurlPage.java index af2f8a7..702bbcc 100644 --- a/src/fi/harism/curl/CurlPage.java +++ b/src/fi/harism/curl/CurlPage.java @@ -1,5 +1,5 @@ /* - Copyright 2012 Harri Smatt + Copyright 2013 Harri Smatt Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,9 +17,7 @@ package fi.harism.curl; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.RectF; /** * Storage class for page textures, blend colors and possibly some other values @@ -33,11 +31,11 @@ public class CurlPage { public static final int SIDE_BOTH = 3; public static final int SIDE_FRONT = 1; + private Bitmap mBitmapBack; + private Bitmap mBitmapFront; + private boolean mBitmapsChanged; private int mColorBack; private int mColorFront; - private Bitmap mTextureBack; - private Bitmap mTextureFront; - private boolean mTexturesChanged; /** * Default constructor. @@ -47,165 +45,111 @@ public CurlPage() { } /** - * Getter for color. + * Getter for bitmap. */ - public int getColor(int side) { + public Bitmap getBitmap(int side) { switch (side) { case SIDE_FRONT: - return mColorFront; + return mBitmapFront; + case SIDE_BACK: + return mBitmapBack; default: - return mColorBack; + return null; } } - /** - * Calculates the next highest power of two for a given integer. - */ - private int getNextHighestPO2(int n) { - n -= 1; - n = n | (n >> 1); - n = n | (n >> 2); - n = n | (n >> 4); - n = n | (n >> 8); - n = n | (n >> 16); - n = n | (n >> 32); - return n + 1; - } - - /** - * Generates nearest power of two sized Bitmap for give Bitmap. Returns this - * new Bitmap using default return statement + original texture coordinates - * are stored into RectF. - */ - private Bitmap getTexture(Bitmap bitmap, RectF textureRect) { - // Bitmap original size. - int w = bitmap.getWidth(); - int h = bitmap.getHeight(); - // Bitmap size expanded to next power of two. This is done due to - // the requirement on many devices, texture width and height should - // be power of two. - int newW = getNextHighestPO2(w); - int newH = getNextHighestPO2(h); - - // TODO: Is there another way to create a bigger Bitmap and copy - // original Bitmap to it more efficiently? Immutable bitmap anyone? - Bitmap bitmapTex = Bitmap.createBitmap(newW, newH, bitmap.getConfig()); - Canvas c = new Canvas(bitmapTex); - c.drawBitmap(bitmap, 0, 0, null); - - // Calculate final texture coordinates. - float texX = (float) w / newW; - float texY = (float) h / newH; - textureRect.set(0f, 0f, texX, texY); - - return bitmapTex; + public boolean getBitmapsChanged() { + return mBitmapsChanged; } /** - * Getter for textures. Creates Bitmap sized to nearest power of two, copies - * original Bitmap into it and returns it. RectF given as parameter is - * filled with actual texture coordinates in this new upscaled texture - * Bitmap. + * Getter for color. */ - public Bitmap getTexture(RectF textureRect, int side) { + public int getColor(int side) { switch (side) { case SIDE_FRONT: - return getTexture(mTextureFront, textureRect); + return mColorFront; default: - return getTexture(mTextureBack, textureRect); + return mColorBack; } } - /** - * Returns true if textures have changed. - */ - public boolean getTexturesChanged() { - return mTexturesChanged; - } - - /** - * Returns true if back siding texture exists and it differs from front - * facing one. - */ - public boolean hasBackTexture() { - return !mTextureFront.equals(mTextureBack); - } - /** * Recycles and frees underlying Bitmaps. */ public void recycle() { - if (mTextureFront != null) { - mTextureFront.recycle(); + if (mBitmapFront != null) { + mBitmapFront.recycle(); } - mTextureFront = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); - mTextureFront.eraseColor(mColorFront); - if (mTextureBack != null) { - mTextureBack.recycle(); + mBitmapFront = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); + mBitmapFront.eraseColor(mColorFront); + if (mBitmapBack != null) { + mBitmapBack.recycle(); } - mTextureBack = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); - mTextureBack.eraseColor(mColorBack); - mTexturesChanged = false; + mBitmapBack = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); + mBitmapBack.eraseColor(mColorBack); + mBitmapsChanged = false; } /** * Resets this CurlPage into its initial state. */ public void reset() { - mColorBack = Color.WHITE; - mColorFront = Color.WHITE; + mColorBack = Color.TRANSPARENT; + mColorFront = Color.TRANSPARENT; recycle(); + mBitmapsChanged = true; } /** - * Setter blend color. + * Setter for Bitmaps. */ - public void setColor(int color, int side) { + public void setBitmap(Bitmap texture, int side) { + if (texture == null) { + texture = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); + if (side == SIDE_BACK) { + texture.eraseColor(mColorBack); + } else { + texture.eraseColor(mColorFront); + } + } switch (side) { case SIDE_FRONT: - mColorFront = color; + if (mBitmapFront != null) + mBitmapFront.recycle(); + mBitmapFront = texture; break; case SIDE_BACK: - mColorBack = color; + if (mBitmapBack != null) + mBitmapBack.recycle(); + mBitmapBack = texture; break; - default: - mColorFront = mColorBack = color; + case SIDE_BOTH: + if (mBitmapFront != null) + mBitmapFront.recycle(); + if (mBitmapBack != null) + mBitmapBack.recycle(); + mBitmapFront = mBitmapBack = texture; break; } + mBitmapsChanged = true; } /** - * Setter for textures. + * Setter blend color. */ - public void setTexture(Bitmap texture, int side) { - if (texture == null) { - texture = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); - if (side == SIDE_BACK) { - texture.eraseColor(mColorBack); - } else { - texture.eraseColor(mColorFront); - } - } + public void setColor(int color, int side) { switch (side) { case SIDE_FRONT: - if (mTextureFront != null) - mTextureFront.recycle(); - mTextureFront = texture; + mColorFront = color; break; case SIDE_BACK: - if (mTextureBack != null) - mTextureBack.recycle(); - mTextureBack = texture; + mColorBack = color; break; - case SIDE_BOTH: - if (mTextureFront != null) - mTextureFront.recycle(); - if (mTextureBack != null) - mTextureBack.recycle(); - mTextureFront = mTextureBack = texture; + default: + mColorFront = mColorBack = color; break; } - mTexturesChanged = true; } } diff --git a/src/fi/harism/curl/CurlRenderer.java b/src/fi/harism/curl/CurlRenderer.java index 3b12479..7cc3321 100644 --- a/src/fi/harism/curl/CurlRenderer.java +++ b/src/fi/harism/curl/CurlRenderer.java @@ -1,5 +1,5 @@ /* - Copyright 2012 Harri Smatt + Copyright 2013 Harri Smatt Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,8 +24,9 @@ import android.graphics.Color; import android.graphics.PointF; import android.graphics.RectF; +import android.opengl.GLES20; import android.opengl.GLSurfaceView; -import android.opengl.GLU; +import android.opengl.Matrix; /** * Actual renderer class. @@ -40,30 +41,29 @@ public class CurlRenderer implements GLSurfaceView.Renderer { public static final int PAGE_RIGHT = 2; // Constants for changing view mode. public static final int SHOW_ONE_PAGE = 1; - public static final int SHOW_TWO_PAGES = 2; // Set to true for checking quickly how perspective projection looks. - private static final boolean USE_PERSPECTIVE_PROJECTION = false; - - private int mBackgroundColor; - - private boolean mBackgroundColorChanged = false; + // private static final boolean USE_PERSPECTIVE_PROJECTION = false; + // Background color constant. + private int mBackgroundColor = 0xFF000000; // Curl meshes used for static and dynamic rendering. private Vector mCurlMeshes; private RectF mMargins = new RectF(); private CurlRenderer.Observer mObserver; - - private RectF mPageRectLeft; - - private RectF mPageRectRight; + // Page rectangles. + private final RectF mPageRectLeft; + private final RectF mPageRectRight; + // Projection matrix. + private final float[] mProjectionMatrix = new float[16]; + // Shaders. + private final CurlShader mShaderShadow = new CurlShader(); + private final CurlShader mShaderTexture = new CurlShader(); + // View mode. private int mViewMode = SHOW_ONE_PAGE; - - private int mViewportHeight; - // Screen size. - private int mViewportWidth; + private int mViewportWidth, mViewportHeight; // Rect for render area. - private RectF mViewRect = new RectF(); + private final RectF mViewRect = new RectF(); /** * Basic constructor. @@ -78,7 +78,7 @@ public CurlRenderer(CurlRenderer.Observer observer) { /** * Adds CurlMesh to this renderer. */ - public synchronized void addCurlMesh(CurlMesh mesh) { + public void addCurlMesh(CurlMesh mesh) { removeCurlMesh(mesh); mCurlMeshes.add(mesh); } @@ -97,33 +97,137 @@ public RectF getPageRect(int page) { } @Override - public synchronized void onDrawFrame(GL10 gl) { + public void onDrawFrame(GL10 unused) { mObserver.onDrawFrame(); - if (mBackgroundColorChanged) { - gl.glClearColor(Color.red(mBackgroundColor) / 255f, - Color.green(mBackgroundColor) / 255f, - Color.blue(mBackgroundColor) / 255f, - Color.alpha(mBackgroundColor) / 255f); - mBackgroundColorChanged = false; - } - - gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // | GL10.GL_DEPTH_BUFFER_BIT); - gl.glLoadIdentity(); - - if (USE_PERSPECTIVE_PROJECTION) { - gl.glTranslatef(0, 0, -6f); - } - - for (int i = 0; i < mCurlMeshes.size(); ++i) { - mCurlMeshes.get(i).onDrawFrame(gl); + GLES20.glClearColor(Color.red(mBackgroundColor) / 255f, + Color.green(mBackgroundColor) / 255f, + Color.blue(mBackgroundColor) / 255f, + Color.alpha(mBackgroundColor) / 255f); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + + for (CurlMesh mesh : mCurlMeshes) { + + // + // Render drop shadow. + // + + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, + GLES20.GL_ONE_MINUS_SRC_ALPHA); + + mShaderShadow.useProgram(); + + GLES20.glVertexAttribPointer(mShaderShadow.getHandle("aPosition"), + 3, GLES20.GL_FLOAT, false, 0, mesh.getShadowVertices()); + GLES20.glEnableVertexAttribArray(mShaderShadow + .getHandle("aPosition")); + + GLES20.glVertexAttribPointer(mShaderShadow.getHandle("aPenumbra"), + 2, GLES20.GL_FLOAT, false, 0, mesh.getShadowPenumbra()); + GLES20.glEnableVertexAttribArray(mShaderShadow + .getHandle("aPenumbra")); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, + mesh.getDropShadowCount()); + + GLES20.glDisable(GLES20.GL_BLEND); + + // + // Render page textures. + // + + mShaderTexture.useProgram(); + + GLES20.glVertexAttribPointer(mShaderTexture.getHandle("aPosition"), + 3, GLES20.GL_FLOAT, false, 0, mesh.getVertices()); + GLES20.glEnableVertexAttribArray(mShaderTexture + .getHandle("aPosition")); + GLES20.glVertexAttribPointer(mShaderTexture.getHandle("aNormal"), + 3, GLES20.GL_FLOAT, false, 0, mesh.getNormals()); + GLES20.glEnableVertexAttribArray(mShaderTexture + .getHandle("aNormal")); + GLES20.glVertexAttribPointer(mShaderTexture.getHandle("aTexCoord"), + 2, GLES20.GL_FLOAT, false, 0, mesh.getTexCoords()); + GLES20.glEnableVertexAttribArray(mShaderTexture + .getHandle("aTexCoord")); + + if (!mesh.getFlipTexture()) { + int color = mesh.getPage().getColor(CurlPage.SIDE_FRONT); + GLES20.glUniform4f(mShaderTexture.getHandle("uColorFront"), + Color.red(color) / 255f, Color.green(color) / 255f, + Color.blue(color) / 255f, Color.alpha(color) / 255f); + + color = mesh.getPage().getColor(CurlPage.SIDE_BACK); + GLES20.glUniform4f(mShaderTexture.getHandle("uColorBack"), + Color.red(color) / 255f, Color.green(color) / 255f, + Color.blue(color) / 255f, Color.alpha(color) / 255f); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, + mesh.getTextures()[0]); + GLES20.glUniform1i(mShaderTexture.getHandle("sTextureFront"), 0); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, + mesh.getTextures()[1]); + GLES20.glUniform1i(mShaderTexture.getHandle("sTextureBack"), 1); + } else { + int color = mesh.getPage().getColor(CurlPage.SIDE_BACK); + GLES20.glUniform4f(mShaderTexture.getHandle("uColorFront"), + Color.red(color) / 255f, Color.green(color) / 255f, + Color.blue(color) / 255f, Color.alpha(color) / 255f); + + color = mesh.getPage().getColor(CurlPage.SIDE_FRONT); + GLES20.glUniform4f(mShaderTexture.getHandle("uColorBack"), + Color.red(color) / 255f, Color.green(color) / 255f, + Color.blue(color) / 255f, Color.alpha(color) / 255f); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, + mesh.getTextures()[1]); + GLES20.glUniform1i(mShaderTexture.getHandle("sTextureFront"), 0); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, + mesh.getTextures()[0]); + GLES20.glUniform1i(mShaderTexture.getHandle("sTextureBack"), 1); + } + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, + mesh.getVertexCount()); + + // + // Render self shadow. + // + + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, + GLES20.GL_ONE_MINUS_SRC_ALPHA); + + mShaderShadow.useProgram(); + + GLES20.glVertexAttribPointer(mShaderShadow.getHandle("aPosition"), + 3, GLES20.GL_FLOAT, false, 0, mesh.getShadowVertices()); + GLES20.glEnableVertexAttribArray(mShaderShadow + .getHandle("aPosition")); + + GLES20.glVertexAttribPointer(mShaderShadow.getHandle("aPenumbra"), + 2, GLES20.GL_FLOAT, false, 0, mesh.getShadowPenumbra()); + GLES20.glEnableVertexAttribArray(mShaderShadow + .getHandle("aPenumbra")); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, + mesh.getDropShadowCount(), mesh.getSelfShadowCount()); + + GLES20.glDisable(GLES20.GL_BLEND); } } @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - gl.glViewport(0, 0, width, height); + public void onSurfaceChanged(GL10 unused, int width, int height) { + GLES20.glViewport(0, 0, width, height); mViewportWidth = width; mViewportHeight = height; @@ -134,29 +238,28 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { mViewRect.right = ratio; updatePageRects(); - gl.glMatrixMode(GL10.GL_PROJECTION); - gl.glLoadIdentity(); - if (USE_PERSPECTIVE_PROJECTION) { - GLU.gluPerspective(gl, 20f, (float) width / height, .1f, 100f); - } else { - GLU.gluOrtho2D(gl, mViewRect.left, mViewRect.right, - mViewRect.bottom, mViewRect.top); - } + Matrix.orthoM(mProjectionMatrix, 0, -ratio, ratio, -1f, 1f, -10f, 10f); + mShaderTexture.useProgram(); + GLES20.glUniformMatrix4fv(mShaderTexture.getHandle("uProjectionM"), 1, + false, mProjectionMatrix, 0); + mShaderShadow.useProgram(); + GLES20.glUniformMatrix4fv(mShaderShadow.getHandle("uProjectionM"), 1, + false, mProjectionMatrix, 0); - gl.glMatrixMode(GL10.GL_MODELVIEW); - gl.glLoadIdentity(); + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + GLES20.glDisable(GLES20.GL_CULL_FACE); } @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - gl.glClearColor(0f, 0f, 0f, 1f); - gl.glShadeModel(GL10.GL_SMOOTH); - gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); - gl.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST); - gl.glHint(GL10.GL_POLYGON_SMOOTH_HINT, GL10.GL_NICEST); - gl.glEnable(GL10.GL_LINE_SMOOTH); - gl.glDisable(GL10.GL_DEPTH_TEST); - gl.glDisable(GL10.GL_CULL_FACE); + public void onSurfaceCreated(GL10 unused, EGLConfig config) { + try { + mShaderShadow.setProgram(CurlStatic.SHADER_SHADOW_VERTEX, + CurlStatic.SHADER_SHADOW_FRAGMENT); + mShaderTexture.setProgram(CurlStatic.SHADER_TEXTURE_VERTEX, + CurlStatic.SHADER_TEXTURE_FRAGMENT); + } catch (Exception ex) { + ex.printStackTrace(); + } mObserver.onSurfaceCreated(); } @@ -164,7 +267,7 @@ public void onSurfaceCreated(GL10 gl, EGLConfig config) { /** * Removes CurlMesh from this renderer. */ - public synchronized void removeCurlMesh(CurlMesh mesh) { + public void removeCurlMesh(CurlMesh mesh) { while (mCurlMeshes.remove(mesh)) ; } @@ -174,15 +277,13 @@ public synchronized void removeCurlMesh(CurlMesh mesh) { */ public void setBackgroundColor(int color) { mBackgroundColor = color; - mBackgroundColorChanged = true; } /** * Set margins or padding. Note: margins are proportional. Meaning a value * of .1f will produce a 10% margin. */ - public synchronized void setMargins(float left, float top, float right, - float bottom) { + public void setMargins(float left, float top, float right, float bottom) { mMargins.left = left; mMargins.top = top; mMargins.right = right; @@ -194,7 +295,7 @@ public synchronized void setMargins(float left, float top, float right, * Sets visible page count to one or two. Should be either SHOW_ONE_PAGE or * SHOW_TWO_PAGES. */ - public synchronized void setViewMode(int viewmode) { + public void setViewMode(int viewmode) { if (viewmode == SHOW_ONE_PAGE) { mViewMode = viewmode; updatePageRects(); diff --git a/src/fi/harism/curl/CurlShader.java b/src/fi/harism/curl/CurlShader.java new file mode 100644 index 0000000..fb71efc --- /dev/null +++ b/src/fi/harism/curl/CurlShader.java @@ -0,0 +1,143 @@ +/* + Copyright 2013 Harri Smatt + + 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 fi.harism.curl; + +import java.util.HashMap; + +import android.opengl.GLES20; +import android.util.Log; + +/** + * Helper class for handling shaders. + */ +public final class CurlShader { + + // Shader program handle. + private int mProgram = -1; + // HashMap for storing uniform/attribute handles. + private final HashMap mShaderHandleMap = new HashMap(); + + /** + * Get id for given handle name. This method checks for both attribute and + * uniform handles. + * + * @param name + * Name of handle. + * @return Id for given handle or -1 if none found. + */ + public int getHandle(String name) { + if (mShaderHandleMap.containsKey(name)) { + return mShaderHandleMap.get(name); + } + int handle = GLES20.glGetAttribLocation(mProgram, name); + if (handle == -1) { + handle = GLES20.glGetUniformLocation(mProgram, name); + } + if (handle == -1) { + // One should never leave log messages but am not going to follow + // this rule. This line comes handy if you see repeating 'not found' + // messages on LogCat - usually for typos otherwise annoying to + // spot from shader code. + Log.d("CurlShader", "Could not get attrib location for " + name); + } else { + mShaderHandleMap.put(name, handle); + } + return handle; + } + + /** + * Get array of ids with given names. Returned array is sized to given + * amount name elements. + * + * @param names + * List of handle names. + * @return array of handle ids. + */ + public int[] getHandles(String... names) { + int[] res = new int[names.length]; + for (int i = 0; i < names.length; ++i) { + res[i] = getHandle(names[i]); + } + return res; + } + + /** + * Helper method for compiling a shader. + * + * @param shaderType + * Type of shader to compile + * @param source + * String presentation for shader + * @return id for compiled shader + */ + private int loadShader(int shaderType, String source) throws Exception { + int shader = GLES20.glCreateShader(shaderType); + if (shader != 0) { + GLES20.glShaderSource(shader, source); + GLES20.glCompileShader(shader); + int[] compiled = new int[1]; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); + if (compiled[0] == 0) { + String error = GLES20.glGetShaderInfoLog(shader); + GLES20.glDeleteShader(shader); + throw new Exception(error); + } + } + return shader; + } + + /** + * Compiles vertex and fragment shaders and links them into a program one + * can use for rendering. Once OpenGL context is lost and onSurfaceCreated + * is called, there is no need to reset existing GlslShader objects but one + * can simply reload shader. + * + * @param vertexSource + * String presentation for vertex shader + * @param fragmentSource + * String presentation for fragment shader + */ + public void setProgram(String vertexSource, String fragmentSource) + throws Exception { + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, + fragmentSource); + int program = GLES20.glCreateProgram(); + if (program != 0) { + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glLinkProgram(program); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] != GLES20.GL_TRUE) { + String error = GLES20.glGetProgramInfoLog(program); + GLES20.glDeleteProgram(program); + throw new Exception(error); + } + } + mProgram = program; + mShaderHandleMap.clear(); + } + + /** + * Activates this shader program. + */ + public void useProgram() { + GLES20.glUseProgram(mProgram); + } + +} diff --git a/src/fi/harism/curl/CurlStatic.java b/src/fi/harism/curl/CurlStatic.java new file mode 100644 index 0000000..f361085 --- /dev/null +++ b/src/fi/harism/curl/CurlStatic.java @@ -0,0 +1,79 @@ +/* + Copyright 2013 Harri Smatt + + 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 fi.harism.curl; + +public final class CurlStatic { + + // Shaders. + + public static final String SHADER_SHADOW_FRAGMENT = "" + + "precision mediump float; \n" + + "varying vec2 vPenumbra; \n" + + "void main() { \n" + + " float alpha = 1.0 - length(vPenumbra); \n" + + " alpha = smoothstep(0.0, 2.0, alpha); \n" + + " gl_FragColor = vec4(0.0, 0.0, 0.0, alpha); \n" + + "} \n"; + + public static final String SHADER_SHADOW_VERTEX = "" + + "uniform mat4 uProjectionM; \n" + + "attribute vec3 aPosition; \n" + + "attribute vec2 aPenumbra; \n" + + "varying vec2 vPenumbra; \n" + + "void main() { \n" + + " vec3 pos = vec3(aPosition.xy + aPenumbra, aPosition.z); \n" + + " gl_Position = uProjectionM * vec4(pos, 1.0); \n" + + " vPenumbra = normalize(aPenumbra); \n" + + "} \n"; + + public static final String SHADER_TEXTURE_FRAGMENT = "" + + "precision mediump float; \n" + + "uniform sampler2D sTextureFront; \n" + + "uniform sampler2D sTextureBack; \n" + + "uniform vec4 uColorFront; \n" + + "uniform vec4 uColorBack; \n" + + "varying vec3 vNormal; \n" + + "varying vec2 vTexCoord; \n" + + "void main() { \n" + + " vec3 normal = normalize(vNormal); \n" + + " if (normal.z >= 0.0) { \n" + + " gl_FragColor = texture2D(sTextureFront, vTexCoord); \n" + + " gl_FragColor.rgb = mix(gl_FragColor.rgb, \n" + + " uColorFront.rgb, uColorFront.a); \n" + + " } \n" + + " else { \n" + + " gl_FragColor = texture2D(sTextureBack, vTexCoord); \n" + + " gl_FragColor.rgb = mix(gl_FragColor.rgb, \n" + + " uColorBack.rgb, uColorBack.a); \n" + + " } \n" + + " gl_FragColor.rgb *= 0.5 + abs(normal.z) * 0.5; \n" + + "} \n"; + + public static final String SHADER_TEXTURE_VERTEX = "" + + "uniform mat4 uProjectionM; \n" + + "attribute vec3 aPosition; \n" + + "attribute vec3 aNormal; \n" + + "attribute vec2 aTexCoord; \n" + + "varying vec3 vNormal; \n" + + "varying vec2 vTexCoord; \n" + + "void main() { \n" + + " gl_Position = uProjectionM * vec4(aPosition, 1.0); \n" + + " vNormal = aNormal; \n" + + " vTexCoord = aTexCoord; \n" + + "} \n"; + +} diff --git a/src/fi/harism/curl/CurlView.java b/src/fi/harism/curl/CurlView.java index b016e61..75cbe69 100644 --- a/src/fi/harism/curl/CurlView.java +++ b/src/fi/harism/curl/CurlView.java @@ -1,5 +1,5 @@ /* - Copyright 2012 Harri Smatt + Copyright 2013 Harri Smatt Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import android.graphics.PointF; import android.graphics.RectF; import android.opengl.GLSurfaceView; +import android.os.SystemClock; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -38,6 +39,7 @@ public class CurlView extends GLSurfaceView implements View.OnTouchListener, private static final int CURL_RIGHT = 2; // Constants for mAnimationTargetEvent. + private static final int DO_NOTHING = 0; private static final int SET_CURL_TO_LEFT = 1; private static final int SET_CURL_TO_RIGHT = 2; @@ -49,7 +51,7 @@ public class CurlView extends GLSurfaceView implements View.OnTouchListener, private boolean mAllowLastPageCurl = true; private boolean mAnimate = false; - private long mAnimationDurationTime = 300; + private long mAnimationDurationTime; private PointF mAnimationSource = new PointF(); private long mAnimationStartTime; private PointF mAnimationTarget = new PointF(); @@ -79,6 +81,7 @@ public class CurlView extends GLSurfaceView implements View.OnTouchListener, private CurlMesh mPageRight; private PointerPosition mPointerPos = new PointerPosition(); + private PointF mPointerPosOld = new PointF(); private CurlRenderer mRenderer; private boolean mRenderLeftPage = true; @@ -123,6 +126,7 @@ public int getCurrentIndex() { */ private void init(Context ctx) { mRenderer = new CurlRenderer(this); + setEGLContextClientVersion(2); setRenderer(mRenderer); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); setOnTouchListener(this); @@ -141,10 +145,14 @@ private void init(Context ctx) { public void onDrawFrame() { // We are not animating. if (mAnimate == false) { + if (mCurlState != CURL_NONE) { + updateCurlPos(mPointerPos); + } return; } - long currentTime = System.currentTimeMillis(); + long currentTime = SystemClock.uptimeMillis(); + // If animation is done. if (currentTime >= mAnimationStartTime + mAnimationDurationTime) { if (mAnimationTargetEvent == SET_CURL_TO_RIGHT) { @@ -161,6 +169,7 @@ public void onDrawFrame() { if (mCurlState == CURL_LEFT) { --mCurrentIndex; } + mCurlState = CURL_NONE; } else if (mAnimationTargetEvent == SET_CURL_TO_LEFT) { // Switch curled page to left. CurlMesh left = mPageCurl; @@ -178,14 +187,15 @@ public void onDrawFrame() { if (mCurlState == CURL_RIGHT) { ++mCurrentIndex; } + mCurlState = CURL_NONE; } - mCurlState = CURL_NONE; mAnimate = false; requestRender(); } else { mPointerPos.mPos.set(mAnimationSource); - float t = 1f - ((float) (currentTime - mAnimationStartTime) / mAnimationDurationTime); - t = 1f - (t * t * t * (3 - 2 * t)); + float t = (float) (currentTime - mAnimationStartTime) + / mAnimationDurationTime; + t = t * t * (3 - 2 * t); mPointerPos.mPos.x += (mAnimationTarget.x - mAnimationSource.x) * t; mPointerPos.mPos.y += (mAnimationTarget.y - mAnimationSource.y) * t; updateCurlPos(mPointerPos); @@ -214,16 +224,17 @@ public void onSurfaceCreated() { // In case surface is recreated, let page meshes drop allocated texture // ids and ask for new ones. There's no need to set textures here as // onPageSizeChanged should be called later on. - mPageLeft.resetTexture(); - mPageRight.resetTexture(); - mPageCurl.resetTexture(); + mPageLeft.resetTextures(); + mPageRight.resetTextures(); + mPageCurl.resetTextures(); } @Override public boolean onTouch(View view, MotionEvent me) { // No dragging during animation at the moment. // TODO: Stop animation on touch event and return to drag mode. - if (mAnimate || mPageProvider == null) { + if ((mAnimate && mAnimationTargetEvent != DO_NOTHING) + || mPageProvider == null) { return false; } @@ -232,12 +243,13 @@ public boolean onTouch(View view, MotionEvent me) { RectF leftRect = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT); // Store pointer position. + mPointerPosOld.set(mPointerPos.mPos); mPointerPos.mPos.set(me.getX(), me.getY()); mRenderer.translate(mPointerPos.mPos); if (mEnableTouchPressure) { mPointerPos.mPressure = me.getPressure(); } else { - mPointerPos.mPressure = 0.8f; + mPointerPos.mPressure = 0.0f; } switch (me.getAction()) { @@ -299,9 +311,20 @@ else if (mDragStartPos.x >= rightRect.left if (mCurlState == CURL_NONE) { return false; } + + mAnimationSource.set(mDragStartPos); + mAnimationTarget.set(mPointerPos.mPos); + mAnimationStartTime = SystemClock.uptimeMillis(); + mAnimationDurationTime = 300; + mAnimationTargetEvent = DO_NOTHING; + mAnimate = true; + requestRender(); + break; } case MotionEvent.ACTION_MOVE: { - updateCurlPos(mPointerPos); + if (mAnimate) { + mAnimationTarget.set(mPointerPos.mPos); + } break; } case MotionEvent.ACTION_CANCEL: @@ -314,8 +337,9 @@ else if (mDragStartPos.x >= rightRect.left // result (which is easier done by altering curl position and/or // direction directly), this is done in a hope it made code a // bit more readable and easier to maintain. - mAnimationSource.set(mPointerPos.mPos); - mAnimationStartTime = System.currentTimeMillis(); + mAnimationSource.set(mPointerPosOld); + mAnimationStartTime = SystemClock.uptimeMillis(); + mAnimationDurationTime = 400; // Given the explanation, here we decide whether to simulate // drag to left or right end. @@ -550,7 +574,7 @@ private void startCurl(int page) { } } if (mCurrentIndex < mPageProvider.getPageCount() - 1) { - updatePage(mPageRight.getTexturePage(), mCurrentIndex + 1); + updatePage(mPageRight.getPage(), mCurrentIndex + 1); mPageRight.setRect(mRenderer .getPageRect(CurlRenderer.PAGE_RIGHT)); mPageRight.setFlipTexture(false); @@ -583,7 +607,7 @@ private void startCurl(int page) { mPageCurl = curl; if (mCurrentIndex > 1) { - updatePage(mPageLeft.getTexturePage(), mCurrentIndex - 2); + updatePage(mPageLeft.getPage(), mCurrentIndex - 2); mPageLeft.setFlipTexture(true); mPageLeft .setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); @@ -629,7 +653,7 @@ private void startCurl(int page) { private void updateCurlPos(PointerPosition pointerPos) { // Default curl radius. - double radius = mRenderer.getPageRect(CURL_RIGHT).width() / 3; + double radius = 0.3 * mRenderer.getPageRect(CURL_RIGHT).width(); // TODO: This is not an optimal solution. Based on feedback received so // far; pressure is not very accurate, it may be better not to map // coefficient to range [0f, 1f] but something like [.2f, 1f] instead. @@ -666,14 +690,7 @@ private void updateCurlPos(PointerPosition pointerPos) { // Actual curl position calculation. if (dist >= curlLen) { double translate = (dist - curlLen) / 2; - if (mViewMode == SHOW_TWO_PAGES) { - mCurlPos.x -= mCurlDir.x * translate / dist; - } else { - float pageLeftX = mRenderer - .getPageRect(CurlRenderer.PAGE_RIGHT).left; - radius = Math.max(Math.min(mCurlPos.x - pageLeftX, radius), - 0f); - } + mCurlPos.x -= mCurlDir.x * translate / dist; mCurlPos.y -= mCurlDir.y * translate / dist; } else { double angle = Math.PI * Math.sqrt(dist / curlLen); @@ -735,14 +752,14 @@ private void updatePages() { } if (rightIdx >= 0 && rightIdx < mPageProvider.getPageCount()) { - updatePage(mPageRight.getTexturePage(), rightIdx); + updatePage(mPageRight.getPage(), rightIdx); mPageRight.setFlipTexture(false); mPageRight.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)); mPageRight.reset(); mRenderer.addCurlMesh(mPageRight); } if (leftIdx >= 0 && leftIdx < mPageProvider.getPageCount()) { - updatePage(mPageLeft.getTexturePage(), leftIdx); + updatePage(mPageLeft.getPage(), leftIdx); mPageLeft.setFlipTexture(true); mPageLeft.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); mPageLeft.reset(); @@ -751,7 +768,7 @@ private void updatePages() { } } if (curlIdx >= 0 && curlIdx < mPageProvider.getPageCount()) { - updatePage(mPageCurl.getTexturePage(), curlIdx); + updatePage(mPageCurl.getPage(), curlIdx); if (mCurlState == CURL_RIGHT) { mPageCurl.setFlipTexture(true);