Revision 91cfe462
Added by Leszek Koltunski over 6 years ago
| src/main/java/org/distorted/library/mesh/MeshCubes.java | ||
|---|---|---|
| 19 | 19 |
|
| 20 | 20 |
package org.distorted.library.mesh; |
| 21 | 21 |
|
| 22 |
import org.distorted.library.type.Static4D; |
|
| 23 |
|
|
| 22 | 24 |
import java.util.ArrayList; |
| 23 | 25 |
|
| 24 | 26 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 25 | 27 |
/** |
| 26 | 28 |
* Create a 3D grid composed of Cubes. |
| 27 | 29 |
* <p> |
| 28 |
* Any subset of a MxNx1 cuboid is possible. |
|
| 30 |
* Any subset of a MxNx1 cuboid is possible. (repeated arbitrary number of times into Z-dir)
|
|
| 29 | 31 |
*/ |
| 30 | 32 |
public class MeshCubes extends MeshBase |
| 31 | 33 |
{
|
| 32 | 34 |
private static final float R = 0.0f;//0.2f; |
| 33 | 35 |
|
| 36 |
private static final int FRONT = 0; |
|
| 37 |
private static final int BACK = 1; |
|
| 38 |
private static final int LEFT = 2; |
|
| 39 |
private static final int RIGHT = 3; |
|
| 40 |
private static final int TOP = 4; |
|
| 41 |
private static final int BOTTOM= 5; |
|
| 42 |
|
|
| 34 | 43 |
private static final int NORTH = 0; |
| 35 | 44 |
private static final int WEST = 1; |
| 36 | 45 |
private static final int EAST = 2; |
| ... | ... | |
| 53 | 62 |
col = c; |
| 54 | 63 |
} |
| 55 | 64 |
} |
| 56 |
|
|
| 65 |
|
|
| 66 |
private float[] mTexMappingX,mTexMappingY, mTexMappingW, mTexMappingH; |
|
| 67 |
|
|
| 57 | 68 |
private int mCols, mRows, mSlices; |
| 58 | 69 |
private int[][] mCubes; |
| 59 | 70 |
private byte[][] mInflateX, mInflateY; |
| ... | ... | |
| 74 | 85 |
return ( (2*row<mRows)^(2*col<mCols) ); |
| 75 | 86 |
} |
| 76 | 87 |
|
| 88 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 89 |
// fill per-side texture mappings. Default: all 6 sides map to the whole texture. |
|
| 90 |
// Each Static4D describes the way its side will map. Format: |
|
| 91 |
// |
|
| 92 |
// 1st float: X-coord of the texture point that our top-left corner of the side maps to |
|
| 93 |
// 2nd float: Y-coord of the texture point that our top-left corner of the side maps to |
|
| 94 |
// 3rd float: X-coord of the texture point that our bot-right corner of the side maps to |
|
| 95 |
// 4th float: Y-coord of the texture point that our bot-right corner of the side maps to |
|
| 96 |
|
|
| 97 |
private void fillTexMappings(Static4D front, Static4D back, Static4D left, Static4D right, Static4D top, Static4D bottom) |
|
| 98 |
{
|
|
| 99 |
if( mTexMappingX==null ) mTexMappingX = new float[6]; |
|
| 100 |
if( mTexMappingY==null ) mTexMappingY = new float[6]; |
|
| 101 |
if( mTexMappingW==null ) mTexMappingW = new float[6]; |
|
| 102 |
if( mTexMappingH==null ) mTexMappingH = new float[6]; |
|
| 103 |
|
|
| 104 |
mTexMappingX[FRONT] = front.get1(); |
|
| 105 |
mTexMappingY[FRONT] = front.get2(); |
|
| 106 |
mTexMappingW[FRONT] = front.get3() - front.get1(); |
|
| 107 |
mTexMappingH[FRONT] = front.get4() - front.get2(); |
|
| 108 |
|
|
| 109 |
mTexMappingX[BACK] = back.get1(); |
|
| 110 |
mTexMappingY[BACK] = back.get2(); |
|
| 111 |
mTexMappingW[BACK] = back.get3() - back.get1(); |
|
| 112 |
mTexMappingH[BACK] = back.get4() - back.get2(); |
|
| 113 |
|
|
| 114 |
mTexMappingX[LEFT] = left.get1(); |
|
| 115 |
mTexMappingY[LEFT] = left.get2(); |
|
| 116 |
mTexMappingW[LEFT] = left.get3() - left.get1(); |
|
| 117 |
mTexMappingH[LEFT] = left.get4() - left.get2(); |
|
| 118 |
|
|
| 119 |
mTexMappingX[RIGHT] = right.get1(); |
|
| 120 |
mTexMappingY[RIGHT] = right.get2(); |
|
| 121 |
mTexMappingW[RIGHT] = right.get3() - right.get1(); |
|
| 122 |
mTexMappingH[RIGHT] = right.get4() - right.get2(); |
|
| 123 |
|
|
| 124 |
mTexMappingX[TOP] = top.get1(); |
|
| 125 |
mTexMappingY[TOP] = top.get2(); |
|
| 126 |
mTexMappingW[TOP] = top.get3() - top.get1(); |
|
| 127 |
mTexMappingH[TOP] = top.get4() - top.get2(); |
|
| 128 |
|
|
| 129 |
mTexMappingX[BOTTOM] = bottom.get1(); |
|
| 130 |
mTexMappingY[BOTTOM] = bottom.get2(); |
|
| 131 |
mTexMappingW[BOTTOM] = bottom.get3() - bottom.get1(); |
|
| 132 |
mTexMappingH[BOTTOM] = bottom.get4() - bottom.get2(); |
|
| 133 |
} |
|
| 134 |
|
|
| 77 | 135 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 78 | 136 |
// return the number of vertices our grid will contain |
| 79 | 137 |
|
| ... | ... | |
| 126 | 184 |
} |
| 127 | 185 |
*/ |
| 128 | 186 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 129 |
|
|
| 187 |
/* |
|
| 130 | 188 |
private static String debug(float[] val, int stop) |
| 131 | 189 |
{
|
| 132 | 190 |
String ret=""; |
| ... | ... | |
| 156 | 214 |
|
| 157 | 215 |
return ret; |
| 158 | 216 |
} |
| 159 |
|
|
| 217 |
*/ |
|
| 160 | 218 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 161 | 219 |
/* |
| 162 | 220 |
private static String debug(Edge e) |
| ... | ... | |
| 251 | 309 |
// |
| 252 | 310 |
// This function also creates a list of 'Edges'. Each Edge is a data structure from which later on we |
| 253 | 311 |
// will start building the side walls of each connected block of land (and sides of holes of water |
| 254 |
// inside). Each Edge needs to point from Land to Water (thus the '(SOUTH,i-1,j)' below) - otherwise
|
|
| 312 |
// inside). Each Edge needs to point from Land to Water (thus the '(SOUTH,row-1,col)' below) - otherwise
|
|
| 255 | 313 |
// later on setting up normal vectors wouldn't work. |
| 256 | 314 |
|
| 257 | 315 |
private void markRegions() |
| 258 | 316 |
{
|
| 259 |
int i, j, numWater=1, numLand=0;
|
|
| 317 |
int row, col, numWater=1, numLand=0;
|
|
| 260 | 318 |
|
| 261 |
for(i=0; i<mRows;i++) if( mCubes[ i][ 0]==0 ) markRegion((short)2, i, 0);
|
|
| 262 |
for(i=0; i<mRows;i++) if( mCubes[ i][mCols-1]==0 ) markRegion((short)2, i, mCols-1);
|
|
| 263 |
for(i=0; i<mCols;i++) if( mCubes[0 ][ i]==0 ) markRegion((short)2, 0, i);
|
|
| 264 |
for(i=0; i<mCols;i++) if( mCubes[mRows-1][ i]==0 ) markRegion((short)2,mRows-1, i);
|
|
| 319 |
for(row=0; row<mRows; row++) if( mCubes[ row][ 0]==0 ) markRegion((short)2, row, 0);
|
|
| 320 |
for(row=0; row<mRows; row++) if( mCubes[ row][mCols-1]==0 ) markRegion((short)2, row, mCols-1);
|
|
| 321 |
for(col=0; col<mCols; col++) if( mCubes[0 ][ col]==0 ) markRegion((short)2, 0, col);
|
|
| 322 |
for(col=0; col<mCols; col++) if( mCubes[mRows-1][ col]==0 ) markRegion((short)2,mRows-1, col);
|
|
| 265 | 323 |
|
| 266 |
for(i=0; i<mRows; i++)
|
|
| 267 |
for(j=0; j<mCols; j++)
|
|
| 324 |
for(row=0; row<mRows; row++)
|
|
| 325 |
for(col=0; col<mCols; col++)
|
|
| 268 | 326 |
{
|
| 269 |
if( mCubes[i][j] == 0 ) { numWater++; markRegion( (short)(2*numWater ),i,j); mEdges.add(new Edge(SOUTH,i-1,j)); }
|
|
| 270 |
if( mCubes[i][j] == 1 ) { numLand ++; markRegion( (short)(2*numLand+1),i,j); mEdges.add(new Edge(NORTH,i ,j)); }
|
|
| 327 |
if( mCubes[row][col] == 0 ) { numWater++; markRegion( (short)(2*numWater ),row,col); mEdges.add(new Edge(SOUTH,row-1,col)); }
|
|
| 328 |
if( mCubes[row][col] == 1 ) { numLand ++; markRegion( (short)(2*numLand+1),row,col); mEdges.add(new Edge(NORTH,row ,col)); }
|
|
| 271 | 329 |
} |
| 272 | 330 |
|
| 273 | 331 |
// now we potentially need to kick out some Edges . Otherwise the following does not work: |
| ... | ... | |
| 280 | 338 |
int initCol, initRow, initSide, lastSide; |
| 281 | 339 |
Edge e1,e2; |
| 282 | 340 |
|
| 283 |
for(i=0; i<mEdgeNum; i++)
|
|
| 341 |
for(int edge=0; edge<mEdgeNum; edge++)
|
|
| 284 | 342 |
{
|
| 285 |
e1 = mEdges.get(i);
|
|
| 343 |
e1 = mEdges.get(edge);
|
|
| 286 | 344 |
initRow= e1.row; |
| 287 | 345 |
initCol= e1.col; |
| 288 | 346 |
initSide=e1.side; |
| ... | ... | |
| 295 | 353 |
|
| 296 | 354 |
if( e1.side==NORTH || e1.side==SOUTH ) |
| 297 | 355 |
{
|
| 298 |
for(j=i+1;j<mEdgeNum;j++)
|
|
| 356 |
for(int j=edge+1;j<mEdgeNum;j++)
|
|
| 299 | 357 |
{
|
| 300 | 358 |
e2 = mEdges.get(j); |
| 301 | 359 |
|
| ... | ... | |
| 450 | 508 |
if( !lastBlockIsNE || (!front && !seenLand) ) repeatLast(attribs); |
| 451 | 509 |
addFrontVertex( 1, vectZ, col, row+1, attribs); |
| 452 | 510 |
} |
| 453 |
addFrontVertex( 2, vectZ, col+1, row, attribs); |
|
| 511 |
addFrontVertex( 2, vectZ, col+1, row , attribs);
|
|
| 454 | 512 |
addFrontVertex( 3, vectZ, col+1, row+1, attribs); |
| 455 | 513 |
} |
| 456 | 514 |
else |
| ... | ... | |
| 660 | 718 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+1] = mInflateY[row][col]/2.0f; |
| 661 | 719 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+2] = vectZ; |
| 662 | 720 |
|
| 663 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = x; |
|
| 664 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = 1.0f-y; |
|
| 721 |
if( vectZ>0 ) |
|
| 722 |
{
|
|
| 723 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = mTexMappingX[FRONT] + x * mTexMappingW[FRONT]; |
|
| 724 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[FRONT] + (1.0f-y) * mTexMappingH[FRONT]; |
|
| 725 |
} |
|
| 726 |
else |
|
| 727 |
{
|
|
| 728 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = mTexMappingX[BACK] + x * mTexMappingW[BACK]; |
|
| 729 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BACK] + (1.0f-y) * mTexMappingH[BACK]; |
|
| 730 |
} |
|
| 665 | 731 |
|
| 666 | 732 |
currVert++; |
| 667 | 733 |
} |
| ... | ... | |
| 672 | 738 |
{
|
| 673 | 739 |
//android.util.Log.e("CUBES", "adding Side vertex!");
|
| 674 | 740 |
float x, y, z; |
| 675 |
int row, col, texX, texY;
|
|
| 741 |
int row, col; |
|
| 676 | 742 |
|
| 677 | 743 |
switch(curr.side) |
| 678 | 744 |
{
|
| ... | ... | |
| 694 | 760 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+1] = mInflateY[row][col]/2.0f; |
| 695 | 761 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+2] = z; |
| 696 | 762 |
|
| 697 |
texY = (mRows-row+slice)%(2*mRows); |
|
| 698 |
if( texY>mRows ) texY = 2*mRows-texY; |
|
| 699 |
|
|
| 700 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = x; |
|
| 701 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = (float)texY/mRows; |
|
| 763 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = mTexMappingX[TOP] + x * mTexMappingW[TOP]; |
|
| 764 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[TOP] + (0.5f-z) * mTexMappingH[TOP]; |
|
| 702 | 765 |
|
| 703 | 766 |
break; |
| 704 | 767 |
case SOUTH: row = curr.row+1; |
| ... | ... | |
| 719 | 782 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+1] = mInflateY[row][col]/2.0f; |
| 720 | 783 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+2] = z; |
| 721 | 784 |
|
| 722 |
texY = (mRows-row+slice)%(2*mRows); |
|
| 723 |
if( texY>mRows ) texY = 2*mRows-texY; |
|
| 724 |
|
|
| 725 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = x; |
|
| 726 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = (float)texY/mRows; |
|
| 785 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = mTexMappingX[BOTTOM] + x * mTexMappingW[BOTTOM]; |
|
| 786 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BOTTOM] + (0.5f-z) * mTexMappingH[BOTTOM]; |
|
| 727 | 787 |
|
| 728 | 788 |
break; |
| 729 | 789 |
case WEST : row = (back ? (curr.row+1):(curr.row)); |
| ... | ... | |
| 744 | 804 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+1] = mInflateY[row][col]/2.0f; |
| 745 | 805 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+2] = z; |
| 746 | 806 |
|
| 747 |
texX = (col+slice)%(2*mCols); |
|
| 748 |
if( texX>mCols ) texX = 2*mCols-texX; |
|
| 749 |
|
|
| 750 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = (float)texX/mCols; |
|
| 751 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = 1.0f-y; |
|
| 807 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = mTexMappingX[LEFT] + (0.5f-z) * mTexMappingW[LEFT]; |
|
| 808 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[LEFT] + (1.0f-y) * mTexMappingH[LEFT]; |
|
| 752 | 809 |
|
| 753 | 810 |
break; |
| 754 | 811 |
case EAST : row = (back ? (curr.row):(curr.row+1)); |
| ... | ... | |
| 769 | 826 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+1] = mInflateY[row][col]/2.0f; |
| 770 | 827 |
attribs[VERT_ATTRIBS*currVert + INF_ATTRIB+2] = z; |
| 771 | 828 |
|
| 772 |
texX = (col+slice)%(2*mCols); |
|
| 773 |
if( texX>mCols ) texX = 2*mCols-texX; |
|
| 774 |
|
|
| 775 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = (float)texX/mCols; |
|
| 776 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = 1.0f-y; |
|
| 829 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB ] = mTexMappingX[RIGHT] + (0.5f-z) * mTexMappingW[RIGHT]; |
|
| 830 |
attribs[VERT_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[RIGHT] + (1.0f-y) * mTexMappingH[RIGHT]; |
|
| 777 | 831 |
|
| 778 | 832 |
break; |
| 779 | 833 |
} |
| ... | ... | |
| 845 | 899 |
* <p></p> |
| 846 | 900 |
* <p> |
| 847 | 901 |
* <pre> |
| 848 |
* For example, (cols=2, desc="111010") describes the following shape: |
|
| 902 |
* For example, (cols=2, desc="111010", slices=1) describes the following shape:
|
|
| 849 | 903 |
* |
| 850 | 904 |
* XX |
| 851 | 905 |
* X |
| 852 | 906 |
* X |
| 853 | 907 |
* |
| 854 |
* whereas (cols=2,desc="110001") describes |
|
| 908 |
* whereas (cols=2,desc="110001", slices=1) describes
|
|
| 855 | 909 |
* |
| 856 | 910 |
* XX |
| 857 | 911 |
* |
| ... | ... | |
| 863 | 917 |
public MeshCubes(int cols, String desc, int slices) |
| 864 | 918 |
{
|
| 865 | 919 |
super( (float)slices/cols); |
| 920 |
|
|
| 921 |
Static4D map = new Static4D(0.0f,0.0f,1.0f,1.0f); |
|
| 922 |
fillTexMappings(map,map,map,map,map,map); |
|
| 923 |
prepareDataStructures(cols,desc,slices); |
|
| 924 |
build(); |
|
| 925 |
} |
|
| 926 |
|
|
| 927 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 928 |
/** |
|
| 929 |
* Creates the underlying mesh of vertices, normals, texture coords with custom texture mappings. |
|
| 930 |
* |
|
| 931 |
* The mappings decide what part of the texture appears on a given side. Example: |
|
| 932 |
* front = (0.0,0.5,0.5,1.0) means that the front side of the grid will be textured with the |
|
| 933 |
* bottom-left quadrant of the texture. |
|
| 934 |
* |
|
| 935 |
* Default is: each of the 6 sides maps to the whole texture, i.e. (0.0,0.0,1.0,1.0) |
|
| 936 |
* |
|
| 937 |
* @param cols Integer helping to parse the next parameter. |
|
| 938 |
* @param desc String describing the subset of a MxNx1 cuboid that we want to create. |
|
| 939 |
* Its MxN characters - all 0 or 1 - decide of appropriate field is taken or not. |
|
| 940 |
* <p></p> |
|
| 941 |
* <p> |
|
| 942 |
* <pre> |
|
| 943 |
* For example, (cols=2, desc="111010", slices=1) describes the following shape: |
|
| 944 |
* |
|
| 945 |
* XX |
|
| 946 |
* X |
|
| 947 |
* X |
|
| 948 |
* |
|
| 949 |
* whereas (cols=2,desc="110001", slices=1) describes |
|
| 950 |
* |
|
| 951 |
* XX |
|
| 952 |
* |
|
| 953 |
* X |
|
| 954 |
* </pre> |
|
| 955 |
* </p> |
|
| 956 |
* @param slices Number of slices, i.e. 'depth' of the Mesh. |
|
| 957 |
* @param front Texture mapping the front side maps to. |
|
| 958 |
* @param back Texture mapping the back side maps to. |
|
| 959 |
* @param left Texture mapping the left side maps to. |
|
| 960 |
* @param right Texture mapping the right side maps to. |
|
| 961 |
* @param top Texture mapping the top side maps to. |
|
| 962 |
* @param bottom Texture mapping the bottom side maps to. |
|
| 963 |
*/ |
|
| 964 |
public MeshCubes(int cols, String desc, int slices, Static4D front, Static4D back, Static4D left, Static4D right, Static4D top, Static4D bottom) |
|
| 965 |
{
|
|
| 966 |
super( (float)slices/cols); |
|
| 967 |
fillTexMappings(front,back,left,right,top,bottom); |
|
| 866 | 968 |
prepareDataStructures(cols,desc,slices); |
| 867 | 969 |
build(); |
| 868 | 970 |
} |
| ... | ... | |
| 878 | 980 |
public MeshCubes(int cols, int rows, int slices) |
| 879 | 981 |
{
|
| 880 | 982 |
super( (float)slices/cols); |
| 983 |
|
|
| 984 |
Static4D map = new Static4D(0.0f,0.0f,1.0f,1.0f); |
|
| 985 |
fillTexMappings(map,map,map,map,map,map); |
|
| 986 |
prepareDataStructures(cols,rows,slices); |
|
| 987 |
build(); |
|
| 988 |
} |
|
| 989 |
|
|
| 990 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 991 |
/** |
|
| 992 |
* Creates a full, hole-less underlying mesh of vertices, normals, texture coords and colors with |
|
| 993 |
* custom texture mappings. |
|
| 994 |
* |
|
| 995 |
* The mappings decide what part of the texture appears on a given side. Example: |
|
| 996 |
* front = (0.0,0.5,0.5,1.0) means that the front side of the grid will be textured with the |
|
| 997 |
* bottom-left quadrant of the texture. |
|
| 998 |
* |
|
| 999 |
* Default is: each of the 6 sides maps to the whole texture, i.e. (0.0,0.0,1.0,1.0) |
|
| 1000 |
* |
|
| 1001 |
* @param cols Number of columns, i.e. 'width' of the Mesh. |
|
| 1002 |
* @param rows Number of rows, i.e. 'height' of the Mesh. |
|
| 1003 |
* @param slices Number of slices, i.e. 'depth' of the Mesh. |
|
| 1004 |
* @param front Texture mapping the front side maps to. |
|
| 1005 |
* @param back Texture mapping the back side maps to. |
|
| 1006 |
* @param left Texture mapping the left side maps to. |
|
| 1007 |
* @param right Texture mapping the right side maps to. |
|
| 1008 |
* @param top Texture mapping the top side maps to. |
|
| 1009 |
* @param bottom Texture mapping the bottom side maps to. |
|
| 1010 |
*/ |
|
| 1011 |
public MeshCubes(int cols, int rows, int slices, Static4D front, Static4D back, Static4D left, Static4D right, Static4D top, Static4D bottom) |
|
| 1012 |
{
|
|
| 1013 |
super( (float)slices/cols); |
|
| 1014 |
fillTexMappings(front,back,left,right,top,bottom); |
|
| 881 | 1015 |
prepareDataStructures(cols,rows,slices); |
| 882 | 1016 |
build(); |
| 883 | 1017 |
} |
Also available in: Unified diff
1) new 'Rubik' app (skeleton)
2) MeshCubes: add support for custom texture mappings on each side (Rubik needs that!)