    001    package net.minecraft.client.renderer;
    002
    003    import cpw.mods.fml.relauncher.Side;
    004    import cpw.mods.fml.relauncher.SideOnly;
    005    import java.nio.ByteBuffer;
    006    import java.nio.ByteOrder;
    007    import java.nio.FloatBuffer;
    008    import java.nio.IntBuffer;
    009    import java.nio.ShortBuffer;
    010    import java.util.Arrays;
    011
    012    import org.lwjgl.opengl.ARBVertexBufferObject;
    013    import org.lwjgl.opengl.GL11;
    014    import org.lwjgl.opengl.GLContext;
    015
    016    @SideOnly(Side.CLIENT)
    017    public class Tessellator
    018    {
    019        private static int nativeBufferSize = 0x200000;
    020        private static int trivertsInBuffer = (nativeBufferSize / 48) * 6;
    021        public static boolean renderingWorldRenderer = false;
    022        public boolean defaultTexture = false;
    023        private int rawBufferSize = 0;
    024        public int textureID = 0;
    025        /**
    026         * Boolean used to check whether quads should be drawn as two triangles. Initialized to false and never changed.
    027         */
    028        private static boolean convertQuadsToTriangles = false;
    029
    030        /**
    031         * Boolean used to check if we should use vertex buffers. Initialized to false and never changed.
    032         */
    033        private static boolean tryVBO = false;
    034
    035        /** The byte buffer used for GL allocation. */
    036        private static ByteBuffer byteBuffer = GLAllocation.createDirectByteBuffer(nativeBufferSize * 4);
    037
    038        /** The same memory as byteBuffer, but referenced as an integer buffer. */
    039        private static IntBuffer intBuffer = byteBuffer.asIntBuffer();
    040
    041        /** The same memory as byteBuffer, but referenced as an float buffer. */
    042        private static FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
    043
    044        /** Short buffer */
    045        private static ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
    046
    047        /** Raw integer array. */
    048        private int[] rawBuffer;
    049
    050        /**
    051         * The number of vertices to be drawn in the next draw call. Reset to 0 between draw calls.
    052         */
    053        private int vertexCount = 0;
    054
    055        /** The first coordinate to be used for the texture. */
    056        private double textureU;
    057
    058        /** The second coordinate to be used for the texture. */
    059        private double textureV;
    060        private int brightness;
    061
    062        /** The color (RGBA) value to be used for the following draw call. */
    063        private int color;
    064
    065        /**
    066         * Whether the current draw object for this tessellator has color values.
    067         */
    068        private boolean hasColor = false;
    069
    070        /**
    071         * Whether the current draw object for this tessellator has texture coordinates.
    072         */
    073        private boolean hasTexture = false;
    074        private boolean hasBrightness = false;
    075
    076        /**
    077         * Whether the current draw object for this tessellator has normal values.
    078         */
    079        private boolean hasNormals = false;
    080
    081        /** The index into the raw buffer to be used for the next data. */
    082        private int rawBufferIndex = 0;
    083
    084        /**
    085         * The number of vertices manually added to the given draw call. This differs from vertexCount because it adds extra
    086         * vertices when converting quads to triangles.
    087         */
    088        private int addedVertices = 0;
    089
    090        /** Disables all color information for the following draw call. */
    091        private boolean isColorDisabled = false;
    092
    093        /** The draw mode currently being used by the tessellator. */
    094        public int drawMode;
    095
    096        /**
    097         * An offset to be applied along the x-axis for all vertices in this draw call.
    098         */
    099        public double xOffset;
    100
    101        /**
    102         * An offset to be applied along the y-axis for all vertices in this draw call.
    103         */
    104        public double yOffset;
    105
    106        /**
    107         * An offset to be applied along the z-axis for all vertices in this draw call.
    108         */
    109        public double zOffset;
    110
    111        /** The normal to be applied to the face being drawn. */
    112        private int normal;
    113
    114        /** The static instance of the Tessellator. */
    115        public static Tessellator instance = new Tessellator(2097152);
    116
    117        /** Whether this tessellator is currently in draw mode. */
    118        public boolean isDrawing = false;
    119
    120        /** Whether we are currently using VBO or not. */
    121        private static boolean useVBO = false;
    122
    123        /** An IntBuffer used to store the indices of vertex buffer objects. */
    124        private static IntBuffer vertexBuffers;
    125
    126        /**
    127         * The index of the last VBO used. This is used in round-robin fashion, sequentially, through the vboCount vertex
    128         * buffers.
    129         */
    130        private int vboIndex = 0;
    131
    132        /** Number of vertex buffer objects allocated for use. */
    133        private static int vboCount = 10;
    134
    135        /** The size of the buffers used (in integers). */
    136        private int bufferSize;
    137
    138        private Tessellator(int par1)
    139        {
    140        }
    141
    142        public Tessellator()
    143        {
    144        }
    145
    146        static
    147        {
    148            instance.defaultTexture = true;
    149            useVBO = tryVBO && GLContext.getCapabilities().GL_ARB_vertex_buffer_object;
    150
    151            if (useVBO)
    152            {
    153                vertexBuffers = GLAllocation.createDirectIntBuffer(vboCount);
    154                ARBVertexBufferObject.glGenBuffersARB(vertexBuffers);
    155            }
    156        }
    157
    158        /**
    159         * Draws the data set up in this tessellator and resets the state to prepare for new drawing.
    160         */
    161        public int draw()
    162        {
    163            if (!this.isDrawing)
    164            {
    165                throw new IllegalStateException("Not tesselating!");
    166            }
    167            else
    168            {
    169                this.isDrawing = false;
    170
    171                int offs = 0;
    172                while (offs < vertexCount)
    173                {
    174                    int vtc = 0;
    175                    if (drawMode == 7 && convertQuadsToTriangles)
    176                    {
    177                        vtc = Math.min(vertexCount - offs, trivertsInBuffer);
    178                    }
    179                    else
    180                    {
    181                        vtc = Math.min(vertexCount - offs, nativeBufferSize >> 5);
    182                    }
    183                    this.intBuffer.clear();
    184                    this.intBuffer.put(this.rawBuffer, offs * 8, vtc * 8);
    185                    this.byteBuffer.position(0);
    186                    this.byteBuffer.limit(vtc * 32);
    187                    offs += vtc;
    188
    189                    if (this.useVBO)
    190                    {
    191                        this.vboIndex = (this.vboIndex + 1) % this.vboCount;
    192                        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, this.vertexBuffers.get(this.vboIndex));
    193                        ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, this.byteBuffer, ARBVertexBufferObject.GL_STREAM_DRAW_ARB);
    194                    }
    195
    196                    if (this.hasTexture)
    197                    {
    198                        if (this.useVBO)
    199                        {
    200                            GL11.glTexCoordPointer(2, GL11.GL_FLOAT, 32, 12L);
    201                        }
    202                        else
    203                        {
    204                            this.floatBuffer.position(3);
    205                            GL11.glTexCoordPointer(2, 32, this.floatBuffer);
    206                        }
    207
    208                        GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    209                    }
    210
    211                    if (this.hasBrightness)
    212                    {
    213                        OpenGlHelper.setClientActiveTexture(OpenGlHelper.lightmapTexUnit);
    214
    215                        if (this.useVBO)
    216                        {
    217                            GL11.glTexCoordPointer(2, GL11.GL_SHORT, 32, 28L);
    218                        }
    219                        else
    220                        {
    221                            this.shortBuffer.position(14);
    222                            GL11.glTexCoordPointer(2, 32, this.shortBuffer);
    223                        }
    224
    225                        GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    226                        OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit);
    227                    }
    228
    229                    if (this.hasColor)
    230                    {
    231                        if (this.useVBO)
    232                        {
    233                            GL11.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 32, 20L);
    234                        }
    235                        else
    236                        {
    237                            this.byteBuffer.position(20);
    238                            GL11.glColorPointer(4, true, 32, this.byteBuffer);
    239                        }
    240
    241                        GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
    242                    }
    243
    244                    if (this.hasNormals)
    245                    {
    246                        if (this.useVBO)
    247                        {
    248                            GL11.glNormalPointer(GL11.GL_UNSIGNED_BYTE, 32, 24L);
    249                        }
    250                        else
    251                        {
    252                            this.byteBuffer.position(24);
    253                            GL11.glNormalPointer(32, this.byteBuffer);
    254                        }
    255
    256                        GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
    257                    }
    258
    259                    if (this.useVBO)
    260                    {
    261                        GL11.glVertexPointer(3, GL11.GL_FLOAT, 32, 0L);
    262                    }
    263                    else
    264                    {
    265                        this.floatBuffer.position(0);
    266                        GL11.glVertexPointer(3, 32, this.floatBuffer);
    267                    }
    268
    269                    GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
    270
    271                    if (this.drawMode == 7 && convertQuadsToTriangles)
    272                    {
    273                        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vtc);
    274                    }
    275                    else
    276                    {
    277                        GL11.glDrawArrays(this.drawMode, 0, vtc);
    278                    }
    279
    280                    GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
    281
    282                    if (this.hasTexture)
    283                    {
    284                        GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    285                    }
    286
    287                    if (this.hasBrightness)
    288                    {
    289                        OpenGlHelper.setClientActiveTexture(OpenGlHelper.lightmapTexUnit);
    290                        GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    291                        OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit);
    292                    }
    293
    294                    if (this.hasColor)
    295                    {
    296                        GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
    297                    }
    298
    299                    if (this.hasNormals)
    300                    {
    301                        GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
    302                    }
    303                }
    304
    305                if (rawBufferSize > 0x20000 && rawBufferIndex < (rawBufferSize << 3))
    306                {
    307                    rawBufferSize = 0;
    308                    rawBuffer = null;
    309                }
    310
    311                int var1 = this.rawBufferIndex * 4;
    312                this.reset();
    313                return var1;
    314            }
    315        }
    316
    317        /**
    318         * Clears the tessellator state in preparation for new drawing.
    319         */
    320        private void reset()
    321        {
    322            this.vertexCount = 0;
    323            this.byteBuffer.clear();
    324            this.rawBufferIndex = 0;
    325            this.addedVertices = 0;
    326        }
    327
    328        /**
    329         * Sets draw mode in the tessellator to draw quads.
    330         */
    331        public void startDrawingQuads()
    332        {
    333            this.startDrawing(7);
    334        }
    335
    336        /**
    337         * Resets tessellator state and prepares for drawing (with the specified draw mode).
    338         */
    339        public void startDrawing(int par1)
    340        {
    341            if (this.isDrawing)
    342            {
    343                throw new IllegalStateException("Already tesselating!");
    344            }
    345            else
    346            {
    347                this.isDrawing = true;
    348                this.reset();
    349                this.drawMode = par1;
    350                this.hasNormals = false;
    351                this.hasColor = false;
    352                this.hasTexture = false;
    353                this.hasBrightness = false;
    354                this.isColorDisabled = false;
    355            }
    356        }
    357
    358        /**
    359         * Sets the texture coordinates.
    360         */
    361        public void setTextureUV(double par1, double par3)
    362        {
    363            this.hasTexture = true;
    364            this.textureU = par1;
    365            this.textureV = par3;
    366        }
    367
    368        public void setBrightness(int par1)
    369        {
    370            this.hasBrightness = true;
    371            this.brightness = par1;
    372        }
    373
    374        /**
    375         * Sets the RGB values as specified, converting from floats between 0 and 1 to integers from 0-255.
    376         */
    377        public void setColorOpaque_F(float par1, float par2, float par3)
    378        {
    379            this.setColorOpaque((int)(par1 * 255.0F), (int)(par2 * 255.0F), (int)(par3 * 255.0F));
    380        }
    381
    382        /**
    383         * Sets the RGBA values for the color, converting from floats between 0 and 1 to integers from 0-255.
    384         */
    385        public void setColorRGBA_F(float par1, float par2, float par3, float par4)
    386        {
    387            this.setColorRGBA((int)(par1 * 255.0F), (int)(par2 * 255.0F), (int)(par3 * 255.0F), (int)(par4 * 255.0F));
    388        }
    389
    390        /**
    391         * Sets the RGB values as specified, and sets alpha to opaque.
    392         */
    393        public void setColorOpaque(int par1, int par2, int par3)
    394        {
    395            this.setColorRGBA(par1, par2, par3, 255);
    396        }
    397
    398        /**
    399         * Sets the RGBA values for the color. Also clamps them to 0-255.
    400         */
    401        public void setColorRGBA(int par1, int par2, int par3, int par4)
    402        {
    403            if (!this.isColorDisabled)
    404            {
    405                if (par1 > 255)
    406                {
    407                    par1 = 255;
    408                }
    409
    410                if (par2 > 255)
    411                {
    412                    par2 = 255;
    413                }
    414
    415                if (par3 > 255)
    416                {
    417                    par3 = 255;
    418                }
    419
    420                if (par4 > 255)
    421                {
    422                    par4 = 255;
    423                }
    424
    425                if (par1 < 0)
    426                {
    427                    par1 = 0;
    428                }
    429
    430                if (par2 < 0)
    431                {
    432                    par2 = 0;
    433                }
    434
    435                if (par3 < 0)
    436                {
    437                    par3 = 0;
    438                }
    439
    440                if (par4 < 0)
    441                {
    442                    par4 = 0;
    443                }
    444
    445                this.hasColor = true;
    446
    447                if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN)
    448                {
    449                    this.color = par4 << 24 | par3 << 16 | par2 << 8 | par1;
    450                }
    451                else
    452                {
    453                    this.color = par1 << 24 | par2 << 16 | par3 << 8 | par4;
    454                }
    455            }
    456        }
    457
    458        /**
    459         * Adds a vertex specifying both x,y,z and the texture u,v for it.
    460         */
    461        public void addVertexWithUV(double par1, double par3, double par5, double par7, double par9)
    462        {
    463            this.setTextureUV(par7, par9);
    464            this.addVertex(par1, par3, par5);
    465        }
    466
    467        /**
    468         * Adds a vertex with the specified x,y,z to the current draw call. It will trigger a draw() if the buffer gets
    469         * full.
    470         */
    471        public void addVertex(double par1, double par3, double par5)
    472        {
    473            if (rawBufferIndex >= rawBufferSize - 32)
    474            {
    475                if (rawBufferSize == 0)
    476                {
    477                    rawBufferSize = 0x10000;
    478                    rawBuffer = new int[rawBufferSize];
    479                }
    480                else
    481                {
    482                    rawBufferSize *= 2;
    483                    rawBuffer = Arrays.copyOf(rawBuffer, rawBufferSize);
    484                }
    485            }
    486            ++this.addedVertices;
    487
    488            if (this.drawMode == 7 && convertQuadsToTriangles && this.addedVertices % 4 == 0)
    489            {
    490                for (int var7 = 0; var7 < 2; ++var7)
    491                {
    492                    int var8 = 8 * (3 - var7);
    493
    494                    if (this.hasTexture)
    495                    {
    496                        this.rawBuffer[this.rawBufferIndex + 3] = this.rawBuffer[this.rawBufferIndex - var8 + 3];
    497                        this.rawBuffer[this.rawBufferIndex + 4] = this.rawBuffer[this.rawBufferIndex - var8 + 4];
    498                    }
    499
    500                    if (this.hasBrightness)
    501                    {
    502                        this.rawBuffer[this.rawBufferIndex + 7] = this.rawBuffer[this.rawBufferIndex - var8 + 7];
    503                    }
    504
    505                    if (this.hasColor)
    506                    {
    507                        this.rawBuffer[this.rawBufferIndex + 5] = this.rawBuffer[this.rawBufferIndex - var8 + 5];
    508                    }
    509
    510                    this.rawBuffer[this.rawBufferIndex + 0] = this.rawBuffer[this.rawBufferIndex - var8 + 0];
    511                    this.rawBuffer[this.rawBufferIndex + 1] = this.rawBuffer[this.rawBufferIndex - var8 + 1];
    512                    this.rawBuffer[this.rawBufferIndex + 2] = this.rawBuffer[this.rawBufferIndex - var8 + 2];
    513                    ++this.vertexCount;
    514                    this.rawBufferIndex += 8;
    515                }
    516            }
    517
    518            if (this.hasTexture)
    519            {
    520                this.rawBuffer[this.rawBufferIndex + 3] = Float.floatToRawIntBits((float)this.textureU);
    521                this.rawBuffer[this.rawBufferIndex + 4] = Float.floatToRawIntBits((float)this.textureV);
    522            }
    523
    524            if (this.hasBrightness)
    525            {
    526                this.rawBuffer[this.rawBufferIndex + 7] = this.brightness;
    527            }
    528
    529            if (this.hasColor)
    530            {
    531                this.rawBuffer[this.rawBufferIndex + 5] = this.color;
    532            }
    533
    534            if (this.hasNormals)
    535            {
    536                this.rawBuffer[this.rawBufferIndex + 6] = this.normal;
    537            }
    538
    539            this.rawBuffer[this.rawBufferIndex + 0] = Float.floatToRawIntBits((float)(par1 + this.xOffset));
    540            this.rawBuffer[this.rawBufferIndex + 1] = Float.floatToRawIntBits((float)(par3 + this.yOffset));
    541            this.rawBuffer[this.rawBufferIndex + 2] = Float.floatToRawIntBits((float)(par5 + this.zOffset));
    542            this.rawBufferIndex += 8;
    543            ++this.vertexCount;
    544        }
    545
    546        /**
    547         * Sets the color to the given opaque value (stored as byte values packed in an integer).
    548         */
    549        public void setColorOpaque_I(int par1)
    550        {
    551            int var2 = par1 >> 16 & 255;
    552            int var3 = par1 >> 8 & 255;
    553            int var4 = par1 & 255;
    554            this.setColorOpaque(var2, var3, var4);
    555        }
    556
    557        /**
    558         * Sets the color to the given color (packed as bytes in integer) and alpha values.
    559         */
    560        public void setColorRGBA_I(int par1, int par2)
    561        {
    562            int var3 = par1 >> 16 & 255;
    563            int var4 = par1 >> 8 & 255;
    564            int var5 = par1 & 255;
    565            this.setColorRGBA(var3, var4, var5, par2);
    566        }
    567
    568        /**
    569         * Disables colors for the current draw call.
    570         */
    571        public void disableColor()
    572        {
    573            this.isColorDisabled = true;
    574        }
    575
    576        /**
    577         * Sets the normal for the current draw call.
    578         */
    579        public void setNormal(float par1, float par2, float par3)
    580        {
    581            this.hasNormals = true;
    582            byte var4 = (byte)((int)(par1 * 127.0F));
    583            byte var5 = (byte)((int)(par2 * 127.0F));
    584            byte var6 = (byte)((int)(par3 * 127.0F));
    585            this.normal = var4 & 255 | (var5 & 255) << 8 | (var6 & 255) << 16;
    586        }
    587
    588        /**
    589         * Sets the translation for all vertices in the current draw call.
    590         */
    591        public void setTranslation(double par1, double par3, double par5)
    592        {
    593            this.xOffset = par1;
    594            this.yOffset = par3;
    595            this.zOffset = par5;
    596        }
    597
    598        /**
    599         * Offsets the translation for all vertices in the current draw call.
    600         */
    601        public void addTranslation(float par1, float par2, float par3)
    602        {
    603            this.xOffset += (double)par1;
    604            this.yOffset += (double)par2;
    605            this.zOffset += (double)par3;
    606        }
    607    }