    001package net.minecraft.client.renderer;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.nio.ByteBuffer;
    006import java.nio.ByteOrder;
    007import java.nio.FloatBuffer;
    008import java.nio.IntBuffer;
    009import java.nio.ShortBuffer;
    010import java.util.Arrays;
    011
    012import org.lwjgl.opengl.ARBVertexBufferObject;
    013import org.lwjgl.opengl.GL11;
    014import org.lwjgl.opengl.GLContext;
    015
    016@SideOnly(Side.CLIENT)
    017public 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 i = this.rawBufferIndex * 4;
    312            this.reset();
    313            return i;
    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 i = 0; i < 2; ++i)
    491            {
    492                int j = 8 * (3 - i);
    493
    494                if (this.hasTexture)
    495                {
    496                    this.rawBuffer[this.rawBufferIndex + 3] = this.rawBuffer[this.rawBufferIndex - j + 3];
    497                    this.rawBuffer[this.rawBufferIndex + 4] = this.rawBuffer[this.rawBufferIndex - j + 4];
    498                }
    499
    500                if (this.hasBrightness)
    501                {
    502                    this.rawBuffer[this.rawBufferIndex + 7] = this.rawBuffer[this.rawBufferIndex - j + 7];
    503                }
    504
    505                if (this.hasColor)
    506                {
    507                    this.rawBuffer[this.rawBufferIndex + 5] = this.rawBuffer[this.rawBufferIndex - j + 5];
    508                }
    509
    510                this.rawBuffer[this.rawBufferIndex + 0] = this.rawBuffer[this.rawBufferIndex - j + 0];
    511                this.rawBuffer[this.rawBufferIndex + 1] = this.rawBuffer[this.rawBufferIndex - j + 1];
    512                this.rawBuffer[this.rawBufferIndex + 2] = this.rawBuffer[this.rawBufferIndex - j + 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 j = par1 >> 16 & 255;
    552        int k = par1 >> 8 & 255;
    553        int l = par1 & 255;
    554        this.setColorOpaque(j, k, l);
    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 k = par1 >> 16 & 255;
    563        int l = par1 >> 8 & 255;
    564        int i1 = par1 & 255;
    565        this.setColorRGBA(k, l, i1, 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 b0 = (byte)((int)(par1 * 127.0F));
    583        byte b1 = (byte)((int)(par2 * 127.0F));
    584        byte b2 = (byte)((int)(par3 * 127.0F));
    585        this.normal = b0 & 255 | (b1 & 255) << 8 | (b2 & 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}