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    }