What you see on the screen is created by a long list of rendering instructions that is executed by OpenGL and your graphics card. These instructions might include "draw a pixel at [3,4,5]", or "draw a line from [1,2,3] to [4,5,6]", or "draw a quadrilateral with vertices [1,2,3], [4,5,6], [7,8,9] and [10,11,12], filling it with bitmap texture abc." Depending on your version of OpenGL, this list of rendering instructions might be either a Display list (glRenderList in forge) or a VertexBufferObject. Minecraft has been designed to use them interchangeably.
Some advice before you start
The Tessellator is good for drawing simple things like lines or cubes. But if you're planning to use the Tessellator for anything other than simple shapes, I recommend that you don't! Use Techne instead, it's much easier.
Techne home website
Techne Tutorial - Part 1 - Basics
Techne home website
Techne Tutorial - Part 1 - Basics
How the Tessellator and WorldRenderer work
The WorldRenderer class is used to add rendering instructions to a Display list. A typical way to use it is:
- WorldRenderer worldRenderer = Tessellator.getInstance().getWorldRenderer().
- GLStateManager.pushAttrib() to save the rendering flags (eg lighting, alpha functions)
- GLStateManager.pushMatrix() to save the current rendering position.
- worldRenderer.setTranslation() to set the origin to an appropriate location.
- worldRenderer.startDrawingQuads(), which tells the renderer that you will be drawing Quads.
- Set up the various flags (brightness, normals, color, alpha), for example
worldRenderer.setColorRGBA(...); or worldRenderer.setColourOpaque_I(...); - Each face of each block is drawn by calling addVertexWithUV(x, y, z, u, v) four times, once for each corner of the face. [x,y,z] are the coordinates of the vertex, and [u,v] is the coordinates of the texture pixel (texel) corresponding to that vertex. See the picture and examples below.
* The order of the vertices is important! If you are looking at a face, the coordinates must be given in an anticlockwise order. Otherwise, the face will be pointing in the wrong direction. - Minecraft uses a single large texture for all block face textures. As the icons are registered during startup, they are stitched together into a single large texture - (see glTexImage2D in TextureUtil). When you render a face, you use the small portion of this large texture that corresponds to the appropriate icon. The usual way to retrieve the [u,v] coordinates for your icon is
icon.getMinU(), icon.getMaxU(), icon.getMinV(), icon.getMaxV(). For points that don't lie on the edge of the texture, use icon.getInterpolatedU(0 .. 16), icon.getInterpolatedV(0..16). (For example: icon.getInterpolatedU(8), icon.getInterpolatedV(8) corresponds to the middle point of the icon's texture.) - Tessellator.getInstance().draw() to complete the drawing.
- GLStateManager.popAttrib() and GLStateManager.popMatrix() to restore the previous rendering settings (avoids interfering with subsequent vanilla rendering code).
Note that the direction of the v axis in the texture map is opposite to the y axis in world coordinates! So for example - to draw the grass texture onto the ABCD face:
Some further examples:
Our test block: rendering an "up arrow" on the Zpos (south) face, a "5" on the east face, a "2" on the north face. |
Red block points north, blue block points east. |
Flipped left-right |
Rotated |
Weirdness, probably not very useful! |
Added the world coordinates in clockwise order instead of anti-clockwise... where did the face go? |
Looking south: clockwise world coordinates make the face point in the opposite direction. (Pass 0 faces are not visible from their back). |
Diagonal |
Rotated on outer face |
There are some restrictions on what you can do with quads:
- Quads can be sheared - eg the top is pushed right relative to the bottom, or the left is pushed up relative to the right. So long as the top edge has the same length as the bottom edge, and the left edge has the same length as the right edge, it will still render OK.
Left half of the texture, top is sheared 0.25 to the right |
Right half of the texture, top is sheared 0.25 to the left |
Top half of the texture, left is sheared down by 0.25 |
- However, if you try to "pinch" the quad (eg the top edge is shorter than the bottom edge) it will render looking rather strange.
Top edge is not the same length as the bottom edge --> strange appearance |
- If your points aren't coplanar (eg wouldn't "lie flat" on a tabletop) then the Quad "folds" into two triangles (ABC, CDA) and may not render how you intended.
Non-coplanar points. (Black line added to emphasise how the face has folded into two triangles.) |
Using the Tessellator for other shapes
Although mostly used for Quads, the Tessellator is also suitable for drawing other shapes as well - see the list of suitable drawMode settings below, taken from GL11.class. The vanilla code uses several of these including GL_LINES, GL_LINE_STRIP, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN. They are used in the same way as for Quads - i.e. .startDrawing(drawMode), setting appropriate flags, adding the appropriate number of vertices, then calling draw().
public static final int GL_POINTS = 0;
public static final int GL_LINES = 1;
public static final int GL_LINE_LOOP = 2;
public static final int GL_LINE_STRIP = 3;
public static final int GL_TRIANGLES = 4;
public static final int GL_TRIANGLE_STRIP = 5;
public static final int GL_TRIANGLE_FAN = 6;
public static final int GL_QUADS = 7;
public static final int GL_QUAD_STRIP = 8;
public static final int GL_POLYGON = 9;
A useful guide to all things OpenGL 1.1 is here. It is superceded by a few more versions of OpenGL by now but it covers nearly all of the aspects that Minecraft uses.
public static final int GL_LINES = 1;
public static final int GL_LINE_LOOP = 2;
public static final int GL_LINE_STRIP = 3;
public static final int GL_TRIANGLES = 4;
public static final int GL_TRIANGLE_STRIP = 5;
public static final int GL_TRIANGLE_FAN = 6;
public static final int GL_QUADS = 7;
public static final int GL_QUAD_STRIP = 8;
public static final int GL_POLYGON = 9;
Changing Rendering Modes
Depending on what you're rendering, you may need to change a few of the rendering settings before starting rendering. Generally these might be- Turning alpha testing on or off (GLStateManager.disableAlpha() or .enableAlpha()) - used for transparent "cutouts" when rendering.
- Turning alpha blending on or off and changing the blend function (used for partially transparent textures like ice) - GLStateManager.enableBlend(), GLStateManager.blendFunc(..)
- Changing depth testing GLStateManager.disableDepth()
- Changing back face culling GLStateManager.disableCull()
- Turning texturing on or off (GLStateManager.enableTexture2D())
- Changing the texture sheet (eg blocks sheet, or another texture of your own):
Minecraft.getMinecraft().getTextureManager().bindTexture(your texture sheet); - Changing the lighting mode between item lighting (with OpenGL light sources) and landscape lighting (with blocklight / sky light multitexturing) - use RenderHelper.enableStandardItemLighting() and .disableStandardItemLighting()
Minecraft.getMinecraft().entityRenderer.enableLightMap() and .disableLightMap(), or
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, skyLight * 16, blockLight * 16); where skyLight = 0 - 15, blockLight = 0 - 15
A useful guide to all things OpenGL 1.1 is here. It is superceded by a few more versions of OpenGL by now but it covers nearly all of the aspects that Minecraft uses.
Some further background information.
VertexFormat (with VertexFormatElement) is used in WorldRenderer to specify
the binary format of vertices used for rendering. Each vertex will have one or more of the
following bits of information:
- The position of that vertex
- The colour of that vertex
- The texture UV coordinate (floats)
- The lightmap texture UV coordinate (shorts) – the “brightness” index (block light, skylight)
- The normal vector at that vertex (used for items, i.e. with scene lighting enabled)
Hello there,
ReplyDeleteI followed your instructions, but ingame the blockface does not appear.
Here is my code:
private static void testDraw()
{
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
GlStateManager.pushAttrib();
GlStateManager.pushMatrix();
worldRenderer.startDrawingQuads();
worldRenderer.addVertexWithUV(0, 5, 0, 0.3, 0.1);
worldRenderer.addVertexWithUV(0, 6, 0, 0.3, 0.0);
worldRenderer.addVertexWithUV(1, 6, 0, 0.2, 0.0);
worldRenderer.addVertexWithUV(1, 5, 0, 0.2, 0.1);
tess.draw();
GlStateManager.popAttrib();
GlStateManager.popMatrix();
}
I already debugged and I noticed that the code is executed, so guess ive done smth wrong here.
But what is it? Pls help !
sry for my bad english im german
Hi Could you post this question on the forum? http://www.minecraftforge.net/forum/index.php/board,73.0.html Lots of helpful folks there.
ReplyDeleteAt a rough guess - you are rendering to the wrong [x,y,z] location, so it's there, but you can't see it. The code looks ok.
Dein English ist ganz gut, sicher besser als mein Deutsch :)
-TGG
Hey, Ive already solved my problem, it actually was at the wrong position. Your guide really helped me and i learned how minecraft works and stuff.. so thanks :D
DeleteAs far as I can tell, Icons don't exist anymore in 1.8. What is the process to get texture coordinates now?
ReplyDeleteHi
ReplyDeleteTextureAtlasSprite might be what you're looking for, eg
final ResourceLocation fireballRL = new ResourceLocation("dragonmounts:entities/breath_fire");
TextureAtlasSprite sprite = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(fireballRL.toString());
-TGG