zoukankan      html  css  js  c++  java
  • 几何球

    效果图如下:




    1、首先给出的是用于初始化几何球顶点坐标数据的initvertexData方法:

     <pre name="code" class="java">  
        //自定义的初始化顶点数据的方法
        public void initVertexData(float scale, float aHalf, int n) //大小,黄金长方形长边的一半,分段数
    	{
    		aHalf*=scale;		//长边的一半
    		bHalf=aHalf*0.618034f;		//短边的一半
    		r=(float) Math.sqrt(aHalf*aHalf+bHalf*bHalf);
    		vCount=3*20*n*n;//顶点个数,共有20个三角形,每个三角形都有三个顶点
    		//正20面体坐标数据初始化
    		ArrayList<Float> alVertix20=new ArrayList<Float>();//正20面体的顶点列表(未卷绕)
    		ArrayList<Integer> alFaceIndex20=new ArrayList<Integer>();//正20面体组织成面的顶点的索引值列表(按逆时针卷绕)
    		//正20面体顶点
    		initAlVertix20(alVertix20,aHalf,bHalf);
    		//正20面体索引
    		initAlFaceIndex20(alFaceIndex20);
    		//计算卷绕顶点
    		float[] vertices20=VectorUtil.cullVertex(alVertix20, alFaceIndex20);//只计算顶点
    
    		//坐标数据初始化
    		ArrayList<Float> alVertix=new ArrayList<Float>();//原顶点列表(未卷绕)
    		ArrayList<Integer> alFaceIndex=new ArrayList<Integer>();//组织成面的顶点的索引值列表(按逆时针卷绕)
    		int vnCount=0;//前i-1行前所有顶点数的和
    		for(int k=0;k<vertices20.length;k+=9)//对正20面体每个大三角形循环
    		{
    			float [] v1=new float[]{vertices20[k+0], vertices20[k+1], vertices20[k+2]};
    			float [] v2=new float[]{vertices20[k+3], vertices20[k+4], vertices20[k+5]};
    			float [] v3=new float[]{vertices20[k+6], vertices20[k+7], vertices20[k+8]};
    			//顶点
    			for(int i=0;i<=n;i++)
    			{
    				float[] viStart=VectorUtil.devideBall(r, v1, v2, n, i);
    				float[] viEnd=VectorUtil.devideBall(r, v1, v3, n, i);
    				for(int j=0;j<=i;j++)
    				{
    					float[] vi=VectorUtil.devideBall(r, viStart, viEnd, i, j);
    					alVertix.add(vi[0]); alVertix.add(vi[1]); alVertix.add(vi[2]);
    				}
    			}
    			//索引
    			for(int i=0;i<n;i++)
    			{
    				if(i==0){//若是第0行,直接加入卷绕后顶点索引012
    					alFaceIndex.add(vnCount+0); alFaceIndex.add(vnCount+1);alFaceIndex.add(vnCount+2);
    					vnCount+=1;
    					if(i==n-1){//如果是每个大三角形的最后一次循环,将下一列的顶点个数也加上
    						vnCount+=2;
    					}
    					continue;
    				}
    				int iStart=vnCount;//第i行开始的索引
    				int viCount=i+1;//第i行顶点数
    				int iEnd=iStart+viCount-1;//第i行结束索引
    				
    				int iStartNext=iStart+viCount;//第i+1行开始的索引
    				int viCountNext=viCount+1;//第i+1行顶点数
    				int iEndNext=iStartNext+viCountNext-1;//第i+1行结束的索引
    				//前面的四边形
    				for(int j=0;j<viCount-1;j++)
    				{
    					int index0=iStart+j;//四边形的四个顶点索引
    					int index1=index0+1;
    					int index2=iStartNext+j;
    					int index3=index2+1;
    					alFaceIndex.add(index0); alFaceIndex.add(index2);alFaceIndex.add(index3);//加入前面的四边形
    					alFaceIndex.add(index0); alFaceIndex.add(index3);alFaceIndex.add(index1);				
    				}// j
    				alFaceIndex.add(iEnd); alFaceIndex.add(iEndNext-1);alFaceIndex.add(iEndNext); //最后一个三角形
    				vnCount+=viCount;//第i行前所有顶点数的和
    				if(i==n-1){//如果是每个大三角形的最后一次循环,将下一列的顶点个数也加上
    					vnCount+=viCountNext;
    				}
    			}// i
    		}// k
    		
    		//计算卷绕顶点
    		float[] vertices=VectorUtil.cullVertex(alVertix, alFaceIndex);//只计算顶点
    		float[] normals=vertices;//顶点就是法向量
    		
    		//纹理
    		//正20面体纹理坐标数据初始化
    		ArrayList<Float> alST20=new ArrayList<Float>();//正20面体的纹理坐标列表(未卷绕)
    		ArrayList<Integer> alTexIndex20=new ArrayList<Integer>();//正20面体组织成面的纹理坐标的索引值列表(按逆时针卷绕)
    		//正20面体纹理坐标
    		float sSpan=1/5.5f;//每个纹理三角形的边长
    		float tSpan=1/3.0f;//每个纹理三角形的高
    		//按正二十面体的平面展开图计算纹理坐标
    		for(int i=0;i<5;i++){
    			alST20.add(sSpan+sSpan*i); alST20.add(0f);
    		}
    		for(int i=0;i<6;i++){
    			alST20.add(sSpan/2+sSpan*i); alST20.add(tSpan);
    		}
    		for(int i=0;i<6;i++){
    			alST20.add(sSpan*i); alST20.add(tSpan*2);
    		}
    		for(int i=0;i<5;i++){
    			alST20.add(sSpan/2+sSpan*i); alST20.add(tSpan*3);
    		}
    		//正20面体索引
    		initAlTexIndex20(alTexIndex20);
    
    		//计算卷绕纹理坐标
    		float[] st20=VectorUtil.cullTexCoor(alST20, alTexIndex20);//只计算纹理坐标
    		ArrayList<Float> alST=new ArrayList<Float>();//原纹理坐标列表(未卷绕)
    		for(int k=0;k<st20.length;k+=6)
    		{
    			float [] st1=new float[]{st20[k+0], st20[k+1], 0};//三角形的纹理坐标
    			float [] st2=new float[]{st20[k+2], st20[k+3], 0};
    			float [] st3=new float[]{st20[k+4], st20[k+5], 0};
    			for(int i=0;i<=n;i++)
    			{
    				float[] stiStart=VectorUtil.devideLine(st1, st2, n, i);
    				float[] stiEnd=VectorUtil.devideLine(st1, st3, n, i);
    				for(int j=0;j<=i;j++)
    				{
    					float[] sti=VectorUtil.devideLine(stiStart, stiEnd, i, j);
    					//将纹理坐标加入列表
    					alST.add(sti[0]); alST.add(sti[1]);
    				}
    			}
    		}
    		//计算卷绕后纹理坐标
    		float[] textures=VectorUtil.cullTexCoor(alST, alFaceIndex);
    		
    		//顶点坐标数据初始化
    		ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);//创建顶点坐标数据缓冲
            vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
            mVertexBuffer = vbb.asFloatBuffer();//转换为float型缓冲
            mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
            mVertexBuffer.position(0);//设置缓冲区起始位置
            //法向量数据初始化  
            ByteBuffer nbb = ByteBuffer.allocateDirect(normals.length*4);//创建顶点法向量数据缓冲
            nbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
            mNormalBuffer = nbb.asFloatBuffer();//转换为float型缓冲
            mNormalBuffer.put(normals);//向缓冲区中放入顶点法向量数据
            mNormalBuffer.position(0);//设置缓冲区起始位置
            //st坐标数据初始化		
            ByteBuffer tbb = ByteBuffer.allocateDirect(textures.length*4);//创建顶点纹理数据缓冲
            tbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
            mTexCoorBuffer = tbb.asFloatBuffer();//转换为float型缓冲
            mTexCoorBuffer.put(textures);//向缓冲区中放入顶点纹理数据
            mTexCoorBuffer.position(0);//设置缓冲区起始位置
    	}
        

    
    2、接着给出的是初始化正二十面体顶点坐标数据的initAI20方法:
    

      public void initAlVertix20(ArrayList<Float> alVertix20,float aHalf,float bHalf){
        	
    		alVertix20.add(0f); alVertix20.add(aHalf); alVertix20.add(-bHalf);//顶正棱锥顶点
    		
    		alVertix20.add(0f); alVertix20.add(aHalf); alVertix20.add(bHalf);//棱柱上的点
    		alVertix20.add(aHalf); alVertix20.add(bHalf); alVertix20.add(0f);
    		alVertix20.add(bHalf); alVertix20.add(0f); alVertix20.add(-aHalf);
    		alVertix20.add(-bHalf); alVertix20.add(0f); alVertix20.add(-aHalf);
    		alVertix20.add(-aHalf); alVertix20.add(bHalf); alVertix20.add(0f);
    		
    		alVertix20.add(-bHalf); alVertix20.add(0f); alVertix20.add(aHalf);
    		alVertix20.add(bHalf); alVertix20.add(0f); alVertix20.add(aHalf);
    		alVertix20.add(aHalf); alVertix20.add(-bHalf); alVertix20.add(0f);
    		alVertix20.add(0f); alVertix20.add(-aHalf); alVertix20.add(-bHalf);
    		alVertix20.add(-aHalf); alVertix20.add(-bHalf); alVertix20.add(0f);
    		
    		alVertix20.add(0f); alVertix20.add(-aHalf); alVertix20.add(bHalf);//底棱锥顶点
    		
        }

    3、初始化构成正二十面体的20个三角形顶点坐标编号数据的initAIfaceIndex20的方法:

     public void initAlFaceIndex20(ArrayList<Integer> alFaceIndex20){ //初始化正二十面体的顶点索引数据
        	
    		alFaceIndex20.add(0); alFaceIndex20.add(1); alFaceIndex20.add(2);
    		alFaceIndex20.add(0); alFaceIndex20.add(2); alFaceIndex20.add(3);
    		alFaceIndex20.add(0); alFaceIndex20.add(3); alFaceIndex20.add(4);
    		alFaceIndex20.add(0); alFaceIndex20.add(4); alFaceIndex20.add(5);
    		alFaceIndex20.add(0); alFaceIndex20.add(5); alFaceIndex20.add(1);
    		
    		alFaceIndex20.add(1); alFaceIndex20.add(6); alFaceIndex20.add(7);
    		alFaceIndex20.add(1); alFaceIndex20.add(7); alFaceIndex20.add(2);
    		alFaceIndex20.add(2); alFaceIndex20.add(7); alFaceIndex20.add(8);
    		alFaceIndex20.add(2); alFaceIndex20.add(8); alFaceIndex20.add(3);
    		alFaceIndex20.add(3); alFaceIndex20.add(8); alFaceIndex20.add(9);
    		alFaceIndex20.add(3); alFaceIndex20.add(9); alFaceIndex20.add(4);
    		alFaceIndex20.add(4); alFaceIndex20.add(9); alFaceIndex20.add(10);
    		alFaceIndex20.add(4); alFaceIndex20.add(10); alFaceIndex20.add(5);
    		alFaceIndex20.add(5); alFaceIndex20.add(10); alFaceIndex20.add(6);
    		alFaceIndex20.add(5); alFaceIndex20.add(6); alFaceIndex20.add(1);
    		
    		alFaceIndex20.add(6); alFaceIndex20.add(11); alFaceIndex20.add(7);
    		alFaceIndex20.add(7); alFaceIndex20.add(11); alFaceIndex20.add(8);
    		alFaceIndex20.add(8); alFaceIndex20.add(11); alFaceIndex20.add(9);
    		alFaceIndex20.add(9); alFaceIndex20.add(11); alFaceIndex20.add(10);
    		alFaceIndex20.add(10); alFaceIndex20.add(11); alFaceIndex20.add(6);
        }

    4、初始化构成正二十面体的20个三角形各个顶点坐标编号数据的initAIfaceIndex20的方法:


     public void initAlTexIndex20(ArrayList<Integer> alTexIndex20) //初始化顶点纹理索引数据
        {
    		alTexIndex20.add(0); alTexIndex20.add(5); alTexIndex20.add(6);
    		alTexIndex20.add(1); alTexIndex20.add(6); alTexIndex20.add(7);
    		alTexIndex20.add(2); alTexIndex20.add(7); alTexIndex20.add(8);
    		alTexIndex20.add(3); alTexIndex20.add(8); alTexIndex20.add(9);
    		alTexIndex20.add(4); alTexIndex20.add(9); alTexIndex20.add(10);
    		
    		alTexIndex20.add(5); alTexIndex20.add(11); alTexIndex20.add(12);
    		alTexIndex20.add(5); alTexIndex20.add(12); alTexIndex20.add(6);
    		alTexIndex20.add(6); alTexIndex20.add(12); alTexIndex20.add(13);
    		alTexIndex20.add(6); alTexIndex20.add(13); alTexIndex20.add(7);
    		alTexIndex20.add(7); alTexIndex20.add(13); alTexIndex20.add(14);
    		alTexIndex20.add(7); alTexIndex20.add(14); alTexIndex20.add(8);
    		alTexIndex20.add(8); alTexIndex20.add(14); alTexIndex20.add(15);
    		alTexIndex20.add(8); alTexIndex20.add(15); alTexIndex20.add(9);
    		alTexIndex20.add(9); alTexIndex20.add(15); alTexIndex20.add(16);
    		alTexIndex20.add(9); alTexIndex20.add(16); alTexIndex20.add(10);
    		
    		alTexIndex20.add(11); alTexIndex20.add(17); alTexIndex20.add(12);
    		alTexIndex20.add(12); alTexIndex20.add(18); alTexIndex20.add(13);
    		alTexIndex20.add(13); alTexIndex20.add(19); alTexIndex20.add(14);
    		alTexIndex20.add(14); alTexIndex20.add(20); alTexIndex20.add(15);
    		alTexIndex20.add(15); alTexIndex20.add(21); alTexIndex20.add(16);
        	
        }
        

    5、最后给出的是向量计算及圆弧拆分相关方法所属的工具类——VectorUtil:

    import java.util.ArrayList;
    
    //计算三角形法向量的工具类
    public class VectorUtil {
    	//向量规格化的方法
    	public static float[] normalizeVector(float [] vec){
    		float mod=module(vec);
    		return new float[]{vec[0]/mod, vec[1]/mod, vec[2]/mod};//返回规格化后的向量
    	}
    	//求向量的模的方法
    	public static float module(float [] vec){
    		return (float) Math.sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]);
    	}
    	//两个向量叉乘的方法
    	public static float[] crossTwoVectors(float[] a, float[] b)
    	{
    		float x=a[1]*b[2]-a[2]*b[1];
    		float y=a[2]*b[0]-a[0]*b[2];
    		float z=a[0]*b[1]-a[1]*b[0];
    		return new float[]{x, y, z};//返回叉乘结果
    	}
    	//两个向量点乘的方法
    	public static float dotTwoVectors(float[] a, float[] b)
    	{
    		return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];//返回点乘结果
    	}
    
    	//根据原纹理坐标和索引,计算卷绕后的纹理的方法
    	public static float[] cullTexCoor(
    			ArrayList<Float> alST,//原纹理坐标列表(未卷绕)
    			ArrayList<Integer> alTexIndex//组织成面的纹理坐标的索引值列表(按逆时针卷绕)
    			)
    	{
    		float[] textures=new float[alTexIndex.size()*2];
    		//生成顶点的数组
    		int stCount=0;
    		for(int i:alTexIndex){
    			textures[stCount++]=alST.get(2*i);
    			textures[stCount++]=alST.get(2*i+1);
    		}
    		return textures;
    	}
    	public static float[] cullVertex(
    			ArrayList<Float> alv,//原顶点列表(未卷绕)
    			ArrayList<Integer> alFaceIndex//组织成面的顶点的索引值列表(按逆时针卷绕)
    			)
    	{
    		float[] vertices=new float[alFaceIndex.size()*3];
    		//生成顶点的数组
    		int vCount=0;
    		for(int i:alFaceIndex){
    			vertices[vCount++]=alv.get(3*i);
    			vertices[vCount++]=alv.get(3*i+1);
    			vertices[vCount++]=alv.get(3*i+2);
    		}
    		return vertices;
    	}
    	//计算圆弧的n等分点坐标的方法
    	public static float[] devideBall(
    			float r, //球的半径
    			float[] start, //指向圆弧起点的向量
    			float[] end, //指向圆弧终点的向量
    			int n, //圆弧分的份数
    			int i //求第i份在圆弧上的坐标(i为0和n时分别代表起点和终点坐标)
    			)
    	{
    		/* 
    		 * 先求出所求向量的规格化向量,再乘以半径r即可
    		 * s0*x+s1*y+s2*z=cos(angle1)//根据所求向量和起点向量夹角为angle1---1式
    		 * e0*x+e1*y+e2*z=cos(angle2)//根据所求向量和终点向量夹角为angle2---2式
    		 * x*x+y*y+z*z=1//所球向量的规格化向量模为1---3式
    		 * x*n0+y*n1+z*n2=0//所球向量与法向量垂直---4式
    		 * 算法为:将1、2两式用换元法得出x=a1+b1*z,y=a2+b2*z的形式,
    		 * 将其代入4式求出z,再求出x、y,最后将向量(x,y,z)乘以r即为所求坐标。
    		 * 1式和2式是将3式代入得到的,因此已经用上了。
    		 * 由于叉乘的结果做了分母,因此起点、终点、球心三点不能共线
    		 * 注意结果是将劣弧等分
    		 */
    		//先将指向起点和终点的向量规格化
    		float[] s=VectorUtil.normalizeVector(start);
    		float[] e=VectorUtil.normalizeVector(end);
    		if(n==0){//如果n为零,返回起点坐标
    			return new float[]{s[0]*r, s[1]*r, s[2]*r};
    		}
    		//求两个向量的夹角
    		double angrad=Math.acos(VectorUtil.dotTwoVectors(s, e));//起点终点向量夹角
    		double angrad1=angrad*i/n;//所球向量和起点向量的夹角
    		double angrad2=angrad-angrad1;//所球向量和终点向量的夹角
    		//求法向量normal
    		float[] normal=VectorUtil.crossTwoVectors(s, e);
    		//用doolittle分解算法解n元一次线性方程组
    		double matrix[][]={//增广矩阵
    				{s[0],s[1],s[2],Math.cos(angrad1)},
    				{e[0],e[1],e[2],Math.cos(angrad2)},
    				{normal[0],normal[1],normal[2],0}  
    		};
    		double result[]=MyMathUtil.doolittle(matrix);//解
    		//求规格化向量xyz的值
    		float x=(float) result[0];
    		float y=(float) result[1];
    		float z=(float) result[2];
    		//返回圆弧的n等分点坐标
    		return new float[]{x*r, y*r, z*r};
    	}
    	//计算线段的n等分点坐标的方法
    	public static float[] devideLine(
    			float[] start, //线段起点坐标
    			float[] end, //线段终点坐标
    			int n, //线段分的份数
    			int i //求第i份在线段上的坐标(i为0和n时分别代表起点和终点坐标)
    			)
    	{
    		if(n==0){//如果n为零,返回起点坐标
    			return start;
    		}
    		//求起点到终点的向量
    		float[] ab=new float[]{end[0]-start[0], end[1]-start[1], end[2]-start[2]};
    		//求向量比例
    		float vecRatio=i/(float)n;
    		//求起点到所求点的向量
    		float[] ac=new float[]{ab[0]*vecRatio, ab[1]*vecRatio, ab[2]*vecRatio};
    		//所求坐标
    		float x=start[0]+ac[0];
    		float y=start[1]+ac[1];
    		float z=start[2]+ac[2];
    		//返回线段的n等分点坐标
    		return new float[]{x, y, z};
    	}
    }
    




  • 相关阅读:
    shell进阶——expect免交互工具的使用
    SSH非交互式密码授权远程执行脚本---转载
    github新项目上传操作(下面的例子以centos7-redis-shell项目为实例)
    camstar跨平台开发
    C/C++语言中的int所能表示的最大值最小值
    剑指offer 15:二进制中1的个数
    剑指offer33:二叉搜索树的后序遍历序列
    剑指offer31:栈的压入弹出序列
    剑指offer:包含min函数的栈
    剑指offer50拓展:字符流中第一个不重复的字符
  • 原文地址:https://www.cnblogs.com/Anzhongliu/p/6092110.html
Copyright © 2011-2022 走看看