package com.wistron.WiGallery; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; import javax.microedition.khronos.opengles.GL10; import Utilities.CSStaticData; import android.util.Log; import android.view.MotionEvent; public class Sphere{ private static final String TAG = "Sphere"; private static float PI = 3.14159265f; private static float DTOR = PI / 90; private static float SCALE_Limit = 0.05f; private static ByteBuffer m_index_buff = null; private static FloatBuffer m_vertex_buff = null; private static ShortBuffer mVertexIndexBuffer = null; //地球: private static FloatBuffer mVertexNormalBuffer = null; //地球: private static FloatBuffer mVertexPositionBuffer = null; //地球:计算出来的原始数据 private static FloatBuffer mVertexCurPositionBuffer = null; //地球:用于缩放显示的 private static FloatBuffer mVertexTextureCoordBuffer = null; //地球: private static FloatBuffer mEmissionBuffer = null; //地球:自发光颜色 private static FloatBuffer mHaloVertexBuffer = null; //光晕:顶点的原始数据 private static FloatBuffer mHaloCurVertexBuffer = null; //光晕:顶点,用于缩放显示的 private static FloatBuffer mHaloCoordBuffer = null; //光晕:贴图坐标 private static ShortBuffer mHaloIndexBuffer = null; //光晕:索引 private List<Element> mLandmarkerList = new ArrayList<Element>(); private OnScaleListener mOnScaleListener = null; private NewtonTheory mDecelor = new NewtonTheory(); private boolean mIsTouching = false; private static float rot = 0; //自转角度 private static float mRadio = 0.8f; //球半径 private static float mTmpRadio = 0.8f; private static float mRadioOffset = 0.3f; //贴图半径偏移 private static float mScaleRate = 0.8f; //缩放比率 private static float mMinScale = 0.8f, //缩放比率下限 mMaxScale = 14.0f; //缩放比率上限 private static float mIconMinScale = 0.1f, //图标缩放比率下限 mIconMaxScale = 1.0f; //图标缩放比率上限 private static float mHaloScale = 1.0f; //光晕比地球的放大倍数 private int mSphereBG = 0; //背景贴图 private float mOldX = 0, mOldY = 0; private boolean mAllowScroll = false; private boolean mLaunchAnim = true; private int mDtheta = 10, // 纬度 mDphi = 10; // 经度 private float mXRot = 0, mYRot = 0, mZRot = 0; public Sphere() { initVertex(mRadio); mDecelor.setScaleLimit(mMinScale, mMaxScale); } private void initVertex(float sphereRadio){ int latitudeBands = 60; //纬线 int longitudeBands = 60; //经线 float radio = sphereRadio; //球体半径 int vpdItr = 0; //VertexPosData迭代器 int nmdItr = 0; //NormalData迭代器 int tcdItr = 0; //TextureCoordData迭代器 int idxItr = 0; //IndexData迭代器 int unitDataSize = (latitudeBands + 1) * (longitudeBands + 1); float[] vertexPosData = new float[3 * unitDataSize]; //顶点坐标 float[] normalData = new float[3 * unitDataSize]; //单位向量 float[] textureCoordData = new float[2 * unitDataSize]; //贴图坐标 short[] indexData = new short[6 * latitudeBands * longitudeBands]; //索引向量 float[] haloVert = null; //光晕顶点 float[] haloCoord = null; //光晕贴图 short[] haloIndex = null; //光晕索引 float[] emission = new float[]{0.3f, 0.3f, 1.0f, 0.8f}; //自发光颜色 //生成光晕坐标 haloVert = new float[]{ -(mRadio + 0.25f)*mHaloScale, -(mRadio + 0.25f)*mHaloScale, -0.15f, (mRadio + 0.25f)*mHaloScale, -(mRadio + 0.25f)*mHaloScale, -0.15f, -(mRadio + 0.25f)*mHaloScale, (mRadio + 0.25f)*mHaloScale, -0.15f, (mRadio + 0.25f)*mHaloScale, (mRadio + 0.25f)*mHaloScale, -0.15f }; //生成光晕贴图坐标 haloCoord = new float[]{ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; //生成光晕索引 haloIndex = new short[]{ 0, 1, 2, 3 }; //生成地球坐标 for(int latNum = 0; latNum <= latitudeBands; latNum ++){ //纬线圈 float theta = (float) (latNum * Math.PI / latitudeBands); float sinTheta = (float) Math.sin(theta); float cosTheta = (float) Math.cos(theta); for(int longNum = 0; longNum <= longitudeBands; longNum ++){ //经线圈 float phi = (float) (longNum * 2 *Math.PI / longitudeBands); float sinPhi = (float) Math.sin(phi); float cosPhi = (float) Math.cos(phi); float x = cosPhi * sinTheta; float y = cosTheta; float z = sinPhi * sinTheta; float u = 1f - ((float)longNum / (float)longitudeBands); float v = 1f - ((float)latNum / (float)latitudeBands); normalData[nmdItr] = x; nmdItr ++; normalData[nmdItr] = y; nmdItr ++; normalData[nmdItr] = z; nmdItr ++; textureCoordData[tcdItr] = u; tcdItr ++; textureCoordData[tcdItr] = v; tcdItr ++; vertexPosData[vpdItr] = radio * x; vpdItr ++; vertexPosData[vpdItr] = radio * y; vpdItr ++; vertexPosData[vpdItr] = radio * z; vpdItr ++; } } //生成地球索引 for(int latNum = 0; latNum < latitudeBands; latNum ++){ for(int longNum = 0; longNum < longitudeBands; longNum ++){ int first = (latNum * (longitudeBands + 1)) + longNum; int second = first + longitudeBands + 1; indexData[idxItr] = (short) first; idxItr ++; indexData[idxItr] = (short) second; idxItr ++; indexData[idxItr] = (short) (first + 1); idxItr ++; indexData[idxItr] = (short) second; idxItr ++; indexData[idxItr] = (short) (second + 1); idxItr ++; indexData[idxItr] = (short) (first + 1); idxItr ++; } } //生成缓存 if(mVertexIndexBuffer != null){ mVertexIndexBuffer.clear(); } if(mVertexNormalBuffer != null){ mVertexNormalBuffer.clear(); } if(mVertexPositionBuffer != null){ mVertexPositionBuffer.clear(); } if(mVertexTextureCoordBuffer != null){ mVertexTextureCoordBuffer.clear(); } if(mHaloIndexBuffer != null){ mHaloIndexBuffer.clear(); } if(mHaloVertexBuffer != null){ mHaloVertexBuffer.clear(); } if(mEmissionBuffer != null){ mEmissionBuffer.clear(); } mEmissionBuffer = ResourceManager.makeFloatBuffer(emission); mHaloIndexBuffer = ResourceManager.makeShortBuffer(haloIndex); mHaloCoordBuffer = ResourceManager.makeFloatBuffer(haloCoord); mHaloVertexBuffer = ResourceManager.makeFloatBuffer(haloVert); mVertexIndexBuffer = ResourceManager.makeShortBuffer(indexData); mVertexNormalBuffer = ResourceManager.makeFloatBuffer(normalData); mVertexPositionBuffer = ResourceManager.makeFloatBuffer(vertexPosData); mVertexTextureCoordBuffer = ResourceManager.makeFloatBuffer(textureCoordData); //给出显示顶点 if(mVertexCurPositionBuffer != null){ mVertexCurPositionBuffer.clear(); mVertexCurPositionBuffer = null; } if(mHaloCurVertexBuffer != null){ mHaloCurVertexBuffer.clear(); mHaloCurVertexBuffer = null; } System.gc(); mVertexCurPositionBuffer = ResourceManager.makeFloatBuffer(vertexPosData); mHaloCurVertexBuffer = ResourceManager.makeFloatBuffer(haloVert); //缩放到最小 onScale(1f); } /** * 对球面进行缩放 * * @param scale 1 == 标准; < 1 缩小; > 1 放大 */ public void onScale(float scale) { mScaleRate = scale; //限制缩放比 mTmpRadio *= mScaleRate; if (mTmpRadio - mMinScale < 0) { mTmpRadio = mMinScale; } if(mTmpRadio - mMaxScale > 0){ mTmpRadio = mMaxScale; } if(Math.abs(mTmpRadio - mRadio) < SCALE_Limit){ return; } //设定缩放级别:一共4个级别 if(mTmpRadio < (mMaxScale - mMinScale)*0.25 + mMinScale){ //不要写 tmpRadio >= mMinScale mDtheta = 10; mDphi = 10; }else if(mTmpRadio >= (mMaxScale - mMinScale)*0.25 + mMinScale && mRadio < (mMaxScale - mMinScale)*0.5 + mMinScale){ mDtheta = 8; mDphi = 8; }else if(mTmpRadio >= (mMaxScale - mMinScale)*0.5 + mMinScale && mRadio < (mMaxScale - mMinScale)*0.75 + mMinScale){ mDtheta = 6; mDphi = 6; }else if(mTmpRadio >= (mMaxScale - mMinScale)*0.75 + mMinScale){ //不要写 tmpRadio <= mMaxScale mDtheta = 4; mDphi = 4; } //指定平滑缩放 mDecelor.setSmoothScale(mRadio, mTmpRadio); } public boolean onFlingEvent(float velocityX, float velocityY) { //加速度变换器 mDecelor.setStartVelocity(velocityY/(6*mRadio), 0); mDecelor.setStartVelocity(velocityX/(6*mRadio), 1); return true; } public boolean onTouchEvent(float xDistance, float yDistance) { mXRot -= (yDistance/6f)/mRadio; //使地球在任何倍数下都尽量紧跟手指的位置来滑动 mYRot -= (xDistance/6f)/mRadio; //地球半径越大,滑动越慢,反之则滑动越快 mXRot = mXRot % 360f; mYRot = mYRot % 360f; mZRot = mZRot % 360f; return true; } public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mAllowScroll = true; mOldX = event.getX(); mOldY = event.getY(); break; case MotionEvent.ACTION_MOVE: mXRot += event.getX() - mOldX; mYRot += event.getY() - mOldY; mOldX = event.getX(); mOldY = event.getY(); break; case MotionEvent.ACTION_UP: default: mAllowScroll = false; break; } return true; } public void onDraw(GL10 gl, List<ElementList> list) { if(mLaunchAnim){ //触发开场动画 onFlingEvent(6000, 0); mLaunchAnim = false; } if(CSStaticData.EARTH_AUTO_ROTATE){ //地球自转 mYRot = (float) ((0.5f + mYRot) % 360f); } //加速度变换器 mXRot = mXRot + mDecelor.getDecelerator(0); mYRot = mYRot + mDecelor.getDecelerator(1); mZRot = mZRot + mDecelor.getDecelerator(2); //缩放阻尼变换器 float[] temp = new float[1]; if(mDecelor.getSmoothScaleSin(temp)){ if(temp != null || temp.length == 1){ mRadio = temp[0]; if(mOnScaleListener != null){ mOnScaleListener.onScaleChanged(mRadio); } } sphereScale(); } //限制mXRot旋转 float includeAngle = mXRot % 360; if(includeAngle > 60){ mXRot = 60; } if(includeAngle < -60){ mXRot = -60; } //绘制 CreateUnitSphere(gl, mDtheta, mDphi, mXRot, mYRot, mZRot); drawIcon(gl, list); // 测试 } public GPSPointInfo getGPSPoint(double latitude, double longitude){ return new GPSPointInfo(latitude, longitude, (mRadio + mRadioOffset)*(1f - 0.175f), new float[]{0, 0, 0}); } private void drawIcon(GL10 gl, List<ElementList> list) { float scale = mScaleRate; if(scale - mIconMinScale < 0){ scale = mIconMinScale; } if(scale - mIconMaxScale > 0){ scale = mIconMaxScale; } gl.glRotatef(mXRot, 1.0f, 0.0f, 0.0f); gl.glRotatef(mYRot, 0.0f, 1.0f, 0.0f); gl.glRotatef(mZRot, 0.0f, 0.0f, 1.0f); //修正Icon方向 for(Element itr : mLandmarkerList){ GPSPointInfo gps = getGPSPoint(itr.m_latitude, itr.m_longitude); gl.glPushMatrix(); gl.glTranslatef(gps.getGPSPoint()[0], gps.getGPSPoint()[1], gps.getGPSPoint()[2]); // gl.glRotatef(gps.getZTheata(), 0f, 0f, 1f); //不能转Z轴,也不能把Z轴转为0 gl.glRotatef(gps.getYTheata(), 0f, 1f, 0f); gl.glRotatef(gps.getXTheata(), 1f, 0f, 0f); gl.glScalef(scale * 0.175f, scale * 0.175f, scale * 0.175f); gl.glPushMatrix(); gl.glRotatef(90f, 1.0f, 0.0f, 0.0f); itr.onDraw(gl, true); gl.glPopMatrix(); gl.glPopMatrix(); } ///////GroupList.draw for (int i = 0; i < list.size(); i++) { ElementList elist = list.get(i); if (elist.getName().equalsIgnoreCase("Unknown")) { continue; } Element elem = elist.get(i); if(elem != null){ double longitude = elem.m_longitude; double latitude = elem.m_latitude; float[] pos = GetPositionAndAngle(latitude, longitude); elist.moveTo(pos[0], pos[1], pos[2]); elist.setAngleTo(pos[3], pos[4], pos[5]); elist.onDraw(gl, MEDIA_VIEW.LEFT_VIEW); } } /////// } public float[] GetPositionAndAngle(double latitude, double longitude) { float scale = mScaleRate; if(scale - mIconMinScale < 0){ scale = mIconMinScale; } if(scale - mIconMaxScale > 0){ scale = mIconMaxScale; } GPSPointInfo gps = getGPSPoint(latitude, longitude); float[] pos = {0,0,0, 0, 0, 0}; pos[0] = gps.getGPSPoint()[0]; pos[1] = gps.getGPSPoint()[1]; pos[2] = gps.getGPSPoint()[2]; pos[3] = gps.getXTheata(); pos[4] = gps.getYTheata(); pos[5] = gps.getZTheata(); return pos; } static public void CreateUnitSphere(GL10 gl, int dtheta, int dphi, float xRot, float yRot, float zRot) { gl.glPushMatrix(); gl.glBindTexture(GL10.GL_TEXTURE_2D, WiGalleryOpenGLRenderer.m_earth_overlay_id); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mHaloCurVertexBuffer); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mHaloCoordBuffer); gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, mHaloIndexBuffer.capacity(), GL10.GL_UNSIGNED_SHORT, mHaloIndexBuffer); gl.glPopMatrix(); gl.glPushMatrix(); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // 旋转效果 gl.glRotatef(xRot, 1.0f, 0.0f, 0.0f); gl.glRotatef(yRot, 0.0f, 1.0f, 0.0f); gl.glRotatef(zRot, 0.0f, 0.0f, 1.0f); gl.glEnable(GL10.GL_DEPTH_TEST); //gl.glEnableClientState(GL_NORMAL_ARRAY); //允许激活法线数组 //gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY); //允许激活贴图数组 gl.glBindTexture(GL10.GL_TEXTURE_2D, WiGalleryOpenGLRenderer.m_earth_background_id); //gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); //gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexCurPositionBuffer); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mVertexTextureCoordBuffer); gl.glNormalPointer(GL10.GL_FLOAT, 0, mVertexNormalBuffer); //设置法线数组,用于光照处理 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_EMISSION, mEmissionBuffer); //自发光 gl.glDrawElements(GL10.GL_TRIANGLES, mVertexIndexBuffer.capacity(), GL10.GL_UNSIGNED_SHORT, mVertexIndexBuffer); gl.glPopMatrix(); } public void sphereScale(){ // 重刷半径 if(mVertexCurPositionBuffer == null){ mVertexCurPositionBuffer = FloatBuffer.allocate(mVertexPositionBuffer.capacity()); } if(mHaloCurVertexBuffer == null){ mHaloCurVertexBuffer = FloatBuffer.allocate(mHaloVertexBuffer.capacity()); } for (int i = 0; i < mVertexPositionBuffer.capacity(); i++){ mVertexCurPositionBuffer.put(i, mVertexPositionBuffer.get(i) * mRadio); } for (int i = 0; i < mHaloVertexBuffer.capacity(); i++){ mHaloCurVertexBuffer.put(i, mHaloVertexBuffer.get(i) * mRadio); } } public void addLandmarker(Element elem){ mLandmarkerList.add(elem); } public static float getMinScale() { return mMinScale; } public static float getMaxScale() { return mMaxScale; } public void clearLandmarker(){ mLandmarkerList.clear(); } public void setOnScaleListener(OnScaleListener listener){ mOnScaleListener = listener; } public interface OnScaleListener{ public void onScaleChanged(float rate); } public void isTouching(boolean touching) { mIsTouching = touching; } }