#include "stdafx.h" #include "Tesselator.h" #include "..\Minecraft.World\BasicTypeContainers.h" #include "..\Minecraft.World\FloatBuffer.h" #include "..\Minecraft.World\IntBuffer.h" #include "..\Minecraft.World\ByteBuffer.h" bool Tesselator::TRIANGLE_MODE = false; bool Tesselator::USE_VBO = false; /* Things to check we are intialising in the constructor... double u, v; int col; int mode; double xo, yo, zo; int normal; */ DWORD Tesselator::tlsIdx = TlsAlloc(); Tesselator *Tesselator::getInstance() { return (Tesselator *)TlsGetValue(tlsIdx); } void Tesselator::CreateNewThreadStorage(int bytes) { Tesselator *instance = new Tesselator(bytes/4); TlsSetValue(tlsIdx, instance); } Tesselator::Tesselator(int size) { // 4J - this block of things moved to constructor from general initialisations round Java class vertices = 0; hasColor = false; hasTexture = false; hasTexture2 = false; hasNormal = false; p = 0; count = 0; _noColor = false; tesselating = false; vboMode = false; vboId = 0; vboCounts = 10; // 4J - adding these things to constructor just to be sure that they are initialised with something u = v = 0; col = 0; mode = 0; xo = yo = zo = 0; xoo = yoo = zoo = 0; // 4J added _normal = 0; useCompactFormat360 = false; // 4J added mipmapEnable = true; // 4J added useProjectedTexturePixelShader = false; // 4J added this->size = size; _array = new intArray(size); vboMode = USE_VBO; // 4J removed - && GLContext.getCapabilities().GL_ARB_vertex_buffer_object; if (vboMode) { vboIds = MemoryTracker::createIntBuffer(vboCounts); ARBVertexBufferObject::glGenBuffersARB(vboIds); } } Tesselator *Tesselator::getUniqueInstance(int size) { return new Tesselator(size); } void Tesselator::end() { // if (!tesselating) throw new IllegalStateException("Not tesselating!"); // 4J - removed tesselating = false; if (vertices > 0) { // 4J - a lot of stuff taken out here for fiddling round with enable client states etc. // that don't matter for our renderer if (!hasColor) { // 4J - TEMP put in fixed vertex colors if we don't have any, until we have a shader that can cope without them unsigned int *pColData = (unsigned int *)_array->data; pColData += 5; for( int i = 0; i < vertices; i++ ) { *pColData = 0xffffffff; pColData += 8; } } if (mode == GL_QUADS && TRIANGLE_MODE) { // glDrawArrays(GL_TRIANGLES, 0, vertices); // 4J - changed for xbox RenderManager.DrawVertices(C4JRender::PRIMITIVE_TYPE_TRIANGLE_LIST,vertices,_array->data, useCompactFormat360?C4JRender::VERTEX_TYPE_COMPRESSED:C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1, useProjectedTexturePixelShader?C4JRender::PIXEL_SHADER_TYPE_PROJECTION:C4JRender::PIXEL_SHADER_TYPE_STANDARD); } else { // glDrawArrays(mode, 0, vertices); // 4J - changed for xbox // For compact vertices, the vertexCount has to be calculated from the amount of data written, as // we insert extra fake vertices to encode supplementary data for more awkward quads that have non // axis aligned UVs (eg flowing lava/water) int vertexCount = vertices; if( useCompactFormat360 ) { RenderManager.DrawVertices((C4JRender::ePrimitiveType)mode,vertexCount,_array->data,C4JRender::VERTEX_TYPE_COMPRESSED, C4JRender::PIXEL_SHADER_TYPE_STANDARD); } else { if( useProjectedTexturePixelShader ) { RenderManager.DrawVertices((C4JRender::ePrimitiveType)mode,vertexCount,_array->data,C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TEXGEN, C4JRender::PIXEL_SHADER_TYPE_PROJECTION); } else { RenderManager.DrawVertices((C4JRender::ePrimitiveType)mode,vertexCount,_array->data,C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1, C4JRender::PIXEL_SHADER_TYPE_STANDARD); } } } glDisableClientState(GL_VERTEX_ARRAY); if (hasTexture) glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (hasColor) glDisableClientState(GL_COLOR_ARRAY); if (hasNormal) glDisableClientState(GL_NORMAL_ARRAY); } clear(); } void Tesselator::clear() { vertices = 0; p = 0; count = 0; } void Tesselator::begin() { begin(GL_QUADS); bounds.reset(); // 4J MGH - added } void Tesselator::useProjectedTexture(bool enable) { useProjectedTexturePixelShader = enable; } void Tesselator::useCompactVertices(bool enable) { useCompactFormat360 = enable; } bool Tesselator::getCompactVertices() { return useCompactFormat360; } bool Tesselator::setMipmapEnable(bool enable) { bool prev = mipmapEnable; mipmapEnable = enable; return prev; } void Tesselator::begin(int mode) { /* // 4J - removed if (tesselating) { throw new IllegalStateException("Already tesselating!"); } */ tesselating = true; clear(); this->mode = mode; hasNormal = false; hasColor = false; hasTexture = false; hasTexture2 = false; _noColor = false; } void Tesselator::tex(float u, float v) { hasTexture = true; this->u = u; this->v = v; } void Tesselator::tex2(int tex2) { hasTexture2 = true; this->_tex2 = tex2; } void Tesselator::color(float r, float g, float b) { color((int) (r * 255), (int) (g * 255), (int) (b * 255)); } void Tesselator::color(float r, float g, float b, float a) { color((int) (r * 255), (int) (g * 255), (int) (b * 255), (int) (a * 255)); } void Tesselator::color(int r, int g, int b) { color(r, g, b, 255); } void Tesselator::color(int r, int g, int b, int a) { if (_noColor) return; if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; if (a > 255) a = 255; if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; if (a < 0) a = 0; hasColor = true; // 4J - removed little-endian option col = (r << 24) | (g << 16) | (b << 8) | (a); } void Tesselator::color(byte r, byte g, byte b) { color(r & 0xff, g & 0xff, b & 0xff); } void Tesselator::vertexUV(float x, float y, float z, float u, float v) { tex(u, v); vertex(x, y, z); } // Pack the 4 vertices of a quad up into a compact format. This is structured as 8 bytes per vertex, // arranged in blocks of 4 vertices per quad. Currently this is (one letter per nyblle): // // cccc xxyy zzll rgbi (vertex 0) // umin xxyy zzll rgbi (vertex 1) // vmin xxyy zzll rgbi (vertex 2) // udvd xxyy zzll rgbi (vertex 3) // // where: cccc is a 15-bit (5 bits per x/y/z) origin position / offset for the whole quad. Each // component is unsigned, and offset by 16 so has a range 0 to 31 actually representing -16 to 15 // xx,yy,zz are 8-bit deltas from this origin to each vertex. These are unsigned 1.7 fixed point, ie // representing a range of 0 to 1.9921875 // rgb is 4:4:4 RGB // umin, vmin are 3:13 unsigned fixed point UVs reprenting the min u and v required by the quad // ud,vd are 8-bit unsigned fixed pont UV deltas, which can be added to umin/vmin to get umax, vmax // and therefore define the 4 corners of an axis aligned UV mapping // i is a code per vertex that indicates which of umin/umax should be used for u, and which // of vmin/vmax should be used for v for this vertex. The coding is: // 0 - u = umin, v = vmin // 1 - u = umin, v = vmax // 2 - u = umax, v = vmin // 3 - u = umax, v = vmax // 4 - not axis aligned, use uv stored in the vertex data 4 on from this one // ll is an 8-bit (4 bit per u/v) index into the current lighting texture // // For quads that don't have axis aligned UVs (ie have a code for 4 in i as described above) the 8 byte vertex // is followed by a further 8 bytes which have explicit UVs defined for each vertex: // // 0000 0000 uuuu vvvv (vertex 0) // 0000 0000 uuuu vvvv (vertex 1) // 0000 0000 uuuu vvvv (vertex 2) // 0000 0000 uuuu vvvv (vertex 3) // void Tesselator::packCompactQuad() { // Offset x/y/z by 16 so that we can deal with a -16 -> 16 range for( int i = 0; i < 4; i++ ) { m_ix[i] += 16 * 128; m_iy[i] += 16 * 128; m_iz[i] += 16 * 128; } // Find min x/y/z unsigned int minx = m_ix[0]; unsigned int miny = m_iy[0]; unsigned int minz = m_iz[0]; for( int i = 1; i < 4; i++ ) { if( m_ix[i] < minx ) minx = m_ix[i]; if( m_iy[i] < miny ) miny = m_iy[i]; if( m_iz[i] < minz ) minz = m_iz[i]; } // Everything has been scaled by a factor of 128 to get it into an int, and so // the minimum now should be in the range of (0->32) * 128. Get the base x/y/z // that our quad will be referenced from now, which can be stored in 5 bits unsigned int basex = ( minx >> 7 ); unsigned int basey = ( miny >> 7 ); unsigned int basez = ( minz >> 7 ); // If the min is 32, then this whole quad must be in that plane - make the min 15 instead so // we can still offset from that with our delta to get to the exact edge if( basex == 32 ) basex = 31; if( basey == 32 ) basey = 31; if( basez == 32 ) basez = 31; // Now get deltas to each vertex - these have an 8-bit range so they can span a // full unit range from the base position for( int i = 0; i < 4; i++ ) { m_ix[i] -= basex << 7; m_iy[i] -= basey << 7; m_iz[i] -= basez << 7; } // Now write the data out unsigned int *data = (unsigned int *)&_array->data[p]; for( int i = 0; i < 4; i++ ) { data[i * 2 + 0] = ( m_ix[i] << 8 ) | ( m_iy[i] ); data[i * 2 + 1] = ( m_iz[i] << 24 ) | ( m_clr[i] ); } data[0] |= ( basex << 26 ) | ( basey << 21 )| ( basez << 16 ); // Now process UVs. First find min & max U & V unsigned int minu = m_u[0]; unsigned int minv = m_v[0]; unsigned int maxu = m_u[0]; unsigned int maxv = m_v[0]; for( int i = 1; i < 4; i++ ) { if( m_u[i] < minu ) minu = m_u[i]; if( m_v[i] < minv ) minv = m_v[i]; if( m_u[i] > maxu ) maxu = m_u[i]; if( m_v[i] > maxv ) maxv = m_v[i]; } // In nearly all cases, all our UVs should be axis aligned for this quad. So the only values they should // have in each dimension should be the min/max. We're going to store: // (1) minu/maxu (16 bits each, only actuall needs to store 14 bits to get a 0 to 2 range for each // (2) du/dv ( ie maxu-minu, maxv-minv) - 8 bits each, to store a range of 0 to 15.9375 texels. This // should be enough to map the full UV range of a single 16x16 region of the terrain texture, since // we always pull UVs in by 1/16th of their range at the sides unsigned int du = maxu - minu; unsigned int dv = maxv - minv; if( du > 255 ) du = 255; if( dv > 255 ) dv = 255; // Check if this quad has UVs that can be referenced this way. This should only happen for flowing water // and lava, where the texture coordinates are rotated for the top surface of the tile. bool axisAligned = true; for( int i = 0; i < 4; i++ ) { if(! ( ( ( m_u[i] == minu ) || ( m_u[i] == maxu ) ) && ( ( m_v[i] == minv ) || ( m_v[i] == maxv ) ) ) ) { axisAligned = false; } } if( axisAligned ) { // Now go through each vertex, and work out which of the min/max should be used for each dimension, // and store for( int i = 0; i < 4; i++ ) { unsigned int code = 0; if( m_u[i] == maxu ) code |= 2; if( m_v[i] == maxv ) code |= 1; data[i * 2 + 1] |= code; data[i * 2 + 1] |= m_t2[i] << 16; } // Finally, store the minu/minv/du/dv data[1 * 2 + 0] |= minu << 16; data[2 * 2 + 0] |= minv << 16; data[3 * 2 + 0] |= ( du << 24 | dv << 16 ); p += 4 * 2; } else { // The UVs aren't axis aligned - store them in the next 4 vertices. These will be indexed from // our base vertices because we'll set a special code (4) for the UVs. They won't be drawn as actual // verts when these extra vertices go through the vertex shader, because we'll make sure that // they get interpreted as a zero area quad and so they'll be quickly eliminated from rendering post-tranform for( int i = 0; i < 4; i++ ) { data[i * 2 + 1] |= ( 4 ); // The special code to indicate they need further data to be fetched data[i * 2 + 1] |= m_t2[i] << 16; data[8 + i * 2] = 0; // This includes x/y coordinate of each vert as (0,0) so they will be interpreted as a zero area quad data[9 + i * 2] = m_u[i] << 16 | m_v[i]; } // Extra 8 bytes required p += 8 * 2; } } typedef unsigned short hfloat; extern hfloat convertFloatToHFloat(float f); extern float convertHFloatToFloat(hfloat hf); void Tesselator::vertex(float x, float y, float z) { bounds.addVert(x+xo, y+yo, z+zo); // 4J MGH - added count++; // Signal to pixel shader whether to use mipmapping or not, by putting u into > 1 range if it is to be disabled float uu = mipmapEnable ? u : (u + 1.0f); // 4J - this format added for 360 to keep memory size of tesselated tiles down - // see comments in packCompactQuad() for exact format if( useCompactFormat360 ) { unsigned int ucol = (unsigned int)col; unsigned short packedcol = ((col & 0xf8000000 ) >> 16 ) | ((col & 0x00fc0000 ) >> 13 ) | ((col & 0x0000f800 ) >> 11 ); int ipackedcol = ((int)packedcol) & 0xffff; // 0 to 65535 range ipackedcol -= 32768; // -32768 to 32767 range ipackedcol &= 0xffff; int16_t* pShortData = (int16_t*)&_array->data[p]; #ifdef __PS3__ float tex2U = ((int16_t*)&_tex2)[1] + 8; float tex2V = ((int16_t*)&_tex2)[0] + 8; float colVal1 = ((col&0xff000000)>>24)/256.0f; float colVal2 = ((col&0x00ff0000)>>16)/256.0f; float colVal3 = ((col&0x0000ff00)>>8)/256.0f; // pShortData[0] = convertFloatToHFloat(x + xo); // pShortData[1] = convertFloatToHFloat(y + yo); // pShortData[2] = convertFloatToHFloat(z + zo); // pShortData[3] = convertFloatToHFloat(uu); // pShortData[4] = convertFloatToHFloat(tex2U + colVal1); // pShortData[5] = convertFloatToHFloat(tex2V + colVal2); // pShortData[6] = convertFloatToHFloat(colVal3); // pShortData[7] = convertFloatToHFloat(v); pShortData[0] = (((int)((x + xo ) * 1024.0f))&0xffff); pShortData[1] = (((int)((y + yo ) * 1024.0f))&0xffff); pShortData[2] = (((int)((z + zo ) * 1024.0f))&0xffff); pShortData[3] = ipackedcol; pShortData[4] = (((int)(uu * 8192.0f))&0xffff); pShortData[5] = (((int)(v * 8192.0f))&0xffff); pShortData[6] = (((int)(tex2U * (8192.0f/256.0f)))&0xffff); pShortData[7] = (((int)(tex2V * (8192.0f/256.0f)))&0xffff); p += 4; #else pShortData[0] = (((int)((x + xo ) * 1024.0f))&0xffff); pShortData[1] = (((int)((y + yo ) * 1024.0f))&0xffff); pShortData[2] = (((int)((z + zo ) * 1024.0f))&0xffff); pShortData[3] = ipackedcol; pShortData[4] = (((int)(uu * 8192.0f))&0xffff); pShortData[5] = (((int)(v * 8192.0f))&0xffff); int16_t u2 = ((int16_t*)&_tex2)[0]; int16_t v2 = ((int16_t*)&_tex2)[1]; pShortData[6] = u2; pShortData[7] = v2; p += 4; #endif vertices++; if (vertices % 4 == 0 && ( ( p >= size - 4 * 4 ) || ( ( p / 4 ) >= 65532 ) ) ) // Max 65535 verts in D3D, so 65532 is the last point at the end of a quad to catch it { end(); tesselating = true; } } else { if (mode == GL_QUADS && TRIANGLE_MODE && count % 4 == 0) { for (int i = 0; i < 2; i++) { int offs = 8 * (3 - i); if (hasTexture) { _array->data[p + 3] = _array->data[p - offs + 3]; _array->data[p + 4] = _array->data[p - offs + 4]; } if (hasColor) { _array->data[p + 5] = _array->data[p - offs + 5]; } _array->data[p + 0] = _array->data[p - offs + 0]; _array->data[p + 1] = _array->data[p - offs + 1]; _array->data[p + 2] = _array->data[p - offs + 2]; vertices++; p += 8; } } if (hasTexture) { float *fdata = (float *)(_array->data + p + 3); *fdata++ = uu; *fdata++ = v; } if (hasColor) { _array->data[p + 5] = col; } if (hasNormal) { _array->data[p + 6] = _normal; } if (hasTexture2) { #ifdef __PS3__ int16_t tex2U = ((int16_t*)&_tex2)[1] + 8; int16_t tex2V = ((int16_t*)&_tex2)[0] + 8; int16_t* pShortArray = (int16_t*)&_array->data[p + 7]; pShortArray[0] = tex2U; pShortArray[1] = tex2V; #else _array->data[p + 7] = _tex2; #endif } else { // -512 each for u/v will mean that the renderer will use global settings (set via // RenderManager.StateSetVertexTextureUV) rather than these local ones *(unsigned int *)(&_array->data[p + 7]) = 0xfe00fe00; } float *fdata = (float *)(_array->data + p); *fdata++ = (x + xo); *fdata++ = (y + yo); *fdata++ = (z + zo); p += 8; vertices++; if (vertices % 4 == 0 && p >= size - 8 * 4) { end(); tesselating = true; } } } void Tesselator::color(int c) { int r = ((c >> 16) & 255); int g = ((c >> 8) & 255); int b = ((c) & 255); color(r, g, b); } void Tesselator::color(int c, int alpha) { int r = ((c >> 16) & 255); int g = ((c >> 8) & 255); int b = ((c) & 255); color(r, g, b, alpha); } void Tesselator::noColor() { _noColor = true; } #ifdef __PS3__ uint32_t _ConvertF32toX11Y11Z10N(float x, float y, float z) { // 11111111111 X 0x000007FF // 1111111111100000000000 Y 0x003FF800 // 11111111110000000000000000000000 Z 0xFFC00000 // ZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXX // #defines for X11Y11Z10N format #define X11Y11Z10N_X_MASK 0x000007FF #define X11Y11Z10N_X_BITS 11 #define X11Y11Z10N_X_SHIFT 0 #define X11Y11Z10N_Y_MASK 0x003FF800 #define X11Y11Z10N_Y_BITS 11 #define X11Y11Z10N_Y_SHIFT 11 #define X11Y11Z10N_Z_MASK 0xFFC00000 #define X11Y11Z10N_Z_BITS 10 #define X11Y11Z10N_Z_SHIFT 22 #ifndef _CONTENT_PACKAGE if (x<-1.0f || x>1.0f) { printf("Value (%5.3f) should be in range [-1..1]. Conversion will clamp to X11Y11Z10N.\n", x); } if (y<-1.0f || y>1.0f) { printf("Value (%5.3f) should be in range [-1..1]. Conversion will clamp to X11Y11Z10N.\n", y); } if (z<-1.0f || z>1.0f) { printf("Value (%5.3f) should be in range [-1..1]. Conversion will clamp to X11Y11Z10N.\n", z); } #endif const uint32_t uX = ((int32_t(max(min(((x)*2047.f - 1.f)*0.5f, 1023.f), -1024.f)) & (X11Y11Z10N_X_MASK >> X11Y11Z10N_X_SHIFT)) << X11Y11Z10N_X_SHIFT); const uint32_t uY = ((int32_t(max(min(((y)*2047.f - 1.f)*0.5f, 1023.f), -1024.f)) & (X11Y11Z10N_Y_MASK >> X11Y11Z10N_Y_SHIFT)) << X11Y11Z10N_Y_SHIFT); const uint32_t uZ = ((int32_t(max(min(((z)*1023.f - 1.f)*0.5f, 511.f), -512.f )) & (X11Y11Z10N_Z_MASK >> X11Y11Z10N_Z_SHIFT)) << X11Y11Z10N_Z_SHIFT); const uint32_t xyz = uX | uY | uZ; return xyz; } #endif // __PS3__ void Tesselator::normal(float x, float y, float z) { hasNormal = true; #if defined(__PS3__) _normal = _ConvertF32toX11Y11Z10N(x,y,z); #else byte xx = (byte) (x * 127); byte yy = (byte) (y * 127); byte zz = (byte) (z * 127); _normal = (xx & 0xff) | ((yy & 0xff) << 8) | ((zz & 0xff) << 16); #endif } void Tesselator::offset(float xo, float yo, float zo) { this->xo = xo; this->yo = yo; this->zo = zo; // 4J added this->xoo = xo; this->yoo = yo; this->zoo = zo; } void Tesselator::addOffset(float x, float y, float z) { xo += x; yo += y; zo += z; } bool Tesselator::hasMaxVertices() { return false; }