Minecraft:Procedural animated texture generation
More actions
In older versions of Minecraft, the textures for animated blocks were generated on-the-fly using certain algorithms, rather than being predefined image files.
In cases where these textures failed to generate or were intentionally disabled, dedicated placeholder textures would be used instead.
Blocks
Fire
Detailed information is limited, however an implementation has been made in MC-TextureGen: https://github.com/NeRdTheNed/MC-TextureGen/blob/main/mc-texture-gen-impl/src/main/java/com/github/nerdthened/mctexturegen/generators/FireGenerator.java
The generated texture is 16×20; the bottom 4 pixels are cropped, leaving a 16×16 texture.
Nether portals
Minecraft:Procedural animated texture generation/Nether portals
Gears
Code which generates the frames of the gear texture can be found here.
The animation for gears was generated using two predefined image files - Template:Code for the rotating gear and Template:Code for the stationary center.
The animation is rendered as a 16×16 texture like most other blocks, with Template:Code being downsampled. The gear rotates exactly 1/64 of a full rotation (5.625 degrees) every game tick - this means that although there would theoretically be many possible frames created by intermediate rotations, only 64 unique frames can ever appear in practice. The resulting gear takes 3.2 seconds to make a full rotation and has 18.75 RPM.
There are two different animations used for gears - one for clockwise rotation, and another for counterclockwise rotation, to allow for logical meshing. These are generated effectively identically, with the only difference being the direction of rotation; both start on the same frame, but cycle through them in the opposite direction.<ref name="geargen">Template:Citation</ref>
-
Clockwise rotation
-
Counterclockwise rotation
Fluids
Fluid textures are generated via 3-layer non-deterministic cellular automata, which modify the RGB and alpha values of the texture accordingly,<ref name="classicube">https://github.com/UnknownShadow200/ClassiCube/wiki/MInecraft-Classic-lava-animation-algorithm</ref> along with shifting of the texture to emulate flowing. Each layer is represented as an array of floats, each with 256 elements, corresponding to the 256 (16×16) texture pixels within the block.<ref name="classicube"/>
As can be seen when loading up a world with water or lava in view, the textures start as a solid color before the cellular automaton starts generating the texture.
Indexes which end up outside of the bounds of the texture reappear at the opposite respective side.<ref name="classicube"/>
For the purposes of explanation, the variables used have been named (arbitrarily) as attributes of a boiling pot of soup over a fire.<ref name="classicube"/> The first layer represents the flame_heat value (which can be either negative or positive), the second layer represents the pot_heat value and the third layer represents the soup_heat value.
Note that Template:El and Template:El used different pseudorandom generators for generating all random numbers in the game (Java Edition using a LCG whereas Pocket Edition used a MT19937), which includes the random numbers used for water and lava. Due to being random, however, this likely has negligible visual impact.
Water
Minecraft:Procedural animated texture generation/Water
Lava
Items
Clocks
To generate its appearances, the clock combined 2 textures, one being the actual clock, and the other being the dial.
-
Clock texture file
The logic solely for mixing the two (as there is additional logic for moving the dial in a wobbly fashion that isn't pertinent here) is as follows:
<syntaxhighlight lang="python3" line="1">
- Assume RGBA values are handled as 0.0 to 1.0 float values
def setup_clock_sprite (item: Image, dial: Image, dial_angle: float, output: Image): rx = sin(-dial_angle) ry = cos(-dial_angle) for y in range(item.height): for x in range(item.width): pix = item.get_pixel(x, y) if pix.r == pix.b and pix.g == 0 and pix.b > 0: u: float = -(x / (item.width - 1) - 0.5) v: float = y / (item.height - 1) - 0.5 dial_pix = dial.get_pixel( int(((u * ry + v * rx + 0.5) * dial.width)) % dial.width, int(((v * ry - u * rx + 0.5) * dial.height)) % dial.height ) dial_pix.rgb *= pix.r pix = dial_pix output.set_pixel(x, y, pix) </syntaxhighlight> This results in the item and dial sprites being mixed by fuchsia areas of the clock sprite, as well as being shaded by them (contrary to the popular belief that they were mixed solely on the two shades found on the vanilla sprite). This allowed for the clock to be animated precisely, having 230 visually distinct frames, in an era where block and item sprites couldn't be animated individually without mods. The pre-rendered animated approach, used in 1.5 onward, is far less precise, with only 64 different frames.
Due to an oversight with how assets were loaded however, the item sprite for clocks couldn't be overridden by texture packs (as they are set to always load from the vanilla Template:Code atlas, stored in Template:Code, rather than the one of the currently active texture pack.
Compasses
Compasses simply draw two lines over the item sprite to form the needle.
Much like clocks, the code responsible for moving the needle is also present with the "setup" code, however it is omitted here as it is not pertinent to the actual drawing of the sprite. Also like clocks, an oversight in how the compass sprite is set to be loaded prevents texture packs from overriding the compass's base sprite.
-
The base sprite for the compass
<syntaxhighlight lang="python3" line="1"> def setup_compass_sprite (item: Image, angle: float, output: Image): NX = 8.5 NY = 7.5 SCALE_X = 0.3 SCALE_Y = SCALE_X * 0.5
# copy the item's texture into the output for i, pix in enumerate(item): output.set_pixeli(i, pix)
rx = sin(angle) ry = cos(angle)
# draw the smaller horizontal spurs of the needle # 1 is added to the endpoint, as `range` here is # end-exclusive. The original loops did `i <= 4` for i in range(-4, 4 + 1): x = int(NX + ry * i * SCALE_X) y = int(NY - rx * i * SCALE_Y) output.set_pixel(x, y, '#646464')
# draw the main part needle for i in range(-8, 16 + 1): x = int(NX + rx * i * SCALE_X) y = int(NY + ry * i * SCALE_Y) if i >= 0: # Main red pointer output.set_pixel(x, y, '#FF1414') else: # Grey back half output.set_pixel(x, y, '#646464') </syntaxhighlight> The generated compass sprite has 104 possible unique frames, while the pre-rendered compass has significantly less - 32 frames are defined, but as some of these are identical, the actual effective number of unique frames is 28 and was further reduced to 27 after the Minecraft:Texture Update.
-
Procedurally generated
-
Pre-rendered frames
-
Visualization of all of the points that are plotted in drawing the needle
References
Template:Navbox removed features
Minecraft:pt:Geração de textura animada processual Minecraft:ru:Процедурная генерация анимированных текстур