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}