diff --git a/docs/building.rst b/docs/building.rst index 21f4371..9dbfe27 100644 --- a/docs/building.rst +++ b/docs/building.rst @@ -2,21 +2,30 @@ Building the Overviewer from Source =================================== -These instructions are for building the C extension for Overviewer. Also note -that pre-built Windows and Debian executables are available in the `Downloads -`_ section. These -kits already contain the compiled extension and require no further setup (so you -can skip to the :doc:`Running ` section of the docs). +These instructions are for building the C extension for Overviewer. Once you +have finished with these instructions, head to :doc:`running`. + +.. note:: + + Pre-built Windows and Debian executables are available on the + :doc:`installing` page. These kits already contain the compiled code and + require no further setup, so you can skip to the next section of the docs: + :doc:`running`. Get The Source ============== -First step: download the source! Either clone with Git or download the most recent snapshot -* URL to clone: ``git://github.com/overviewer/Minecraft-Overviewer.git`` +First step: download the platform-independent source! Either clone with Git +(recommended if you know Git) or download the most recent snapshot + +* Git URL to clone: ``git://github.com/overviewer/Minecraft-Overviewer.git`` * `Download most recent tar archive `_ * `Download most recent zip archive `_ +Once you have the source, see below for instructions on building for your +system. + Build Instructions For Various Operating Systems ================================================ @@ -157,5 +166,10 @@ regular user. Installing the Compiled Code ---------------------------- -You can run the ``overviewer.py`` script from the build directory just fine. If -you'd like to install, run ``python setup.py install`` + +You can run the ``overviewer.py`` script from the build directory just fine; +installation is unnecessary. If you'd like to install, run + +:: + + python setup.py install diff --git a/docs/design/cube_parts.png b/docs/design/blockrendering/cube_parts.png similarity index 100% rename from docs/design/cube_parts.png rename to docs/design/blockrendering/cube_parts.png diff --git a/docs/design/cube_parts.svg b/docs/design/blockrendering/cube_parts.svg similarity index 100% rename from docs/design/cube_parts.svg rename to docs/design/blockrendering/cube_parts.svg diff --git a/docs/design/cube_sides.png b/docs/design/blockrendering/cube_sides.png similarity index 100% rename from docs/design/cube_sides.png rename to docs/design/blockrendering/cube_sides.png diff --git a/docs/design/cube_top.png b/docs/design/blockrendering/cube_top.png similarity index 100% rename from docs/design/cube_top.png rename to docs/design/blockrendering/cube_top.png diff --git a/docs/design/pixelfix.png b/docs/design/blockrendering/pixelfix.png similarity index 100% rename from docs/design/pixelfix.png rename to docs/design/blockrendering/pixelfix.png diff --git a/docs/design/pixelfix.svg b/docs/design/blockrendering/pixelfix.svg similarity index 100% rename from docs/design/pixelfix.svg rename to docs/design/blockrendering/pixelfix.svg diff --git a/docs/design/tessellation.png b/docs/design/blockrendering/tessellation.png similarity index 100% rename from docs/design/tessellation.png rename to docs/design/blockrendering/tessellation.png diff --git a/docs/design/tessellation.svg b/docs/design/blockrendering/tessellation.svg similarity index 100% rename from docs/design/tessellation.svg rename to docs/design/blockrendering/tessellation.svg diff --git a/docs/design/texturecubing.png b/docs/design/blockrendering/texturecubing.png similarity index 100% rename from docs/design/texturecubing.png rename to docs/design/blockrendering/texturecubing.png diff --git a/docs/design/texturecubing.svg b/docs/design/blockrendering/texturecubing.svg similarity index 100% rename from docs/design/texturecubing.svg rename to docs/design/blockrendering/texturecubing.svg diff --git a/docs/design/texturesidesteps.png b/docs/design/blockrendering/texturesidesteps.png similarity index 100% rename from docs/design/texturesidesteps.png rename to docs/design/blockrendering/texturesidesteps.png diff --git a/docs/design/texturesidesteps.svg b/docs/design/blockrendering/texturesidesteps.svg similarity index 100% rename from docs/design/texturesidesteps.svg rename to docs/design/blockrendering/texturesidesteps.svg diff --git a/docs/design/texturetopsteps.png b/docs/design/blockrendering/texturetopsteps.png similarity index 100% rename from docs/design/texturetopsteps.png rename to docs/design/blockrendering/texturetopsteps.png diff --git a/docs/design/texturetopsteps.svg b/docs/design/blockrendering/texturetopsteps.svg similarity index 100% rename from docs/design/texturetopsteps.svg rename to docs/design/blockrendering/texturetopsteps.svg diff --git a/docs/design/designdoc.rst b/docs/design/designdoc.rst index e6a7ef7..357ecd7 100644 --- a/docs/design/designdoc.rst +++ b/docs/design/designdoc.rst @@ -90,7 +90,7 @@ the process remains the same). In order to render a cube out of this, an `affine transformation`_ is applied to the texture in order to transform it to the top, left, and right faces of the cube. -.. image:: texturecubing.png +.. image:: blockrendering/texturecubing.png :alt: A texture gets rendered into a cube .. _affine transformation: http://en.wikipedia.org/wiki/Affine_transformation @@ -130,12 +130,12 @@ these steps: This produces an image of size 24 by 12 as seen in the following sequence. -.. image:: texturetopsteps.png +.. image:: blockrendering/texturetopsteps.png :alt: The 4 steps for transforming a texture square into the top of the cube. The final image, shown below, becomes the top of the cube. -.. image:: cube_top.png +.. image:: blockrendering/cube_top.png :alt: Top of the cube On the left is what will become the top of the block at actual size after the @@ -156,13 +156,13 @@ a shear. 2. The 12 by 12 square is sheared by a factor of 1.5 in the Y direction, producing an image that is bounded by a 12 by 18 pixel square. -.. image:: texturesidesteps.png +.. image:: blockrendering/texturesidesteps.png :alt: Texture being sheared for the side of the cube. This image is simply flipped along the horizontal axis for the other visible side of the cube. -.. image:: cube_sides.png +.. image:: blockrendering/cube_sides.png :alt: The sides of the block Again, shown on the left are the two sides of the block at actual size, the @@ -177,7 +177,7 @@ However, notice from the middle of the three images in the sequence below that the images as transformed don't fit together exactly. There is some overlap when put in the 24 by 24 box in which they must fit. -.. image:: cube_parts.png +.. image:: blockrendering/cube_parts.png :alt: How the cube parts fit together There is one more complication. The cubes don't tessellate perfectly. This @@ -185,7 +185,7 @@ diagram illustrates when a cube is positioned next to another. The lower cubes are 18 pixels lower and 12 pixels to either side, which is half the width and 3/4 the height respectively. -.. image:: tessellation.png +.. image:: blockrendering/tessellation.png :alt: Cubes don't tessellate perfectly The solution is to manually touch up those 6 pixels. 3 pixels are added on the @@ -194,7 +194,7 @@ perfectly! This is done at the end of :func:`textures._build_block` -.. image:: pixelfix.png +.. image:: blockrendering/pixelfix.png :alt: The 6 pixels manually added to each cube. Other Cube Types @@ -336,6 +336,128 @@ Tile Rendering ============== .. Covers the placement of chunk images on a tile +So now that we know how to draw a single chunk, we can move on to placing them +on an image. + +For the diagrams in this section, we are positioning an entire chunk, but +frequently, only the top face of the chunk is drawn (shown in green below). + +.. image:: tilerendering/topofchunk.png + :alt: The top of a chunk is highlighted + +This makes it easier and less cumbersome to describe chunk positionings. Just +remember that chunks extend down for 1536 more pixels. + +Chunk Addressing +---------------- + +Chunks in Minecraft have an X,Z address, starting at 0,0 and extending to +positive and negative infinity on both axes. Since we're looking at things +diagonally, however, we need a way of addressing these chunks in the final +image. For that, we refer to them in rows and columns. Consider this grid +showing the tops of a five by five region of chunks, labeled with their in-game +addresses. + +.. image:: tilerendering/chunkgrid.png + :alt: A grid of 5x5 chunks showing how chunks are addressed. + +Now, we want to transform each chunk to a row/column address as shown here: + +.. image:: tilerendering/chunkgridwithrowcol.png + :alt: A grid of 5x5 chunks showing how chunks are addressed. + +So the chunk at address 0,0 would be at col 0, row 0; while the chunk at address +1,1 would be at col 2, row 0. The intersection of the red and green lines +addresses the chunk in col,row format. + +Notice that as a consequence of this addressing scheme, there is no chunk at +e.g. column 1 row 0. There are some col,row addresses that lie between chunks +(as can be seen where the red/green lines intersect at a chunk boundary instead +of the middle of a chunk). Something to keep in mind. + +So how does one translate between them? It turns out that a chunk's column +address is simply the sum of the X and the Z coordinate, while the row is the +difference. Try it! + +:: + + col = X + Z + row = Z - X + + X = (col - row) / 2 + Z = (col + row) / 2 + +Chunk Positioning +----------------- +Again just looking at the top of a chunk, we can work out how to position them +relative to each other. This is similar to how to position blocks relative to +each other, but this time, for chunks. + +A chunk's top face is 384 pixels wide by 192 pixels tall. Similar to the block, +neighboring chunks have these relationships: + +.. image:: tilerendering/chunkpositioning.png + :alt: Chunk positioning diagram + +But that's all pretty trivial. With this knowledge, we could draw the chunks at +the above offsets in one large image, but for large worlds, that would quickly +become too much to handle. (Early versions of the Overviewer did this, but the +large, unwieldy images quickly motivated the development of rendering to +individual tiles) + +Tile Layout +----------- + +Instead of rendering to one large image, chunks are rendered to small tiles. +Only a handful of chunks need to be rendered into each tile. The downside is +that chunks must be rendered multiple times for each tile they appear in, but +the upside is that arbitrarily sized maps can be viewed. + +The Overviewer uses a tile size of 384 by 384 pixels. This is the same as a +width of a chunk and is no coincidence. Just considering the top face of a +chunk, 8 chunks get rendered into a tile in this configuration: + +.. image:: tilerendering/chunksintile.png + :alt: The 8 chunks that get rendered into a tile + +So the overall strategy is to convert all chunks into diagonal col,row +coordinates, then for each tile decide which chunks belong in it, then render +them in the appropriate place on the tile. + +The rendering routines are actually passed a range of chunks to render, e.g. +rows 4-6, cols 20-24. The lower bound col,row chunk given in the range is +rendered at position 0,0 in the diagram above. That is, at offset -192,-96 +pixels. + +The rendering routines takes the given range of columns and rows, converts it +back into chunk coordinates, and renders the given 8 chunks plus all chunks from +the 16 rows above the given range (see the note below). The chunks are +positioned correctly with the above positioning rules, so any chunks that are +out of the bounds get rendered off the tile and don't affect the final image. +(There is therefore no penalty for rendering out-of-bounds chunks for a tile +except increased processing) + +.. note:: + + Remember that chunks are actually very tall, so there are actually several + rows above 0 in the above diagram that are rendered into the tile. Since the + chunk outlines in the diagrams are only the top face of the chunk, they most + likely don't contribute to the image since chunks usually don't have + anything to render way up at the top near the sky. + +Since every other column of chunks is half-way in two tiles, they must be +rendered twice. Each neighboring tile is therefore only 2 columns over, not 3 as +one may suspect at first. Same goes for the rows: The next tile down is 4 rows +down, not 5. + +Quadtrees +========= +.. About the tile output + +get_range_by_path +----------------- +.. Explain the quadtree.QuadtreeGen._get_range_by_path method + Reading the Data Files ====================== .. diff --git a/docs/design/tilerendering/chunkgrid.png b/docs/design/tilerendering/chunkgrid.png new file mode 100644 index 0000000..53f793d Binary files /dev/null and b/docs/design/tilerendering/chunkgrid.png differ diff --git a/docs/design/tilerendering/chunkgrid.svg b/docs/design/tilerendering/chunkgrid.svg new file mode 100644 index 0000000..1b53af5 --- /dev/null +++ b/docs/design/tilerendering/chunkgrid.svg @@ -0,0 +1,529 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + 0,0 + 1,1 + 2,2 + -1,-1 + -2,-2 + 1,-1 + 2,-2 + -1,1 + -2,2 + 0,1 + 0,2 + -1,2 + 1,0 + 2,-1 + 2,0 + 2,1 + 1,2 + -2,1 + -1,0 + -2,0 + 0,-1 + 0,-2 + 1,-2 + -1,-2 + -2,-1 + X + Z + + + + diff --git a/docs/design/tilerendering/chunkgridwithrowcol.png b/docs/design/tilerendering/chunkgridwithrowcol.png new file mode 100644 index 0000000..6f23725 Binary files /dev/null and b/docs/design/tilerendering/chunkgridwithrowcol.png differ diff --git a/docs/design/tilerendering/chunkgridwithrowcol.svg b/docs/design/tilerendering/chunkgridwithrowcol.svg new file mode 100644 index 0000000..0b94f76 --- /dev/null +++ b/docs/design/tilerendering/chunkgridwithrowcol.svg @@ -0,0 +1,786 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + 0,0 + + 1,1 + + 2,2 + + -1,-1 + + -2,-2 + + 1,-1 + + 2,-2 + -1,1 + -2,2 + 0,1 + 0,2 + -1,2 + 1,0 + 2,-1 + 2,0 + 2,1 + 1,2 + -2,1 + -1,0 + + -2,0 + + 0,-1 + + 1,-2 + + -1,-2 + + -2,-1 + Col 0 + Col 1 + Col 2 + Col 3 + Col 4 + Col -4 + Col -3 + Col -2 + Col -1 + Row 0 + Row 1 + Row 2 + Row 3 + Row 4 + + Row -1 + + Row -2 + + Row -3 + Row -4 + 0,-2 + + diff --git a/docs/design/tilerendering/chunkpositioning.png b/docs/design/tilerendering/chunkpositioning.png new file mode 100644 index 0000000..da21b39 Binary files /dev/null and b/docs/design/tilerendering/chunkpositioning.png differ diff --git a/docs/design/tilerendering/chunkpositioning.svg b/docs/design/tilerendering/chunkpositioning.svg new file mode 100644 index 0000000..9b608b9 --- /dev/null +++ b/docs/design/tilerendering/chunkpositioning.svg @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 384 + 192 + + + + + + + + + + + (1536) + + + + 192 + 96 + + + + + + + + + + 384 + + + + + + + + 192 + + + + + + diff --git a/docs/design/tilerendering/chunksintile.png b/docs/design/tilerendering/chunksintile.png new file mode 100644 index 0000000..9dc9cce Binary files /dev/null and b/docs/design/tilerendering/chunksintile.png differ diff --git a/docs/design/tilerendering/chunksintile.svg b/docs/design/tilerendering/chunksintile.svg new file mode 100644 index 0000000..1fbd5d0 --- /dev/null +++ b/docs/design/tilerendering/chunksintile.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + Cols: + 0 + 1 + 2 + Rows: + 0 + 1 + 2 + 3 + 4 + 384 + + + + + 384 + + + + + + + + diff --git a/docs/design/tilerendering/topofchunk.png b/docs/design/tilerendering/topofchunk.png new file mode 100644 index 0000000..c67978c Binary files /dev/null and b/docs/design/tilerendering/topofchunk.png differ diff --git a/docs/design/tilerendering/topofchunk.svg b/docs/design/tilerendering/topofchunk.svg new file mode 100644 index 0000000..5cbeb24 --- /dev/null +++ b/docs/design/tilerendering/topofchunk.svg @@ -0,0 +1,4258 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + (Actual ChunkProportions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (DiagramFrom above) + + + + diff --git a/docs/faq.rst b/docs/faq.rst index f9790a1..213d1d9 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -2,22 +2,53 @@ Frequently Asked Questions ========================== -**The full map doesn't display even when fully zoomed out!** - Are you using the :option:`-z` or :option:`--zoom <-z>` option on your commandline or - in settings.py? If so, try removing it, or increasing the value you set. - It's quite likely you don't need it at all. See the documentation for the - :option:`zoom <-z>` option. +.. contents:: + :local: -**You've added a few feature, but it's not showing up on my map!** - Some new features will only show up in newly-rendered areas. Use the - :option:`--forcerender` option to update the entire map. +General Questions +================= -**How do I use this on CentOS 5?** - CentOS 5 comes with Python 2.4, but the Overviewer needs 2.6 or higher. See - the special instructions at :ref:`centos` +The full map doesn't display even when fully zoomed out! +-------------------------------------------------------- -**The background color of the map is black, and I don't like it!** - You can change this by using the :option:`--bg-color` command line option, or - ``bg_color`` in settings.py. See the :doc:`options` page for more - details. +Are you using the :option:`-z` or :option:`--zoom <-z>` option on your +commandline or in settings.py? If so, try removing it, or increasing the value +you set. It's quite likely you don't need it at all. See the documentation for +the :option:`zoom <-z>` option. +You've added a few feature, but it's not showing up on my map! +-------------------------------------------------------------- + +Some new features will only show up in newly-rendered areas. Use the +:option:`--forcerender` option to update the entire map. + +How do I use this on CentOS 5? +------------------------------ + +CentOS 5 comes with Python 2.4, but the Overviewer needs 2.6 or higher. See the +special instructions at :ref:`centos` + +The background color of the map is black, and I don't like it! +-------------------------------------------------------------- + +You can change this by using the :option:`--bg-color` command line option, or +``bg_color`` in settings.py. See the :doc:`options` page for more details. + +I downloaded the Windows version but when I double-click it, the window closes real fast. +----------------------------------------------------------------------------------------- + +The Overviewer is a command line program and must be run from a command line. It +is necessary to become at least a little familiar with a command line to run The +Overviewer (if you have no interest in this, perhaps this isn't the mapping +program for you). + +Unfortunately, A full tutorial of the Windows command line is out of scope for this +documentation; consult the almighty Google for tutorials and information on +the Windows command line. (If you would like to contribute a short tutorial to +these docs, please do!) + +Batch files are another easy way to run the Overviewer without messing with +command lines, but information on how to do this has also not been written. + +On a related note, we also welcome contributions for a graphical interface for +the Overviewer. diff --git a/docs/index.rst b/docs/index.rst index 57b1663..b471a14 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,6 +6,10 @@ The Minecraft Overviewer ======================== +See also the `Github Homepage`_ + +.. _Github Homepage: https://github.com/overviewer/Minecraft-Overviewer + Introduction ============ The Minecraft Overviewer is a command-line tool for rendering high-resolution @@ -54,8 +58,9 @@ Windows, Mac, and Linux as long as you have these software packages installed: * Either a Minecraft Client installed or a terrain.png for the textures. -There are additional requirements for compiling it. More details are available -in either the :doc:`Building ` or :doc:`Installing ` pages. +The first three are included in the Windows download. Also, there are additional +requirements for compiling it (like a compiler). More details are available in +either the :doc:`Building ` or :doc:`Installing ` pages. Getting Started =============== @@ -71,7 +76,9 @@ packages and don't want to have to compile anything yourself**, head to the **If you would like to build the Overviewer from source yourself (it's not that bad)**, head to the :doc:`Building ` page. -.. _Github Homepage: https://github.com/overviewer/Minecraft-Overviewer +**For all other platforms** you will need to build it yourself. +:doc:`building`. + Help ==== @@ -92,8 +99,8 @@ Documentation Contents .. toctree:: :maxdepth: 2 - building installing + building running options faq diff --git a/docs/installing.rst b/docs/installing.rst index a9b5b4b..aeb41cd 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -3,8 +3,9 @@ Installing ========== This page is for installing the pre-compiled binary versions of the Overviewer. -If you have built the Overviewer from source yourself, head back to -:doc:`Building `. +If you want to build the Overviewer from source yourself, head to :doc:`Building +`. If you have already built The Overviewer, proceed to +:doc:`running`. Windows @@ -18,8 +19,7 @@ right place! For 64 bit, you'll want these instead: `VC++ 2008 `_ and `VC++ 2010 `_ -3. That's it! Proceed with instructions on :doc:`Running ` the - Overviewer. +3. That's it! Proceed with instructions on :doc:`running`. Debian / Ubuntu =============== diff --git a/docs/running.rst b/docs/running.rst index e5d873c..adc93c7 100644 --- a/docs/running.rst +++ b/docs/running.rst @@ -137,3 +137,23 @@ modification times intact, use ``cp -p``. For people who render from backups, GNU ``tar`` automatically handles modification times correctly. ``rsync -a`` will handle this correctly as well. If you use some other tool, you'll have to figure out how to do this yourself. + +Biome Support +============= + +Minecraft Overviewer has support for using the biome info from the `Minecraft +Biome Extractor`_. If you run the biome extractor on your world, during the +next run Overviewer will automatically recognize the biome info and use it to +colorize your grass and leaves appropriately. This will only appear on updated +chunks, though; to colorize the entire world you will need to re-render from +scratch by using :option:`--forcerender` + +.. note:: + + as of Minecraft 1.8, you currently need to use a patched Biome Extractor + that can be found `here + `_, + or `here on GitHub + `_. + +.. _Minecraft Biome Extractor: http://www.minecraftforum.net/viewtopic.php?f=25&t=80902 diff --git a/overviewer.py b/overviewer.py index 3399424..6992fdc 100755 --- a/overviewer.py +++ b/overviewer.py @@ -15,7 +15,25 @@ # You should have received a copy of the GNU General Public License along # with the Overviewer. If not, see . +import platform import sys + +if platform.system() == 'Windows': + try: + import ctypes + GetConsoleProcessList = ctypes.windll.kernel32.GetConsoleProcessList + num = GetConsoleProcessList(ctypes.byref(ctypes.c_int(0)), ctypes.c_int(1)) + if (num == 1): + print "The Overviewer is a console program. Please open a Windows command prompt" + print "first and run Overviewer from there. Further documentation is available at" + print "http://docs.overviewer.org/\n" + print "Press [Enter] to close this window." + raw_input() + sys.exit(1) + + except Exception: + pass + if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6): print "Sorry, the Overviewer requires at least Python 2.6 to run" if sys.version_info[0] >= 3: @@ -29,7 +47,6 @@ import subprocess import multiprocessing import time import logging -import platform from overviewer_core import util logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s") @@ -139,7 +156,7 @@ def main(): print "Git commit: %s" % overviewer_version.HASH print "built on %s" % overviewer_version.BUILD_DATE print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS) - except: + except Exception: print "version info not found" pass sys.exit(0) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index e1b454b..06a2048 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -125,27 +125,6 @@ def get_tileentity_data(level): data = level['TileEntities'] return data -# This set holds blocks ids that can be seen through, for occlusion calculations -transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, - 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109, 111, 113, 114, 115, 116, 117, 118, - 119, 120]) - -# This set holds block ids that are solid blocks -solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, - 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91, 95, 97, 98, - 99, 100, 103, 108, 109, 110, 112, 114, 121]) - -# This set holds block ids that are fluid blocks -fluid_blocks = set([8,9,10,11]) - -# This set holds block ids that are not candidates for spawning mobs on -# (glass, slabs, stairs, fluids, ice, pistons, webs,TNT, wheat, cactus, iron bars, glass planes, fences, fence gate, cake, bed, repeaters, trapdoor) -nospawn_blocks = set([20,26, 29, 30, 33, 34, 44, 46, 53, 59, 67, 79, 81, 85, 92, 93, 94, 96, 107, 109, 101, 102]).union(fluid_blocks) - class ChunkCorrupt(Exception): pass @@ -414,7 +393,7 @@ def generate_facemasks(): left = Image.new("L", (24,24), 0) whole = Image.new("L", (24,24), 0) - toppart = textures.transform_image(white) + toppart = textures.transform_image_top(white) leftpart = textures.transform_image_side(white) # using the real PIL paste here (not alpha_over) because there is diff --git a/overviewer_core/configParser.py b/overviewer_core/configParser.py index ff685ba..795fd54 100644 --- a/overviewer_core/configParser.py +++ b/overviewer_core/configParser.py @@ -196,7 +196,7 @@ class ConfigOptionParser(object): sys.exit(1) return value elif a['type'] == "float": - return long(value) + return float(value) elif a['type'] == "complex": return complex(value) elif a['type'] == "function": diff --git a/overviewer_core/data/web_assets/overviewer.js b/overviewer_core/data/web_assets/overviewer.js index 34fbfe8..02fa8d9 100644 --- a/overviewer_core/data/web_assets/overviewer.js +++ b/overviewer_core/data/web_assets/overviewer.js @@ -261,6 +261,9 @@ var overviewer = { google.maps.event.addListener(overviewer.map, 'zoom_changed', function() { overviewer.util.updateHash(); }); + google.maps.event.addListener(overviewer.map, 'dblclick', function() { + overviewer.util.updateHash(); + }); // Make the link again whenever the map changes google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function() { diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 545f14f..47e175b 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -20,8 +20,7 @@ static PyObject *textures = NULL; static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; -static PyObject *special_blocks = NULL; -static PyObject *specialblockmap = NULL; +static PyObject *known_blocks = NULL; static PyObject *transparent_blocks = NULL; PyObject *init_chunk_render(PyObject *self, PyObject *args) { @@ -47,13 +46,10 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { blockmap = PyObject_GetAttrString(textures, "blockmap"); if (!blockmap) return NULL; - special_blocks = PyObject_GetAttrString(textures, "special_blocks"); - if (!special_blocks) + known_blocks = PyObject_GetAttrString(textures, "known_blocks"); + if (!known_blocks) return NULL; - specialblockmap = PyObject_GetAttrString(textures, "specialblockmap"); - if (!specialblockmap) - return NULL; - transparent_blocks = PyObject_GetAttrString(chunk_mod, "transparent_blocks"); + transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; @@ -64,6 +60,10 @@ int is_transparent(unsigned char b) { PyObject *block = PyInt_FromLong(b); int ret = PySequence_Contains(transparent_blocks, block); + if (!ret) + { + ret = !(PySequence_Contains(known_blocks, block)); + } Py_DECREF(block); return ret; @@ -381,6 +381,9 @@ chunk_render(PyObject *self, PyObject *args) { state.imgy = yoff - state.x*6 + state.y*6 + 128*12 + 15*6; for (state.z = 0; state.z < 128; state.z++) { + PyObject *tmp; + unsigned char ancilData; + state.imgy -= 12; /* get blockid */ @@ -404,56 +407,48 @@ chunk_render(PyObject *self, PyObject *args) { } blockid = PyInt_FromLong(state.block); - // check for occlusion + /* check for occlusion */ if (render_mode_occluded(rendermode, state.x, state.y, state.z)) { continue; } - // everything stored here will be a borrowed ref + /* everything stored here will be a borrowed ref */ - /* get the texture and mask from block type / ancil. data */ - if (!PySequence_Contains(special_blocks, blockid)) { - /* t = textures.blockmap[blockid] */ - t = PyList_GetItem(blockmap, state.block); + ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); + state.block_data = ancilData; + /* block that need pseudo ancildata: + * grass, water, glass, chest, restone wire, + * ice, fence, portal, iron bars, glass panes */ + if ((state.block == 2) || (state.block == 9) || + (state.block == 20) || (state.block == 54) || + (state.block == 55) || (state.block == 79) || + (state.block == 85) || (state.block == 90) || + (state.block == 101) || (state.block == 102) || + (state.block == 113)) { + ancilData = generate_pseudo_data(&state, ancilData); + state.block_pdata = ancilData; } else { - PyObject *tmp; - - unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); - state.block_data = ancilData; - /* block that need pseudo ancildata: - * grass, water, glass, chest, restone wire, - * ice, fence, portal, iron bars, glass panes */ - if ((state.block == 2) || (state.block == 9) || - (state.block == 20) || (state.block == 54) || - (state.block == 55) || (state.block == 79) || - (state.block == 85) || (state.block == 90) || - (state.block == 101) || (state.block == 102) || - (state.block == 113)) { - ancilData = generate_pseudo_data(&state, ancilData); - state.block_pdata = ancilData; - } else { - state.block_pdata = 0; - } - - tmp = PyTuple_New(2); - - Py_INCREF(blockid); /* because SetItem steals */ - PyTuple_SetItem(tmp, 0, blockid); - PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); - - /* this is a borrowed reference. no need to decref */ - t = PyDict_GetItem(specialblockmap, tmp); - Py_DECREF(tmp); + state.block_pdata = 0; } + tmp = PyTuple_New(2); + + Py_INCREF(blockid); /* because SetItem steals */ + PyTuple_SetItem(tmp, 0, blockid); + PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); + + /* this is a borrowed reference. no need to decref */ + t = PyDict_GetItem(blockmap, tmp); + Py_DECREF(tmp); + /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { PyObject *src, *mask, *mask_light; int randx = 0, randy = 0; src = PyTuple_GetItem(t, 0); - mask = PyTuple_GetItem(t, 1); - mask_light = PyTuple_GetItem(t, 2); + mask = PyTuple_GetItem(t, 0); + mask_light = PyTuple_GetItem(t, 1); if (mask == Py_None) mask = src; diff --git a/overviewer_core/src/rendermode-overlay.c b/overviewer_core/src/rendermode-overlay.c index 17b760b..72e58ff 100644 --- a/overviewer_core/src/rendermode-overlay.c +++ b/overviewer_core/src/rendermode-overlay.c @@ -38,8 +38,8 @@ rendermode_overlay_start(void *data, RenderState *state, PyObject *options) { self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); - self->solid_blocks = PyObject_GetAttrString(state->chunk, "solid_blocks"); - self->fluid_blocks = PyObject_GetAttrString(state->chunk, "fluid_blocks"); + self->solid_blocks = PyObject_GetAttrString(state->textures, "solid_blocks"); + self->fluid_blocks = PyObject_GetAttrString(state->textures, "fluid_blocks"); self->get_color = get_color; diff --git a/overviewer_core/src/rendermode-spawn.c b/overviewer_core/src/rendermode-spawn.c index a5c0a27..c303f23 100644 --- a/overviewer_core/src/rendermode-spawn.c +++ b/overviewer_core/src/rendermode-spawn.c @@ -72,7 +72,7 @@ rendermode_spawn_start(void *data, RenderState *state, PyObject *options) { /* now do custom initializations */ self = (RenderModeSpawn *)data; - self->nospawn_blocks = PyObject_GetAttrString(state->chunk, "nospawn_blocks"); + self->nospawn_blocks = PyObject_GetAttrString(state->textures, "nospawn_blocks"); self->blocklight = PyObject_GetAttrString(state->self, "blocklight"); self->skylight = PyObject_GetAttrString(state->self, "skylight"); diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 12840cb..39d4739 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -24,11 +24,26 @@ from random import randint import numpy from PIL import Image, ImageEnhance, ImageOps, ImageDraw import logging +import functools import util import composite +## +## useful global variables +## + +# user-provided path given by --texture-path _find_file_local_path = None +# image background color to use +bgcolor = None +# an array of the textures in terrain.png, split up +terrain_images = None + +## +## Helpers for opening textures +## + def _find_file(filename, mode="rb", verbose=False): """Searches for the given file and returns an open handle to it. This searches the following locations in this order: @@ -106,9 +121,6 @@ def _load_image(filename): buffer = StringIO(fileobj.read()) return Image.open(buffer).convert("RGBA") -def _get_terrain_image(): - return _load_image("terrain.png") - def _split_terrain(terrain): """Builds and returns a length 256 array of each 16x16 chunk of texture""" textures = [] @@ -129,7 +141,11 @@ def _split_terrain(terrain): return textures -def transform_image(img, blockID=None): +## +## Image Transformation Functions +## + +def transform_image_top(img): """Takes a PIL image and rotates it left 45 degrees and shrinks the y axis by a factor of 2. Returns the resulting image, which will be 24x12 pixels @@ -157,25 +173,10 @@ def transform_image(img, blockID=None): newimg = img.transform((24,12), Image.AFFINE, transform) return newimg -def transform_image_side(img, blockID=None): +def transform_image_side(img): """Takes an image and shears it for the left side of the cube (reflect for the right side)""" - if blockID in (44,): # step block - # make the top half transparent - # (don't just crop img, since we want the size of - # img to be unchanged - mask = img.crop((0,8,16,16)) - n = Image.new(img.mode, img.size, bgcolor) - composite.alpha_over(n, mask,(0,0,16,8), mask) - img = n - if blockID in (78,): # snow - # make the top three quarters transparent - mask = img.crop((0,12,16,16)) - n = Image.new(img.mode, img.size, bgcolor) - composite.alpha_over(n, mask,(0,12,16,16), mask) - img = n - # Size of the cube side before shear img = img.resize((12,12), Image.ANTIALIAS) @@ -188,7 +189,7 @@ def transform_image_side(img, blockID=None): newimg = img.transform((12,18), Image.AFFINE, transform) return newimg -def transform_image_slope(img, blockID=None): +def transform_image_slope(img): """Takes an image and shears it in the shape of a slope going up in the -y direction (reflect for +x direction). Used for minetracks""" @@ -205,7 +206,7 @@ def transform_image_slope(img, blockID=None): return newimg -def transform_image_angle(img, angle, blockID=None): +def transform_image_angle(img, angle): """Takes an image an shears it in arbitrary angle with the axis of rotation being vertical. @@ -245,7 +246,7 @@ def transform_image_angle(img, angle, blockID=None): return newimg -def _build_block(top, side, blockID=None): +def build_block(top, side): """From a top texture and a side texture, build a block image. top and side should be 16x16 image objects. Returns a 24x24 image @@ -253,13 +254,13 @@ def _build_block(top, side, blockID=None): img = Image.new("RGBA", (24,24), bgcolor) original_texture = top.copy() - top = transform_image(top, blockID) + top = transform_image_top(top) if not side: composite.alpha_over(img, top, (0,0), top) return img - side = transform_image_side(side, blockID) + side = transform_image_side(side) otherside = side.transpose(Image.FLIP_LEFT_RIGHT) # Darken the sides slightly. These methods also affect the alpha layer, @@ -272,40 +273,9 @@ def _build_block(top, side, blockID=None): otherside = ImageEnhance.Brightness(otherside).enhance(0.8) otherside.putalpha(othersidealpha) - ## special case for tall-grass, fern, dead shrub, pumpkin/melon stem - ## and nether wart - if blockID in (31,32,104,105,115): - front = original_texture.resize((14,11), Image.ANTIALIAS) - composite.alpha_over(img, front, (5,9)) - return img - - ## special case for non-block things - if blockID in (37,38,6,39,40,83,30,120): ## flowers, sapling, mushrooms, reeds, web, broken air portal frame(data = 1) - # - # instead of pasting these blocks at the cube edges, place them in the middle: - # and omit the top - composite.alpha_over(img, side, (6,3), side) - composite.alpha_over(img, otherside, (6,3), otherside) - return img - - if blockID in (81,): # cacti! - composite.alpha_over(img, side, (1,6), side) - composite.alpha_over(img, otherside, (11,6), otherside) - composite.alpha_over(img, top, (0,0), top) - elif blockID in (44,): # half step - # shift each texture down 6 pixels - composite.alpha_over(img, side, (0,12), side) - composite.alpha_over(img, otherside, (12,12), otherside) - composite.alpha_over(img, top, (0,6), top) - elif blockID in (78,): # snow - # shift each texture down 9 pixels - composite.alpha_over(img, side, (0,6), side) - composite.alpha_over(img, otherside, (12,6), otherside) - composite.alpha_over(img, top, (0,9), top) - else: - composite.alpha_over(img, top, (0,0), top) - composite.alpha_over(img, side, (0,6), side) - composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,0), top) + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) # Manually touch up 6 pixels that leave a gap because of how the # shearing works out. This makes the blocks perfectly tessellate-able @@ -318,8 +288,7 @@ def _build_block(top, side, blockID=None): return img - -def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None): +def build_full_block(top, side1, side2, side3, side4, bottom=None): """From a top texture, a bottom texture and 4 different side textures, build a full block with four differnts faces. All images should be 16x16 image objects. Returns a 24x24 image. Can be used to render any block. @@ -364,7 +333,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None # first back sides if side1 != None : - side1 = transform_image_side(side1, blockID) + side1 = transform_image_side(side1) side1 = side1.transpose(Image.FLIP_LEFT_RIGHT) # Darken this side. @@ -376,7 +345,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None if side2 != None : - side2 = transform_image_side(side2, blockID) + side2 = transform_image_side(side2) # Darken this side. sidealpha2 = side2.split()[3] @@ -386,12 +355,12 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side2, (12,0), side2) if bottom != None : - bottom = transform_image(bottom, blockID) + bottom = transform_image_top(bottom) composite.alpha_over(img, bottom, (0,12), bottom) # front sides if side3 != None : - side3 = transform_image_side(side3, blockID) + side3 = transform_image_side(side3) # Darken this side sidealpha = side3.split()[3] @@ -401,7 +370,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side3, (0,6), side3) if side4 != None : - side4 = transform_image_side(side4, blockID) + side4 = transform_image_side(side4) side4 = side4.transpose(Image.FLIP_LEFT_RIGHT) # Darken this side @@ -412,104 +381,31 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side4, (12,6), side4) if top != None : - top = transform_image(top, blockID) + top = transform_image_top(top) composite.alpha_over(img, top, (0, increment), top) return img +def build_sprite(side): + """From a side texture, create a sprite-like texture such as those used + for spiderwebs or flowers.""" + img = Image.new("RGBA", (24,24), bgcolor) + + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + composite.alpha_over(img, side, (6,3), side) + composite.alpha_over(img, otherside, (6,3), otherside) + return img -def _build_blockimages(): - """Returns a mapping from blockid to an image of that block in perspective - The values of the mapping are actually (image in RGB mode, alpha channel). - This is not appropriate for all block types, only block types that are - proper cubes""" - - # Top textures of all block types. The number here is the index in the - # texture array (terrain_images), which comes from terrain.png's cells, left to right top to - # bottom. - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - topids = [ -1, 1, 0, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33, - # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - 34, -1, 52, 48, -1,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1, - # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4, - # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - 36, 37, -1, -1, 65, -1, -1, -1, 50, 24, -1, -1, 86, -1, -1, -1, - # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, - # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 69, 72, 73, 75, -1, 102,103,104,105,-1, 102, -1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 - -1, -1, -1, -1, -1, -1, -1, 137,-1, -1, -1, -1, -1, -1, 78, -1, - # 112 113 114 115 116 117 118 119 120 121 - 224, -1, -1, -1, -1, -1, -1, -1, -1,175 - ] - - # NOTE: For non-block textures, the sideid is ignored, but can't be -1 - - # And side textures of all block types - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - sideids = [ -1, 1, 3, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33, - # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - 34, -1, 52, 48, -1,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1, - # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35, - # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - 36, 37, -1, -1, 65, -1, -1,101, 50, 24, -1, -1, 86, -1, -1, -1, - # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, - # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 70, 72, 73, 74, -1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 - -1, -1, -1, -1, -1 ,-1 ,-1, 136,-1, -1, -1, -1, -1, -1, 77, -1, - # 112 113 114 115 116 117 118 119 120 121 - 224, -1, -1, -1, -1, -1, -1, -1, -1,175 - ] - - # This maps block id to the texture that goes on the side of the block - if len(topids) != len(sideids): - raise Exception("mismatched lengths") - - allimages = [] - for toptextureid, sidetextureid,blockID in zip(topids, sideids,range(len(topids))): - if toptextureid == -1 or sidetextureid == -1: - allimages.append(None) - continue - - toptexture = terrain_images[toptextureid] - sidetexture = terrain_images[sidetextureid] - - ## _build_block needs to know about the block ID, not just the textures - ## of the block or the texture ID - img = _build_block(toptexture, sidetexture, blockID) - - allimages.append(generate_texture_tuple(img, blockID)) - - # Future block types: - while len(allimages) < 256: - allimages.append(None) - return allimages - -def load_water(): - """Evidentially, the water and lava textures are not loaded from any files - in the jar (that I can tell). They must be generated on the fly. While - terrain.png does have some water and lava cells, not all texture packs - include them. So I load them here from a couple pngs included. - - This mutates the blockmap global list with the new water and lava blocks. - Block 9, standing water, is given a block with only the top face showing. - Block 8, flowing water, is given a full 3 sided cube.""" - - watertexture = _load_image("water.png") - w1 = _build_block(watertexture, None) - blockmap[9] = generate_texture_tuple(w1,9) - w2 = _build_block(watertexture, watertexture) - blockmap[8] = generate_texture_tuple(w2,8) - - lavatexture = _load_image("lava.png") - lavablock = _build_block(lavatexture, lavatexture) - blockmap[10] = generate_texture_tuple(lavablock,10) - blockmap[11] = blockmap[10] +def build_billboard(tex): + """From a texture, create a billboard-like texture such as those used for + tall grass or melon stems.""" + img = Image.new("RGBA", (24,24), bgcolor) + + front = tex.resize((14, 11), Image.ANTIALIAS) + composite.alpha_over(img, front, (5,9)) + return img def generate_opaque_mask(img): """ Takes the alpha channel of the image and generates a mask @@ -519,1847 +415,29 @@ def generate_opaque_mask(img): alpha = img.split()[3] return alpha.point(lambda a: int(min(a, 25.5) * 10)) -def generate_texture_tuple(img, blockid): - """ This takes an image and returns the needed tuple for the - blockmap list and specialblockmap dictionary.""" - return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) - -def generate_special_texture(blockID, data): - """Generates a special texture, such as a correctly facing minecraft track""" - - data = convert_data(blockID, data) - - # blocks need to be handled here (and in chunk.py) - - if blockID == 2: # grass - # data & 0x10 means SNOW sides - side_img = terrain_images[3] - if data & 0x10: - side_img = terrain_images[68] - img = _build_block(terrain_images[0], side_img, 2) - if not data & 0x10: - global biome_grass_texture - composite.alpha_over(img, biome_grass_texture, (0, 0), biome_grass_texture) - return generate_texture_tuple(img, blockID) - - - if blockID == 6: # saplings - # The bottom two bits are used fo the sapling type, the top two - # bits are used as a grow-counter for the tree. - - if data & 0x3 == 0: # usual saplings - toptexture = terrain_images[15] - sidetexture = terrain_images[15] - - if data & 0x3 == 1: # spruce sapling - toptexture = terrain_images[63] - sidetexture = terrain_images[63] - - if data & 0x3 == 2: # birch sapling - toptexture = terrain_images[79] - sidetexture = terrain_images[79] - - if data & 0x3 == 3: # unused usual sapling - toptexture = terrain_images[15] - sidetexture = terrain_images[15] - - img = _build_block(toptexture, sidetexture, blockID) - return generate_texture_tuple(img, blockID) - - - if blockID == 9 or blockID == 20 or blockID == 79: # spring water, flowing water and waterfall water, AND glass, AND ice - # water,glass and ice share the way to be rendered - if blockID == 9: - texture = _load_image("water.png") - elif blockID == 20: - texture = terrain_images[49] - else: - texture = terrain_images[67] - - if (data & 0b10000) == 16: - top = texture - - else: top = None - - if (data & 0b0001) == 1: - side1 = texture # top left - else: side1 = None - - if (data & 0b1000) == 8: - side2 = texture # top right - else: side2 = None - - if (data & 0b0010) == 2: - side3 = texture # bottom left - else: side3 = None - - if (data & 0b0100) == 4: - side4 = texture # bottom right - else: side4 = None - - # if nothing shown do not draw at all - if top == side3 == side4 == None: - return None - - img = _build_full_block(top,None,None,side3,side4) - return generate_texture_tuple(img, blockID) - - - if blockID == 17: # wood: normal, birch and pines - top = terrain_images[21] - if data == 0: - side = terrain_images[20] - img = _build_block(top, side, 17) - if data == 1: - side = terrain_images[116] - img = _build_block(top, side, 17) - if data == 2: - side = terrain_images[117] - img = _build_block(top, side, 17) - - return generate_texture_tuple(img, blockID) - - - if blockID == 18: # leaves - t = terrain_images[52] - if data == 1: - # pine! - t = terrain_images[132] - img = _build_block(t, t, 18) - return generate_texture_tuple(img, blockID) - - - if blockID == 26: # bed - increment = 8 - left_face = None - right_face = None - if data & 0x8 == 0x8: # head of the bed - top = terrain_images[135] - if data & 0x00 == 0x00: # head pointing to West - top = top.copy().rotate(270) - left_face = terrain_images[151] - right_face = terrain_images[152] - if data & 0x01 == 0x01: # ... North - top = top.rotate(270) - left_face = terrain_images[152] - right_face = terrain_images[151] - if data & 0x02 == 0x02: # East - top = top.rotate(180) - left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) - right_face = None - if data & 0x03 == 0x03: # South - right_face = None - right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) - - else: # foot of the bed - top = terrain_images[134] - if data & 0x00 == 0x00: # head pointing to West - top = top.rotate(270) - left_face = terrain_images[150] - right_face = None - if data & 0x01 == 0x01: # ... North - top = top.rotate(270) - left_face = None - right_face = terrain_images[150] - if data & 0x02 == 0x02: # East - top = top.rotate(180) - left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) - right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT) - if data & 0x03 == 0x03: # South - left_face = terrain_images[149] - right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) - - top = (top, increment) - img = _build_full_block(top, None, None, left_face, right_face) - - return generate_texture_tuple(img, blockID) - - - if blockID == 31: # tall grass - if data == 0: # dead shrub - texture = terrain_images[55] - elif data == 1: # tall grass - texture = terrain_images[39] - elif data == 2: # fern - texture = terrain_images[56] - - img = _build_block(texture, texture, blockID) - return generate_texture_tuple(img,31) - - - if blockID in (29,33): # sticky and normal body piston. - if blockID == 29: # sticky - piston_t = terrain_images[106].copy() - else: # normal - piston_t = terrain_images[107].copy() - - # other textures - side_t = terrain_images[108].copy() - back_t = terrain_images[109].copy() - interior_t = terrain_images[110].copy() - - if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff - # remove piston texture from piston body - ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0)) - - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block(back_t ,None ,None ,side_t, side_t) - - elif data & 0x07 == 0x1: # up - img = _build_full_block((interior_t, 4) ,None ,None ,side_t, side_t) - - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t , None, None ,side_t.rotate(90), back_t) - - elif data & 0x07 == 0x3: # west - img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None) - temp = transform_image_side(interior_t, blockID) - temp = temp.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, temp, (9,5), temp) - - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270)) - temp = transform_image_side(interior_t, blockID) - composite.alpha_over(img, temp, (3,5), temp) - - elif data & 0x07 == 0x5: # south - img = _build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90)) - - else: # pushed in, normal full blocks, easy stuff - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block(back_t ,None ,None ,side_t, side_t) - elif data & 0x07 == 0x1: # up - img = _build_full_block(piston_t ,None ,None ,side_t, side_t) - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t) - elif data & 0x07 == 0x3: # west - img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270)) - elif data & 0x07 == 0x5: # south - img = _build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90)) - - - return generate_texture_tuple(img, blockID) - - - if blockID == 34: # piston extension (sticky and normal) - if (data & 0x8) == 0x8: # sticky - piston_t = terrain_images[106].copy() - else: # normal - piston_t = terrain_images[107].copy() - - # other textures - side_t = terrain_images[108].copy() - back_t = terrain_images[107].copy() - # crop piston body - ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0)) - - # generate the horizontal piston extension stick - h_stick = Image.new("RGBA", (24,24), bgcolor) - temp = transform_image_side(side_t, blockID) - composite.alpha_over(h_stick, temp, (1,7), temp) - temp = transform_image(side_t.rotate(90)) - composite.alpha_over(h_stick, temp, (1,1), temp) - # Darken it - sidealpha = h_stick.split()[3] - h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85) - h_stick.putalpha(sidealpha) - - # generate the vertical piston extension stick - v_stick = Image.new("RGBA", (24,24), bgcolor) - temp = transform_image_side(side_t.rotate(90), blockID) - composite.alpha_over(v_stick, temp, (12,6), temp) - temp = temp.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(v_stick, temp, (1,6), temp) - # Darken it - sidealpha = v_stick.split()[3] - v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85) - v_stick.putalpha(sidealpha) - - # Piston orientation is stored in the 3 first bits - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block((back_t, 12) ,None ,None ,side_t, side_t) - composite.alpha_over(img, v_stick, (0,-3), v_stick) - elif data & 0x07 == 0x1: # up - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(piston_t ,None ,None ,side_t, side_t) - composite.alpha_over(img, v_stick, (0,4), v_stick) - composite.alpha_over(img, img2, (0,0), img2) - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), None) - temp = transform_image_side(back_t, blockID).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, temp, (2,2), temp) - composite.alpha_over(img, h_stick, (6,3), h_stick) - elif data & 0x07 == 0x3: # west - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) - composite.alpha_over(img, h_stick, (0,0), h_stick) - composite.alpha_over(img, img2, (0,0), img2) - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270)) - composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) - elif data & 0x07 == 0x5: # south - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90)) - temp = transform_image_side(back_t, blockID) - composite.alpha_over(img2, temp, (10,2), temp) - composite.alpha_over(img, img2, (0,0), img2) - composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 35: # wool - if data == 0: # white - top = side = terrain_images[64] - elif data == 1: # orange - top = side = terrain_images[210] - elif data == 2: # magenta - top = side = terrain_images[194] - elif data == 3: # light blue - top = side = terrain_images[178] - elif data == 4: # yellow - top = side = terrain_images[162] - elif data == 5: # light green - top = side = terrain_images[146] - elif data == 6: # pink - top = side = terrain_images[130] - elif data == 7: # grey - top = side = terrain_images[114] - elif data == 8: # light grey - top = side = terrain_images[225] - elif data == 9: # cyan - top = side = terrain_images[209] - elif data == 10: # purple - top = side = terrain_images[193] - elif data == 11: # blue - top = side = terrain_images[177] - elif data == 12: # brown - top = side = terrain_images[161] - elif data == 13: # dark green - top = side = terrain_images[145] - elif data == 14: # red - top = side = terrain_images[129] - elif data == 15: # black - top = side = terrain_images[113] - - img = _build_block(top, side, 35) - return generate_texture_tuple(img, blockID) - - - if blockID in (43,44): # slab and double-slab - - if data == 0: # stone slab - top = terrain_images[6] - side = terrain_images[5] - elif data == 1: # stone slab - top = terrain_images[176] - side = terrain_images[192] - elif data == 2: # wooden slab - top = side = terrain_images[4] - elif data == 3: # cobblestone slab - top = side = terrain_images[16] - elif data == 4: # brick? - top = side = terrain_images[7] - elif data == 5: # stone brick? - top = side = terrain_images[54] - - img = _build_block(top, side, blockID) - return generate_texture_tuple(img, blockID) - - - if blockID in (50,75,76): # torch, off redstone torch, on redstone torch - - # choose the proper texture - if blockID == 50: # torch - small = terrain_images[80] - elif blockID == 75: # off redstone torch - small = terrain_images[115] - else: # on redstone torch - small = terrain_images[99] - - # compose a torch bigger than the normal - # (better for doing transformations) - torch = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(torch,small,(-4,-3)) - composite.alpha_over(torch,small,(-5,-2)) - composite.alpha_over(torch,small,(-3,-2)) - - # angle of inclination of the texture - rotation = 15 - - if data == 1: # pointing south - torch = torch.rotate(-rotation, Image.NEAREST) # nearest filter is more nitid. - img = _build_full_block(None, None, None, torch, None, None, blockID) - - elif data == 2: # pointing north - torch = torch.rotate(rotation, Image.NEAREST) - img = _build_full_block(None, None, torch, None, None, None, blockID) - - elif data == 3: # pointing west - torch = torch.rotate(rotation, Image.NEAREST) - img = _build_full_block(None, torch, None, None, None, None, blockID) - - elif data == 4: # pointing east - torch = torch.rotate(-rotation, Image.NEAREST) - img = _build_full_block(None, None, None, None, torch, None, blockID) - - elif data == 5: # standing on the floor - # compose a "3d torch". - img = Image.new("RGBA", (24,24), bgcolor) - - small_crop = small.crop((2,2,14,14)) - slice = small_crop.copy() - ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - composite.alpha_over(img, slice, (7,5)) - composite.alpha_over(img, small_crop, (6,6)) - composite.alpha_over(img, small_crop, (7,6)) - composite.alpha_over(img, slice, (7,7)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 51: # fire - firetexture = _load_image("fire.png") - side1 = transform_image_side(firetexture) - side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), bgcolor) - - composite.alpha_over(img, side1, (12,0), side1) - composite.alpha_over(img, side2, (0,0), side2) - - composite.alpha_over(img, side1, (0,6), side1) - composite.alpha_over(img, side2, (12,6), side2) - - return generate_texture_tuple(img, blockID) - - - if blockID in (53,67, 108, 109, 114): # wooden, stone brick, cobblestone and netherbrick stairs. - - if blockID == 53: # wooden - texture = terrain_images[4] - elif blockID == 67: # cobblestone - texture = terrain_images[16] - elif blockID == 108: # red brick stairs - texture = terrain_images[7] - elif blockID == 109: # stone brick stairs - texture = terrain_images[54] - elif blockID == 114: # netherbrick stairs - texture = terrain_images[224] - - side = texture.copy() - half_block_u = texture.copy() # up, down, left, right - half_block_d = texture.copy() - half_block_l = texture.copy() - half_block_r = texture.copy() - - # generate needed geometries - ImageDraw.Draw(side).rectangle((0,0,7,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_u).rectangle((0,8,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_d).rectangle((0,0,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_l).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_r).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - if data == 0: # ascending south - img = _build_full_block(half_block_r, None, None, half_block_d, side.transpose(Image.FLIP_LEFT_RIGHT)) - tmp1 = transform_image_side(half_block_u) - - # Darken the vertical part of the second step - sidealpha = tmp1.split()[3] - # darken it a bit more than usual, looks better - tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) - tmp1.putalpha(sidealpha) - - composite.alpha_over(img, tmp1, (6,3)) - tmp2 = transform_image(half_block_l) - composite.alpha_over(img, tmp2, (0,6)) - - elif data == 1: # ascending north - img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back - tmp1 = transform_image(half_block_r) - composite.alpha_over(img, tmp1, (0,6)) - tmp2 = _build_full_block(half_block_l, None, None, texture, side) - composite.alpha_over(img, tmp2) - - elif data == 2: # ascending west - img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back - tmp1 = transform_image(half_block_u) - composite.alpha_over(img, tmp1, (0,6)) - tmp2 = _build_full_block(half_block_d, None, None, side, texture) - composite.alpha_over(img, tmp2) - - elif data == 3: # ascending east - img = _build_full_block(half_block_u, None, None, side.transpose(Image.FLIP_LEFT_RIGHT), half_block_d) - tmp1 = transform_image_side(half_block_u).transpose(Image.FLIP_LEFT_RIGHT) - - # Darken the vertical part of the second step - sidealpha = tmp1.split()[3] - # darken it a bit more than usual, looks better - tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) - tmp1.putalpha(sidealpha) - - composite.alpha_over(img, tmp1, (6,3)) - tmp2 = transform_image(half_block_d) - composite.alpha_over(img, tmp2, (0,6)) - - # touch up a (horrible) pixel - img.putpixel((18,3),(0,0,0,0)) - - return generate_texture_tuple(img, blockID) - - if blockID == 54: # chests - # First to bits of the pseudo data store if it's a single chest - # or it's a double chest, first half or second half. - # The to last bits store the orientation. - - top = terrain_images[25] - side = terrain_images[26] - - if data & 12 == 0: # single chest - front = terrain_images[27] - back = terrain_images[26] - - elif data & 12 == 4: # double, first half - front = terrain_images[41] - back = terrain_images[57] - - elif data & 12 == 8: # double, second half - front = terrain_images[42] - back = terrain_images[58] - - else: # just in case - front = terrain_images[25] - side = terrain_images[25] - back = terrain_images[25] - - if data & 3 == 0: # facing west - img = _build_full_block(top, None, None, side, front) - - elif data & 3 == 1: # north - img = _build_full_block(top, None, None, front, side) - - elif data & 3 == 2: # east - img = _build_full_block(top, None, None, side, back) - - elif data & 3 == 3: # south - img = _build_full_block(top, None, None, back, side) - - else: - img = _build_full_block(top, None, None, back, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 55: # redstone wire - - if data & 0b1000000 == 64: # powered redstone wire - redstone_wire_t = terrain_images[165] - redstone_wire_t = tintTexture(redstone_wire_t,(255,0,0)) - - redstone_cross_t = terrain_images[164] - redstone_cross_t = tintTexture(redstone_cross_t,(255,0,0)) - - - else: # unpowered redstone wire - redstone_wire_t = terrain_images[165] - redstone_wire_t = tintTexture(redstone_wire_t,(48,0,0)) - - redstone_cross_t = terrain_images[164] - redstone_cross_t = tintTexture(redstone_cross_t,(48,0,0)) - - # generate an image per redstone direction - branch_top_left = redstone_cross_t.copy() - ImageDraw.Draw(branch_top_left).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_top_right = redstone_cross_t.copy() - ImageDraw.Draw(branch_top_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_right).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_bottom_right = redstone_cross_t.copy() - ImageDraw.Draw(branch_bottom_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_right).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_bottom_left = redstone_cross_t.copy() - ImageDraw.Draw(branch_bottom_left).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # generate the bottom texture - if data & 0b111111 == 0: - bottom = redstone_cross_t.copy() - - elif data & 0b1111 == 10: #= 0b1010 redstone wire in the x direction - bottom = redstone_wire_t.copy() - - elif data & 0b1111 == 5: #= 0b0101 redstone wire in the y direction - bottom = redstone_wire_t.copy().rotate(90) - - else: - bottom = Image.new("RGBA", (16,16), bgcolor) - if (data & 0b0001) == 1: - composite.alpha_over(bottom,branch_top_left) - - if (data & 0b1000) == 8: - composite.alpha_over(bottom,branch_top_right) - - if (data & 0b0010) == 2: - composite.alpha_over(bottom,branch_bottom_left) - - if (data & 0b0100) == 4: - composite.alpha_over(bottom,branch_bottom_right) - - # check for going up redstone wire - if data & 0b100000 == 32: - side1 = redstone_wire_t.rotate(90) - else: - side1 = None - - if data & 0b010000 == 16: - side2 = redstone_wire_t.rotate(90) - else: - side2 = None - - img = _build_full_block(None,side1,side2,None,None,bottom) - - return generate_texture_tuple(img, blockID) - - - if blockID == 58: # crafting table - top = terrain_images[43] - side3 = terrain_images[43+16] - side4 = terrain_images[43+16+1] - - img = _build_full_block(top, None, None, side3, side4, None, 58) - return generate_texture_tuple(img, blockID) - - - if blockID == 59: # crops - raw_crop = terrain_images[88+data] - crop1 = transform_image(raw_crop, blockID) - crop2 = transform_image_side(raw_crop, blockID) - crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), bgcolor) - composite.alpha_over(img, crop1, (0,12), crop1) - composite.alpha_over(img, crop2, (6,3), crop2) - composite.alpha_over(img, crop3, (6,3), crop3) - return generate_texture_tuple(img, blockID) - - - if blockID in (61, 62, 23): #furnace and burning furnace - top = terrain_images[62] - side = terrain_images[45] - - if blockID == 61: - front = terrain_images[44] - - elif blockID == 62: - front = terrain_images[45+16] - - elif blockID == 23: - front = terrain_images[46] - - if data == 3: # pointing west - img = _build_full_block(top, None, None, side, front) - - elif data == 4: # pointing north - img = _build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = _build_full_block(top, None, None, side, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 63: # singposts - - texture = terrain_images[4].copy() - # cut the planks to the size of a signpost - ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # If the signpost is looking directly to the image, draw some - # random dots, they will look as text. - if data in (0,1,2,3,4,5,15): - for i in range(15): - x = randint(4,11) - y = randint(3,7) - texture.putpixel((x,y),(0,0,0,255)) - - # Minecraft uses wood texture for the signpost stick - texture_stick = terrain_images[20] - texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) - ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - img = Image.new("RGBA", (24,24), bgcolor) - - # W N ~90 E S ~270 - angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) - angle = math.radians(angles[data]) - post = transform_image_angle(texture, angle) - - # choose the position of the "3D effect" - incrementx = 0 - if data in (1,6,7,8,9,14): - incrementx = -1 - elif data in (3,4,5,11,12,13): - incrementx = +1 - - composite.alpha_over(img, texture_stick,(11, 8),texture_stick) - # post2 is a brighter signpost pasted with a small sift, - # gives to the signpost some 3D effect. - post2 = ImageEnhance.Brightness(post).enhance(1.2) - composite.alpha_over(img, post2,(incrementx, -3),post2) - composite.alpha_over(img, post, (0,-2), post) - - return generate_texture_tuple(img, blockID) - - - if blockID in (64,71): #wooden door, or iron door - if data & 0x8 == 0x8: # top of the door - raw_door = terrain_images[81 if blockID == 64 else 82] - else: # bottom of the door - raw_door = terrain_images[97 if blockID == 64 else 98] - - # if you want to render all doors as closed, then force - # force swung to be False - if data & 0x4 == 0x4: - swung=True - else: - swung=False - - # mask out the high bits to figure out the orientation - img = Image.new("RGBA", (24,24), bgcolor) - if (data & 0x03) == 0: # northeast corner - if not swung: - tex = transform_image_side(raw_door) - composite.alpha_over(img, tex, (0,6), tex) - else: - # flip first to set the doornob on the correct side - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - tex = tex.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - - if (data & 0x03) == 1: # southeast corner - if not swung: - tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - else: - tex = transform_image_side(raw_door) - composite.alpha_over(img, tex, (12,0), tex) - - if (data & 0x03) == 2: # southwest corner - if not swung: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - composite.alpha_over(img, tex, (12,0), tex) - else: - tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - - if (data & 0x03) == 3: # northwest corner - if not swung: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - else: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - composite.alpha_over(img, tex, (0,6), tex) - - return generate_texture_tuple(img, blockID) - - - if blockID == 65: # ladder - img = Image.new("RGBA", (24,24), bgcolor) - raw_texture = terrain_images[83] - #print "ladder is facing: %d" % data - if data == 5: - # normally this ladder would be obsured by the block it's attached to - # but since ladders can apparently be placed on transparent blocks, we - # have to render this thing anyway. same for data == 2 - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (0,6), tex) - return generate_texture_tuple(img, blockID) - if data == 2: - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - return generate_texture_tuple(img, blockID) - if data == 3: - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - return generate_texture_tuple(img, blockID) - if data == 4: - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (12,0), tex) - return generate_texture_tuple(img, blockID) - - - if blockID in (27, 28, 66): # minetrack: - img = Image.new("RGBA", (24,24), bgcolor) - - if blockID == 27: # powered rail - if data & 0x8 == 0: # unpowered - raw_straight = terrain_images[163] - raw_corner = terrain_images[112] # they don't exist but make the code - # much simplier - elif data & 0x8 == 0x8: # powered - raw_straight = terrain_images[179] - raw_corner = terrain_images[112] # leave corners for code simplicity - # filter the 'powered' bit - data = data & 0x7 - - elif blockID == 28: # detector rail - raw_straight = terrain_images[195] - raw_corner = terrain_images[112] # leave corners for code simplicity - - elif blockID == 66: # normal rail - raw_straight = terrain_images[128] - raw_corner = terrain_images[112] - - ## use transform_image to scale and shear - if data == 0: - track = transform_image(raw_straight, blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 6: - track = transform_image(raw_corner, blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 7: - track = transform_image(raw_corner.rotate(270), blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 8: - # flip - track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90), - blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 9: - track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM), - blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 1: - track = transform_image(raw_straight.rotate(90), blockID) - composite.alpha_over(img, track, (0,12), track) - - #slopes - elif data == 2: # slope going up in +x direction - track = transform_image_slope(raw_straight,blockID) - track = track.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, track, (2,0), track) - # the 2 pixels move is needed to fit with the adjacent tracks - - elif data == 3: # slope going up in -x direction - # tracks are sprites, in this case we are seeing the "side" of - # the sprite, so draw a line to make it beautiful. - ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) - # grey from track texture (exterior grey). - # the track doesn't start from image corners, be carefull drawing the line! - elif data == 4: # slope going up in -y direction - track = transform_image_slope(raw_straight,blockID) - composite.alpha_over(img, track, (0,0), track) - - elif data == 5: # slope going up in +y direction - # same as "data == 3" - ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 68: # wall sign - texture = terrain_images[4].copy() - # cut the planks to the size of a signpost - ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # draw some random black dots, they will look as text - """ don't draw text at the moment, they are used in blank for decoration - - if data in (3,4): - for i in range(15): - x = randint(4,11) - y = randint(3,7) - texture.putpixel((x,y),(0,0,0,255)) - """ - - img = Image.new("RGBA", (24,24), bgcolor) - - incrementx = 0 - if data == 2: # east - incrementx = +1 - sign = _build_full_block(None, None, None, None, texture) - elif data == 3: # west - incrementx = -1 - sign = _build_full_block(None, texture, None, None, None) - elif data == 4: # north - incrementx = +1 - sign = _build_full_block(None, None, texture, None, None) - elif data == 5: # south - incrementx = -1 - sign = _build_full_block(None, None, None, texture, None) - - sign2 = ImageEnhance.Brightness(sign).enhance(1.2) - composite.alpha_over(img, sign2,(incrementx, 2),sign2) - composite.alpha_over(img, sign, (0,3), sign) - - return generate_texture_tuple(img, blockID) - - if blockID == 70 or blockID == 72: # wooden and stone pressure plates - if blockID == 70: # stone - t = terrain_images[1].copy() - else: # wooden - t = terrain_images[4].copy() - - # cut out the outside border, pressure plates are smaller - # than a normal block - ImageDraw.Draw(t).rectangle((0,0,15,15),outline=(0,0,0,0)) - - # create the textures and a darker version to make a 3d by - # pasting them with an offstet of 1 pixel - img = Image.new("RGBA", (24,24), bgcolor) - - top = transform_image(t, blockID) - - alpha = top.split()[3] - topd = ImageEnhance.Brightness(top).enhance(0.8) - topd.putalpha(alpha) - - #show it 3d or 2d if unpressed or pressed - if data == 0: - composite.alpha_over(img,topd, (0,12),topd) - composite.alpha_over(img,top, (0,11),top) - elif data == 1: - composite.alpha_over(img,top, (0,12),top) - - return generate_texture_tuple(img, blockID) - - if blockID == 85 or blockID == 113: # normal fences and nether fences - # create needed images for Big stick fence - if blockID == 85: # normal fence - fence_top = terrain_images[4].copy() - fence_side = terrain_images[4].copy() - fence_small_side = terrain_images[4].copy() - else: # netherbrick fence - fence_top = terrain_images[224].copy() - fence_side = terrain_images[224].copy() - fence_small_side = terrain_images[224].copy() - - # generate the textures of the fence - ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # Create the sides and the top of the big stick - fence_side = transform_image_side(fence_side,85) - fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) - fence_top = transform_image(fence_top,85) - - # Darken the sides slightly. These methods also affect the alpha layer, - # so save them first (we don't want to "darken" the alpha layer making - # the block transparent) - sidealpha = fence_side.split()[3] - fence_side = ImageEnhance.Brightness(fence_side).enhance(0.9) - fence_side.putalpha(sidealpha) - othersidealpha = fence_other_side.split()[3] - fence_other_side = ImageEnhance.Brightness(fence_other_side).enhance(0.8) - fence_other_side.putalpha(othersidealpha) - - # Compose the fence big stick - fence_big = Image.new("RGBA", (24,24), bgcolor) - composite.alpha_over(fence_big,fence_side, (5,4),fence_side) - composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) - composite.alpha_over(fence_big,fence_top, (0,0),fence_top) - - # Now render the small sticks. - # Create needed images - ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # Create the sides and the top of the small sticks - fence_small_side = transform_image_side(fence_small_side,85) - fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) - - # Darken the sides slightly. These methods also affect the alpha layer, - # so save them first (we don't want to "darken" the alpha layer making - # the block transparent) - sidealpha = fence_small_other_side.split()[3] - fence_small_other_side = ImageEnhance.Brightness(fence_small_other_side).enhance(0.9) - fence_small_other_side.putalpha(sidealpha) - sidealpha = fence_small_side.split()[3] - fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) - fence_small_side.putalpha(sidealpha) - - # Create img to compose the fence - img = Image.new("RGBA", (24,24), bgcolor) - - # Position of fence small sticks in img. - # These postitions are strange because the small sticks of the - # fence are at the very left and at the very right of the 16x16 images - pos_top_left = (2,3) - pos_top_right = (10,3) - pos_bottom_right = (10,7) - pos_bottom_left = (2,7) - - # +x axis points top right direction - # +y axis points bottom right direction - # First compose small sticks in the back of the image, - # then big stick and thecn small sticks in the front. - - if (data & 0b0001) == 1: - composite.alpha_over(img,fence_small_side, pos_top_left,fence_small_side) # top left - if (data & 0b1000) == 8: - composite.alpha_over(img,fence_small_other_side, pos_top_right,fence_small_other_side) # top right - - composite.alpha_over(img,fence_big,(0,0),fence_big) - - if (data & 0b0010) == 2: - composite.alpha_over(img,fence_small_other_side, pos_bottom_left,fence_small_other_side) # bottom left - if (data & 0b0100) == 4: - composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right - - return generate_texture_tuple(img, blockID) - - - if blockID in (86,91): # pumpkins, jack-o-lantern - top = terrain_images[102] - frontID = 119 if blockID == 86 else 120 - front = terrain_images[frontID] - side = terrain_images[118] - - if data == 0: # pointing west - img = _build_full_block(top, None, None, side, front) - - elif data == 1: # pointing north - img = _build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = _build_full_block(top, None, None, side, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 90: # portal - portaltexture = _load_image("portal.png") - img = Image.new("RGBA", (24,24), bgcolor) - - side = transform_image_side(portaltexture) - otherside = side.transpose(Image.FLIP_TOP_BOTTOM) - - if data in (1,4): - composite.alpha_over(img, side, (5,4), side) - - if data in (2,8): - composite.alpha_over(img, otherside, (5,4), otherside) - - return generate_texture_tuple(img, blockID) - - - if blockID == 92: # cake! (without bites, at the moment) - - top = terrain_images[121] - side = terrain_images[122] - top = transform_image(top, blockID) - side = transform_image_side(side, blockID) - otherside = side.transpose(Image.FLIP_LEFT_RIGHT) - - sidealpha = side.split()[3] - side = ImageEnhance.Brightness(side).enhance(0.9) - side.putalpha(sidealpha) - othersidealpha = otherside.split()[3] - otherside = ImageEnhance.Brightness(otherside).enhance(0.8) - otherside.putalpha(othersidealpha) - - img = Image.new("RGBA", (24,24), bgcolor) - - composite.alpha_over(img, side, (1,6), side) - composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole - composite.alpha_over(img, otherside, (12,6), otherside) - composite.alpha_over(img, top, (0,6), top) - - return generate_texture_tuple(img, blockID) - - - if blockID in (93, 94): # redstone repeaters (diodes), ON and OFF - # generate the diode - top = terrain_images[131] if blockID == 93 else terrain_images[147] - side = terrain_images[5] - increment = 13 - - if (data & 0x3) == 0: # pointing east - pass - - if (data & 0x3) == 1: # pointing south - top = top.rotate(270) - - if (data & 0x3) == 2: # pointing west - top = top.rotate(180) - - if (data & 0x3) == 3: # pointing north - top = top.rotate(90) - - img = _build_full_block( (top, increment), None, None, side, side) - - # compose a "3d" redstone torch - t = terrain_images[115].copy() if blockID == 93 else terrain_images[99].copy() - torch = Image.new("RGBA", (24,24), bgcolor) - - t_crop = t.crop((2,2,14,14)) - slice = t_crop.copy() - ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - composite.alpha_over(torch, slice, (6,4)) - composite.alpha_over(torch, t_crop, (5,5)) - composite.alpha_over(torch, t_crop, (6,5)) - composite.alpha_over(torch, slice, (6,6)) - - # paste redstone torches everywhere! - # the torch is too tall for the repeater, crop the bottom. - ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) - - # touch up the 3d effect with big rectangles, just in case, for other texture packs - ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) - - # torch positions for every redstone torch orientation. - # - # This is a horrible list of torch orientations. I tried to - # obtain these orientations by rotating the positions for one - # orientation, but pixel rounding is horrible and messes the - # torches. - - if (data & 0x3) == 0: # pointing east - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (-3,-1) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (2,2) - static_torch = (-3,-1) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (3,2) - static_torch = (-3,-1) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (4,3) - static_torch = (-3,-1) - - elif (data & 0x3) == 1: # pointing south - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (5,-1) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (0,2) - static_torch = (5,-1) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (-1,2) - static_torch = (5,-1) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (-2,3) - static_torch = (5,-1) - - elif (data & 0x3) == 2: # pointing west - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (5,3) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (0,0) - static_torch = (5,3) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (-1,0) - static_torch = (5,3) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (-2,-1) - static_torch = (5,3) - - elif (data & 0x3) == 3: # pointing north - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (-3,3) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (2,0) - static_torch = (-3,3) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (3,0) - static_torch = (-3,3) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (4,-1) - static_torch = (-3,3) - - # this paste order it's ok for east and south orientation - # but it's wrong for north and west orientations. But using the - # default texture pack the torches are small enough to no overlap. - composite.alpha_over(img, torch, static_torch, torch) - composite.alpha_over(img, torch, moving_torch, torch) - - return generate_texture_tuple(img, blockID) - - - if blockID == 96: # trapdoor - texture = terrain_images[84] - if data & 0x4 == 0x4: # opened trapdoor - if data & 0x3 == 0: # west - img = _build_full_block(None, None, None, None, texture) - if data & 0x3 == 1: # east - img = _build_full_block(None, texture, None, None, None) - if data & 0x3 == 2: # south - img = _build_full_block(None, None, texture, None, None) - if data & 0x3 == 3: # north - img = _build_full_block(None, None, None, texture, None) - - elif data & 0x4 == 0: # closed trapdoor - img = _build_full_block((texture, 12), None, None, texture, texture) - - return generate_texture_tuple(img, blockID) - - if blockID == 97: # silverfish in stone block - if data == 0: # stone - t = terrain_images[1] - elif data == 1: # cobblestone - t = terrain_images[16] - elif data == 2: # stone brick - t = terrain_images[54] - - img = _build_block(t, t, blockID) - - return generate_texture_tuple(img, blockID) - - if blockID == 98: # normal, mossy and cracked stone brick - if data == 0: # normal - t = terrain_images[54] - elif data == 1: # mossy - t = terrain_images[100] - else: # cracked - t = terrain_images[101] - - img = _build_full_block(t, None, None, t, t) - - return generate_texture_tuple(img, blockID) - - if blockID == 99 or blockID == 100: # huge brown and red mushroom - if blockID == 99: # brown - cap = terrain_images[126] - else: # red - cap = terrain_images[125] - stem = terrain_images[141] - porous = terrain_images[142] - - if data == 0: # fleshy piece - img = _build_full_block(porous, None, None, porous, porous) - - if data == 1: # north-east corner - img = _build_full_block(cap, None, None, cap, porous) - - if data == 2: # east side - img = _build_full_block(cap, None, None, porous, porous) - - if data == 3: # south-east corner - img = _build_full_block(cap, None, None, porous, cap) - - if data == 4: # north side - img = _build_full_block(cap, None, None, cap, porous) - - if data == 5: # top piece - img = _build_full_block(cap, None, None, porous, porous) - - if data == 6: # south side - img = _build_full_block(cap, None, None, cap, porous) - - if data == 7: # north-west corner - img = _build_full_block(cap, None, None, cap, cap) - - if data == 8: # west side - img = _build_full_block(cap, None, None, porous, cap) - - if data == 9: # south-west corner - img = _build_full_block(cap, None, None, porous, cap) - - if data == 10: # stem - img = _build_full_block(porous, None, None, stem, stem) - - return generate_texture_tuple(img, blockID) - - if blockID == 101 or blockID == 102: # iron bars and glass panes - if blockID == 101: - # iron bars - t = terrain_images[85] - else: - # glass panes - t = terrain_images[49] - left = t.copy() - right = t.copy() - - # generate the four small pieces of the glass pane - ImageDraw.Draw(right).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(left).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - up_left = transform_image_side(left) - up_right = transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM) - dw_right = transform_image_side(right) - dw_left = transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM) - - # Create img to compose the texture - img = Image.new("RGBA", (24,24), bgcolor) - - # +x axis points top right direction - # +y axis points bottom right direction - # First compose things in the back of the image, - # then things in the front. - - if (data & 0b0001) == 1 or data == 0: - composite.alpha_over(img,up_left, (6,3),up_left) # top left - if (data & 0b1000) == 8 or data == 0: - composite.alpha_over(img,up_right, (6,3),up_right) # top right - if (data & 0b0010) == 2 or data == 0: - composite.alpha_over(img,dw_left, (6,3),dw_left) # bottom left - if (data & 0b0100) == 4 or data == 0: - composite.alpha_over(img,dw_right, (6,3),dw_right) # bottom right - - return generate_texture_tuple(img, blockID) - - if blockID == 104 or blockID == 105: # pumpkin and melon stems. - # the ancildata value indicates how much of the texture - # is shown. - if data & 48 == 0: - # not fully grown stem or no pumpkin/melon touching it, - # straight up stem - t = terrain_images[111].copy() - img = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) - img = _build_block(img, img, blockID) - if data & 7 == 7: - # fully grown stem gets brown color! - # there is a conditional in rendermode-normal to not - # tint the data value 7 - img = tintTexture(img, (211,169,116)) - return generate_texture_tuple(img, blockID) - - else: # fully grown, and a pumpking/melon touching it, - # corner stem - pass - - - if blockID == 106: # vine - img = Image.new("RGBA", (24,24), bgcolor) - raw_texture = terrain_images[143] - # print "vine is facing: %d" % data - if data == 2: # south - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (0,6), tex) - return generate_texture_tuple(img, blockID) - if data == 1: # east - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - return generate_texture_tuple(img, blockID) - if data == 4: # west - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - return generate_texture_tuple(img, blockID) - if data == 8: # north - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (12,0), tex) - return generate_texture_tuple(img, blockID) - - if blockID == 107: - # create the closed gate side - gate_side = terrain_images[4].copy() - gate_side_draw = ImageDraw.Draw(gate_side) - gate_side_draw.rectangle((7,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((7,4,9,6),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((7,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((0,12,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((14,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # darken the sides slightly, as with the fences - sidealpha = gate_side.split()[3] - gate_side = ImageEnhance.Brightness(gate_side).enhance(0.9) - gate_side.putalpha(sidealpha) - - # create the other sides - mirror_gate_side = transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT), blockID) - gate_side = transform_image_side(gate_side, blockID) - gate_other_side = gate_side.transpose(Image.FLIP_LEFT_RIGHT) - mirror_gate_other_side = mirror_gate_side.transpose(Image.FLIP_LEFT_RIGHT) - - # Create img to compose the fence gate - img = Image.new("RGBA", (24,24), bgcolor) - - if data & 0x4: - # opened - data = data & 0x3 - if data == 0: - composite.alpha_over(img, gate_side, (2,8), gate_side) - composite.alpha_over(img, gate_side, (13,3), gate_side) - elif data == 1: - composite.alpha_over(img, gate_other_side, (-1,3), gate_other_side) - composite.alpha_over(img, gate_other_side, (10,8), gate_other_side) - elif data == 2: - composite.alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side) - composite.alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side) - elif data == 3: - composite.alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side) - composite.alpha_over(img, mirror_gate_other_side, (13,7), mirror_gate_other_side) - else: - # closed - - # positions for pasting the fence sides, as with fences - pos_top_left = (2,3) - pos_top_right = (10,3) - pos_bottom_right = (10,7) - pos_bottom_left = (2,7) - - if data == 0 or data == 2: - composite.alpha_over(img, gate_other_side, pos_top_right, gate_other_side) - composite.alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side) - elif data == 1 or data == 3: - composite.alpha_over(img, gate_side, pos_top_left, gate_side) - composite.alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side) - - return generate_texture_tuple(img, blockID) - - if blockID == 111: # lily pad - t = terrain_images[76] # NOTE: using same data as stairs, no - # info in minepedia at the moment. - if data == 0: # pointing south - img = _build_full_block(None, None, None, None, None, t) - elif data == 1: # pointing north - img = _build_full_block(None, None, None, None, None, t.rotate(180)) - elif data == 2: # pointing west - img = _build_full_block(None, None, None, None, None, t.rotate(270)) - elif data == 3: # pointing east - img = _build_full_block(None, None, None, None, None, t.rotate(90)) - - return generate_texture_tuple(img, blockID) - - if blockID == 115: # nether wart - if data == 0: # just come up - t = terrain_images[226] - elif data in (1, 2): - t = terrain_images[227] - elif data >= 3: # fully grown - t = terrain_images[228] - - # use the same technic as tall grass - img = _build_block(t, t, blockID) - return generate_texture_tuple(img, blockID) - - if blockID == 116: # enchantment table - # no book at the moment - top = terrain_images[166] - side = terrain_images[182] - img = _build_full_block((top, 4), None, None, side, side) - return generate_texture_tuple(img, blockID) - - if blockID == 117: # brewing stand - # place holder. just paste it in 2d! - t = terrain_images[157] - img = Image.new("RGBA", (24,24), bgcolor) - - composite.alpha_over(img, t, (4, 4), t) - return generate_texture_tuple(img, blockID) - - if blockID == 118: # cauldron - side = terrain_images[154] - top = terrain_images[138] - bottom = terrain_images[139] - water = transform_image(_load_image("water.png")) - if data == 0: # empty - img = _build_full_block(top, side, side, side, side) - - if data == 1: # 1/3 filled - img = _build_full_block(None , side, side, None, None) - composite.alpha_over(img, water, (0,8), water) - img2 = _build_full_block(top , None, None, side, side) - composite.alpha_over(img, img2, (0,0), img2) - - if data == 2: # 2/3 filled - img = _build_full_block(None , side, side, None, None) - composite.alpha_over(img, water, (0,4), water) - img2 = _build_full_block(top , None, None, side, side) - composite.alpha_over(img, img2, (0,0), img2) - - if data == 3: # 3/3 filled - img = _build_full_block(None , side, side, None, None) - composite.alpha_over(img, water, (0,0), water) - img2 = _build_full_block(top , None, None, side, side) - composite.alpha_over(img, img2, (0,0), img2) - - return generate_texture_tuple(img, blockID) - - if blockID == 119: # air portal - img = Image.new("RGBA", (24,24), bgcolor) - # generate a black texure with white, blue and grey dots resembling stars - t = Image.new("RGBA", (16,16), (0,0,0,255)) - for color in [(155,155,155,255), (100,255,100,255), (255,255,255,255)]: - for i in range(6): - x = randint(0,15) - y = randint(0,15) - t.putpixel((x,y),color) - - t = transform_image(t, blockID) - composite.alpha_over(img, t, (0,0), t) - - return generate_texture_tuple(img, blockID) - - if blockID == 120: # air portal frame - # The bottom 2 bits are oritation info but seems there is no - # graphical difference between orientations - top = terrain_images[158] - eye_t = terrain_images[174] - side = terrain_images[159] - img = _build_full_block((top, 4), None, None, side, side) - if data & 0x4 == 0x4: # ender eye on it - # generate the eye - eye_t = terrain_images[174].copy() - eye_t_s = terrain_images[174].copy() - # cut out from the texture the side and the top of the eye - ImageDraw.Draw(eye_t).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(eye_t_s).rectangle((0,4,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - # trnasform images and paste - eye = transform_image(eye_t) - eye_s = transform_image_side(eye_t_s) - eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, eye_s, (5,5), eye_s) - composite.alpha_over(img, eye_os, (9,5), eye_os) - composite.alpha_over(img, eye, (0,0), eye) - - return generate_texture_tuple(img, blockID) - - - return None - -def convert_data(blockID, data): - if blockID == 26: # bed - #Masked to not clobber block head/foot info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID in (29, 33, 34): # sticky piston, piston, piston extension - #Masked to not clobber block head/foot info - if _north == 'upper-left': - if (data & 0b0111) == 2: data = data & 0b1000 | 5 - elif (data & 0b0111) == 3: data = data & 0b1000 | 4 - elif (data & 0b0111) == 4: data = data & 0b1000 | 2 - elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif _north == 'upper-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 3 - elif (data & 0b0111) == 3: data = data & 0b1000 | 2 - elif (data & 0b0111) == 4: data = data & 0b1000 | 5 - elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif _north == 'lower-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 4 - elif (data & 0b0111) == 3: data = data & 0b1000 | 5 - elif (data & 0b0111) == 4: data = data & 0b1000 | 3 - elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - if blockID in (27, 28, 66): # minetrack: - #Masked to not clobber powered rail on/off info - #Ascending and flat straight - if _north == 'upper-left': - if (data & 0b0111) == 0: data = data & 0b1000 | 1 - elif (data & 0b0111) == 1: data = data & 0b1000 | 0 - elif (data & 0b0111) == 2: data = data & 0b1000 | 5 - elif (data & 0b0111) == 3: data = data & 0b1000 | 4 - elif (data & 0b0111) == 4: data = data & 0b1000 | 2 - elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif _north == 'upper-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 3 - elif (data & 0b0111) == 3: data = data & 0b1000 | 2 - elif (data & 0b0111) == 4: data = data & 0b1000 | 5 - elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif _north == 'lower-right': - if (data & 0b0111) == 0: data = data & 0b1000 | 1 - elif (data & 0b0111) == 1: data = data & 0b1000 | 0 - elif (data & 0b0111) == 2: data = data & 0b1000 | 4 - elif (data & 0b0111) == 3: data = data & 0b1000 | 5 - elif (data & 0b0111) == 4: data = data & 0b1000 | 3 - elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - if blockID == 66: # normal minetrack only - #Corners - if _north == 'upper-left': - if data == 6: data = 7 - elif data == 7: data = 8 - elif data == 8: data = 6 - elif data == 9: data = 9 - elif _north == 'upper-right': - if data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 7 - elif _north == 'lower-right': - if data == 6: data = 9 - elif data == 7: data = 6 - elif data == 8: data = 8 - elif data == 9: data = 7 - if blockID in (50, 75, 76): # torch, off/on redstone torch - if _north == 'upper-left': - if data == 1: data = 3 - elif data == 2: data = 4 - elif data == 3: data = 2 - elif data == 4: data = 1 - elif _north == 'upper-right': - if data == 1: data = 2 - elif data == 2: data = 1 - elif data == 3: data = 4 - elif data == 4: data = 3 - elif _north == 'lower-right': - if data == 1: data = 4 - elif data == 2: data = 3 - elif data == 3: data = 1 - elif data == 4: data = 2 - if blockID in (53,67,108,109,111,114): # wooden, cobblestone, brick - # stonebrick and netherbrick stairs. - # AND lily pad. - if _north == 'upper-left': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 1 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 1 - elif data == 1: data = 0 - elif data == 2: data = 3 - elif data == 3: data = 2 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 2 - elif data == 2: data = 0 - elif data == 3: data = 1 - if blockID in (61, 62, 23): # furnace and burning furnace - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID == 63: # signposts - if _north == 'upper-left': - data = (data + 4) % 16 - elif _north == 'upper-right': - data = (data + 8) % 16 - elif _north == 'lower-right': - data = (data + 12) % 16 - if blockID in (64,71): # wooden/iron door - #Masked to not clobber block top/bottom & swung info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID == 65: # ladder - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID == 68: # wall sign - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID in (86,91): # pumpkins, jack-o-lantern - if _north == 'upper-left': - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - if blockID in (93, 94): # redstone repeaters, ON and OFF - #Masked to not clobber delay info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID == 96: # trapdoor - #Masked to not clobber opened/closed info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - if blockID == 99 or blockID == 100: # huge red and brown mushroom - if _north == 'upper-left': - if data == 1: data = 3 - elif data == 2: data = 6 - elif data == 3: data = 9 - elif data == 4: data = 2 - elif data == 6: data = 8 - elif data == 7: data = 1 - elif data == 8: data = 4 - elif data == 9: data = 7 - elif _north == 'upper-right': - if data == 1: data = 9 - elif data == 2: data = 8 - elif data == 3: data = 7 - elif data == 4: data = 6 - elif data == 6: data = 4 - elif data == 7: data = 3 - elif data == 8: data = 2 - elif data == 9: data = 1 - elif _north == 'lower-right': - if data == 1: data = 7 - elif data == 2: data = 4 - elif data == 3: data = 1 - elif data == 4: data = 2 - elif data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 3 - if blockID == 106: # vine - if _north == 'upper-left': - if data == 1: data = 2 - elif data == 4: data = 8 - elif data == 8: data = 1 - elif data == 2: data = 4 - elif _north == 'upper-right': - if data == 1: data = 4 - elif data == 4: data = 1 - elif data == 8: data = 2 - elif data == 2: data = 8 - elif _north == 'lower-right': - if data == 1: data = 8 - elif data == 4: data = 2 - elif data == 8: data = 4 - elif data == 2: data = 1 - if blockID == 107: # fence gates - opened = False - if data & 0x4: - data = data & 0x3 - opened = True - if _north == 'upper-left': - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - if opened: - data = data | 0x4 - - return data - def tintTexture(im, c): # apparently converting to grayscale drops the alpha channel? i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) i.putalpha(im.split()[3]); # copy the alpha band back in. assuming RGBA return i +def generate_texture_tuple(img): + """ This takes an image and returns the needed tuple for the + blockmap dictionary.""" + if img is None: + return None + return (img, generate_opaque_mask(img)) + +## +## Biomes +## + currentBiomeFile = None currentBiomeData = None grasscolor = None foliagecolor = None watercolor = None +_north = None def prepareBiomeData(worlddir): global grasscolor, foliagecolor, watercolor @@ -2387,7 +465,7 @@ def prepareBiomeData(worlddir): try: grasscolor = list(Image.open(os.path.join(biomeDir,"grasscolor.png")).getdata()) foliagecolor = list(Image.open(os.path.join(biomeDir,"foliagecolor.png")).getdata()) - except: + except Exception: # clear anything that managed to get set grasscolor = None foliagecolor = None @@ -2400,6 +478,7 @@ def getBiomeData(worlddir, chunkX, chunkY): ''' global currentBiomeFile, currentBiomeData + global _north biomeX = chunkX // 32 biomeY = chunkY // 32 rots = 0 @@ -2439,6 +518,10 @@ def getBiomeData(worlddir, chunkX, chunkY): currentBiomeData = data return data +## +## Color Light +## + lightcolor = None lightcolor_checked = False def loadLightColor(): @@ -2448,159 +531,2272 @@ def loadLightColor(): lightcolor_checked = True try: lightcolor = list(_load_image("light_normal.png").getdata()) - except: + except Exception: logging.warning("Light color image could not be found.") lightcolor = None return lightcolor -# This set holds block ids that require special pre-computing. These are typically -# things that require ancillary data to render properly (i.e. ladder plus orientation) -# A good source of information is: -# http://www.minecraftwiki.net/wiki/Data_values -# (when adding new blocks here and in generate_special_textures, -# please, if possible, keep the ascending order of blockid value) - -special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, - 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, - 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, - 86, 90, 91, 92, 93, 94, 96, 97, 98, 99, 100, 101, - 102, 104, 105, 106, 107, 108, 109, 111, 113, 114, - 115, 116, 117, 118, 119, 120]) - -# this is a map of special blockIDs to a list of all -# possible values for ancillary data that it might have. - -special_map = {} - -# 0x10 means SNOW sides -special_map[2] = range(11) + [0x10,] # grass, grass has not ancildata but is - # used in the mod WildGrass, and this - # small fix shows the map as expected, - # and is harmless for normal maps -special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings) -special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data -special_map[17] = range(3) # wood: normal, birch and pine -special_map[18] = range(16) # leaves, birch, normal or pine leaves -special_map[20] = range(32) # glass, used to only render the exterior surface, uses pseudo data -special_map[26] = range(12) # bed, orientation -special_map[23] = range(6) # dispensers, orientation -special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered -special_map[28] = range(6) # detector rail, orientation/slope -special_map[29] = (0,1,2,3,4,5,8,9,10,11,12,13) # sticky piston body, orientation, pushed in/out -special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself -special_map[33] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal piston body, orientation, pushed in/out -special_map[34] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal and sticky piston extension, orientation, sticky/normal -special_map[35] = range(16) # wool, colored and white -special_map[43] = range(6) # stone, sandstone, wooden and cobblestone double-slab -special_map[44] = range(6) # stone, sandstone, wooden and cobblestone slab -special_map[50] = (1,2,3,4,5) # torch, position in the block -special_map[51] = range(16) # fire, position in the block (not implemented) -special_map[53] = range(4) # wooden stairs, orientation -special_map[54] = range(12) # chests, orientation and type (single or double), uses pseudo data -special_map[55] = range(128) # redstone wire, all the possible combinations, uses pseudo data -special_map[58] = (0,) # crafting table, it has 2 different sides -special_map[59] = range(8) # crops, grow from 0 to 7 -special_map[61] = range(6) # furnace, orientation -special_map[62] = range(6) # burning furnace, orientation -special_map[63] = range(16) # signpost, orientation -special_map[64] = range(16) # wooden door, open/close and orientation -special_map[65] = (2,3,4,5) # ladder, orientation -special_map[66] = range(10) # minecrart tracks, orientation, slope -special_map[67] = range(4) # cobblestone stairs, orientation -special_map[68] = (2,3,4,5) # wall sing, orientation -special_map[70] = (0,1) # stone pressure plate, non pressed and pressed -special_map[71] = range(16) # iron door, open/close and orientation -special_map[72] = (0,1) # wooden pressure plate, non pressed and pressed -special_map[75] = (1,2,3,4,5) # off redstone torch, orientation -special_map[76] = (1,2,3,4,5) # on redstone torch, orientation -special_map[79] = range(32) # ice, used to only render the exterior surface, uses pseudo data -special_map[85] = range(17) # fences, all the possible combination, uses pseudo data -special_map[86] = range(5) # pumpkin, orientation -special_map[90] = (1,2,4,8) # portal, in 2 orientations, 4 cases, uses pseudo data -special_map[91] = range(5) # jack-o-lantern, orientation -special_map[92] = range(6) # cake, eaten amount, (not implemented) -special_map[93] = range(16) # OFF redstone repeater, orientation and delay -special_map[94] = range(16) # ON redstone repeater, orientation and delay -special_map[96] = range(8) # trapdoor, open, closed, orientation -special_map[97] = range(3) # silverfish in stony block, type of block -special_map[98] = range(3) # stone brick, normal, mossy and cracked -special_map[99] = range(11) # huge brown mushroom, side, corner, etc, piece -special_map[100] = range(11) # huge red mushroom, side, corner, etc, piece -special_map[101] = range(16) # iron bars, all the possible combination, uses pseudo data -special_map[102] = range(16) # glass panes, all the possible combination, uses pseudo data -special_map[104] = range(8) # pumpkin stem, size of the stem -special_map[105] = range(8) # melon stem, size of the stem -special_map[106] = (1,2,4,8) # vine, orientation -special_map[107] = range(8) # fence gates, orientation + open bit -special_map[108] = range(4) # red stairs, orientation -special_map[109] = range(4) # stonebrick stairs, orientation -special_map[111] = range(4) # lily pad, orientation -special_map[113] = range(16) # netherbrick fence, uses pseudo data -special_map[114] = range(4) # netherbrick stairs, orientation -special_map[115] = range(4) # nether wart, size of the plant -special_map[116] = (0,) # enchantment table, nothings special. book not implemented -special_map[117] = range(8) # brewing stand, number of bottles. (not implemented) -special_map[118] = range(4) # cauldron, amount of water in it. -special_map[119] = (0,) # air portal, generated texture -special_map[120] = range(5) # air portal frame orientation, with or without eye of ender +## +## The big one: generate() and associated framework +## # placeholders that are generated in generate() -bgcolor = None -terrain_images = None -blockmap = None +texture_dimensions = None +blockmap_generators = {} +blockmap = {} biome_grass_texture = None -specialblockmap = None + +known_blocks = set() +transparent_blocks = set() +solid_blocks = set() +fluid_blocks = set() +nospawn_blocks = set() + +# the material registration decorator +def material(blockid=[], data=[0], **kwargs): + # mapping from property name to the set to store them in + properties = {"transparent" : transparent_blocks, "solid" : solid_blocks, "fluid" : fluid_blocks, "nospawn" : nospawn_blocks} + + # make sure blockid and data are iterable + try: + iter(blockid) + except: + blockid = [blockid,] + try: + iter(data) + except: + data = [data,] + + def inner_material(func): + global blockmap_generators + + # create a wrapper function with a known signature + @functools.wraps(func) + def func_wrapper(blockid, data, north): + try: + return func(blockid, data, north) + except TypeError: + return func(blockid, data) + + for block in blockid: + # set the property sets appropriately + known_blocks.update([block]) + for prop in properties: + try: + if block in kwargs.get(prop, []): + properties[prop].update([block]) + except TypeError: + if kwargs.get(prop, False): + properties[prop].update([block]) + + # populate blockmap_generators with our function + for d in data: + blockmap_generators[(block, d)] = func_wrapper + + return func_wrapper + return inner_material + +# shortcut function for pure blocks, default to solid +def block(blockid=[], top_index=None, side_index=None, **kwargs): + new_kwargs = {'solid' : True} + new_kwargs.update(kwargs) + + if top_index is None: + raise ValueError("top_index was not provided") + + if side_index is None: + side_index = top_index + + @material(blockid=blockid, **new_kwargs) + def inner_block(unused_id, unused_data): + return build_block(terrain_images[top_index], terrain_images[side_index]) + return inner_block + +# shortcut function for sprite blocks, defaults to transparent +def sprite(blockid=[], index=None, **kwargs): + new_kwargs = {'transparent' : True} + new_kwargs.update(kwargs) + + if index is None: + raise ValueError("index was not provided") + + @material(blockid=blockid, **new_kwargs) + def inner_sprite(unused_id, unused_data): + return build_sprite(terrain_images[index]) + return inner_sprite + +# shortcut function for billboard blocks, defaults to transparent +def billboard(blockid=[], index=None, **kwargs): + new_kwargs = {'transparent' : True} + new_kwargs.update(kwargs) + + if index is None: + raise ValueError("index was not provided") + + @material(blockid=blockid, **new_kwargs) + def inner_billboard(unused_id, unused_data): + return build_billboard(terrain_images[index]) + return inner_billboard def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'): - global _north - _north = north_direction global _find_file_local_path global bgcolor + global texture_dimensions + global _north bgcolor = bgc - global _find_file_local_path, texture_dimensions _find_file_local_path = path + _north = north_direction texture_dimensions = (texture_size, texture_size) # This maps terainids to 16x16 images global terrain_images - terrain_images = _split_terrain(_get_terrain_image()) - - # generate the normal blocks - global blockmap - blockmap = _build_blockimages() - load_water() + terrain_images = _split_terrain(_load_image("terrain.png")) # generate biome grass mask global biome_grass_texture - biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) - - # generate the special blocks - global specialblockmap, special_blocks - specialblockmap = {} - for blockID in special_blocks: - for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) + biome_grass_texture = build_block(terrain_images[0], terrain_images[38]) + # generate the blocks + global blockmap, blockmap_generators + blockmap = {} + for blockid, data in blockmap_generators: + texgen = blockmap_generators[(blockid, data)] + tex = texgen(blockid, data, north_direction) + blockmap[(blockid, data)] = generate_texture_tuple(tex) + if texture_size != 24: # rescale biome textures. biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS) - # rescale the normal block images - for i in range(len(blockmap)): - if blockmap[i] != None: - block = blockmap[i] - alpha = block[1] - block = block[0] - block.putalpha(alpha) - scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - blockmap[i] = generate_texture_tuple(scaled_block, i) - # rescale the special block images - for blockid, data in iter(specialblockmap): - block = specialblockmap[(blockid,data)] + for blockid, data in iter(blockmap): + block = blockmap[(blockid,data)] if block != None: - alpha = block[1] block = block[0] - block.putalpha(alpha) scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - specialblockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) + blockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) + +## +## and finally: actual texture definitions +## + +# stone +block(blockid=1, top_index=1) + +@material(blockid=2, data=range(11)+[0x10,], solid=True) +def grass(blockid, data): + # 0x10 bit means SNOW + side_img = terrain_images[3] + if data & 0x10: + side_img = terrain_images[68] + img = build_block(terrain_images[0], side_img) + if not data & 0x10: + global biome_grass_texture + composite.alpha_over(img, biome_grass_texture, (0, 0), biome_grass_texture) + return img + +# dirt +block(blockid=3, top_index=2) +# cobblestone +block(blockid=4, top_index=16) +# wooden plank +block(blockid=5, top_index=4) + +@material(blockid=6, data=range(16), transparent=True) +def saplings(blockid, data): + # usual saplings + tex = terrain_images[15] + + if data & 0x3 == 1: # spruce sapling + tex = terrain_images[63] + if data & 0x3 == 2: # birch sapling + tex = terrain_images[79] + + return build_sprite(tex) + +# bedrock +block(blockid=7, top_index=17) + +@material(blockid=8, data=range(16), fluid=True, transparent=True, nospawn=True) +def water(blockid, data): + watertex = _load_image("water.png") + return build_block(watertex, watertex) + +# other water, glass, and ice (no inner surfaces) +# uses pseudo-ancildata found in iterate.c +@material(blockid=[9, 20, 79], data=range(32), fluid=(9,), transparent=True, nospawn=True, solid=(79, 20)) +def no_inner_surfaces(blockid, data): + if blockid == 9: + texture = _load_image("water.png") + elif blockid == 20: + texture = terrain_images[49] + else: + texture = terrain_images[67] + + if (data & 0b10000) == 16: + top = texture + else: + top = None + + if (data & 0b0001) == 1: + side1 = texture # top left + else: + side1 = None + + if (data & 0b1000) == 8: + side2 = texture # top right + else: + side2 = None + + if (data & 0b0010) == 2: + side3 = texture # bottom left + else: + side3 = None + + if (data & 0b0100) == 4: + side4 = texture # bottom right + else: + side4 = None + + # if nothing shown do not draw at all + if top is None and side3 is None and side4 is None: + return None + + img = build_full_block(top,None,None,side3,side4) + return img + +@material(blockid=[10, 11], data=range(16), fluid=True, transparent=False, nospawn=True) +def lava(blockid, data): + lavatex = _load_image("lava.png") + return build_block(lavatex, lavatex) + +# sand +block(blockid=12, top_index=18) +# gravel +block(blockid=13, top_index=19) +# gold ore +block(blockid=14, top_index=32) +# iron ore +block(blockid=15, top_index=33) +# coal ore +block(blockid=16, top_index=34) + +@material(blockid=17, data=range(3), solid=True) +def wood(blockid, data): + top = terrain_images[21] + if data == 0: # normal + return build_block(top, terrain_images[20]) + if data == 1: # birch + return build_block(top, terrain_images[116]) + if data == 2: # pine + return build_block(top, terrain_images[117]) + +@material(blockid=18, data=range(16), transparent=True, solid=True) +def leaves(blockid, data): + t = terrain_images[52] + if data == 1: + # pine! + t = terrain_images[132] + return build_block(t, t) + +# sponge +block(blockid=19, top_index=48) +# lapis lazuli ore +block(blockid=21, top_index=160) +# lapis lazuli block +block(blockid=22, top_index=144) + +# dispensers, furnaces, and burning furnaces +@material(blockid=[23, 61, 62], data=range(6), solid=True) +def furnaces(blockid, data, north): + # first, do the north rotation if needed + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + top = terrain_images[62] + side = terrain_images[45] + + if blockid == 61: + front = terrain_images[44] + elif blockid == 62: + front = terrain_images[61] + elif blockid == 23: + front = terrain_images[46] + + if data == 3: # pointing west + return build_full_block(top, None, None, side, front) + elif data == 4: # pointing north + return build_full_block(top, None, None, front, side) + else: # in any other direction the front can't be seen + return build_full_block(top, None, None, side, side) + +# sandstone +block(blockid=24, top_index=176, side_index=192) +# note block +block(blockid=25, top_index=74) + +@material(blockid=26, data=range(12), transparent=True, nospawn=True) +def bed(blockid, data, north): + # first get north rotation done + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + increment = 8 + left_face = None + right_face = None + if data & 0x8 == 0x8: # head of the bed + top = terrain_images[135] + if data & 0x00 == 0x00: # head pointing to West + top = top.copy().rotate(270) + left_face = terrain_images[151] + right_face = terrain_images[152] + if data & 0x01 == 0x01: # ... North + top = top.rotate(270) + left_face = terrain_images[152] + right_face = terrain_images[151] + if data & 0x02 == 0x02: # East + top = top.rotate(180) + left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) + right_face = None + if data & 0x03 == 0x03: # South + right_face = None + right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) + + else: # foot of the bed + top = terrain_images[134] + if data & 0x00 == 0x00: # head pointing to West + top = top.rotate(270) + left_face = terrain_images[150] + right_face = None + if data & 0x01 == 0x01: # ... North + top = top.rotate(270) + left_face = None + right_face = terrain_images[150] + if data & 0x02 == 0x02: # East + top = top.rotate(180) + left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) + right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT) + if data & 0x03 == 0x03: # South + left_face = terrain_images[149] + right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) + + top = (top, increment) + return build_full_block(top, None, None, left_face, right_face) + +# powered, detector, and normal rails +@material(blockid=[27, 28, 66], data=range(14), transparent=True) +def rails(blockid, data, north): + # first, do north rotation + # Masked to not clobber powered rail on/off info + # Ascending and flat straight + if north == 'upper-left': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + img = Image.new("RGBA", (24,24), bgcolor) + + if blockid == 27: # powered rail + if data & 0x8 == 0: # unpowered + raw_straight = terrain_images[163] + raw_corner = terrain_images[112] # they don't exist but make the code + # much simplier + elif data & 0x8 == 0x8: # powered + raw_straight = terrain_images[179] + raw_corner = terrain_images[112] # leave corners for code simplicity + # filter the 'powered' bit + data = data & 0x7 + + elif blockid == 28: # detector rail + raw_straight = terrain_images[195] + raw_corner = terrain_images[112] # leave corners for code simplicity + + elif blockid == 66: # normal rail + raw_straight = terrain_images[128] + raw_corner = terrain_images[112] + + ## use transform_image to scale and shear + if data == 0: + track = transform_image_top(raw_straight) + composite.alpha_over(img, track, (0,12), track) + elif data == 6: + track = transform_image_top(raw_corner) + composite.alpha_over(img, track, (0,12), track) + elif data == 7: + track = transform_image_top(raw_corner.rotate(270)) + composite.alpha_over(img, track, (0,12), track) + elif data == 8: + # flip + track = transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90)) + composite.alpha_over(img, track, (0,12), track) + elif data == 9: + track = transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM)) + composite.alpha_over(img, track, (0,12), track) + elif data == 1: + track = transform_image_top(raw_straight.rotate(90)) + composite.alpha_over(img, track, (0,12), track) + + #slopes + elif data == 2: # slope going up in +x direction + track = transform_image_slope(raw_straight) + track = track.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, track, (2,0), track) + # the 2 pixels move is needed to fit with the adjacent tracks + + elif data == 3: # slope going up in -x direction + # tracks are sprites, in this case we are seeing the "side" of + # the sprite, so draw a line to make it beautiful. + ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) + # grey from track texture (exterior grey). + # the track doesn't start from image corners, be carefull drawing the line! + elif data == 4: # slope going up in -y direction + track = transform_image_slope(raw_straight) + composite.alpha_over(img, track, (0,0), track) + + elif data == 5: # slope going up in +y direction + # same as "data == 3" + ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) + + return img + +# sticky and normal piston body +@material(blockid=[29, 33], data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, solid=True, nospawn=True) +def piston(blockid, data, north): + # first, north rotation + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + if blockid == 29: # sticky + piston_t = terrain_images[106].copy() + else: # normal + piston_t = terrain_images[107].copy() + + # other textures + side_t = terrain_images[108].copy() + back_t = terrain_images[109].copy() + interior_t = terrain_images[110].copy() + + if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff + # remove piston texture from piston body + ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0)) + + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block(back_t ,None ,None ,side_t, side_t) + + elif data & 0x07 == 0x1: # up + img = build_full_block((interior_t, 4) ,None ,None ,side_t, side_t) + + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t , None, None ,side_t.rotate(90), back_t) + + elif data & 0x07 == 0x3: # west + img = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None) + temp = transform_image_side(interior_t) + temp = temp.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, temp, (9,5), temp) + + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270)) + temp = transform_image_side(interior_t) + composite.alpha_over(img, temp, (3,5), temp) + + elif data & 0x07 == 0x5: # south + img = build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90)) + + else: # pushed in, normal full blocks, easy stuff + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block(back_t ,None ,None ,side_t, side_t) + elif data & 0x07 == 0x1: # up + img = build_full_block(piston_t ,None ,None ,side_t, side_t) + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t) + elif data & 0x07 == 0x3: # west + img = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270)) + elif data & 0x07 == 0x5: # south + img = build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90)) + + return img + +# sticky and normal piston shaft +@material(blockid=34, data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, nospawn=True) +def piston_extension(blockid, data, north): + # first, north rotation + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + if (data & 0x8) == 0x8: # sticky + piston_t = terrain_images[106].copy() + else: # normal + piston_t = terrain_images[107].copy() + + # other textures + side_t = terrain_images[108].copy() + back_t = terrain_images[107].copy() + # crop piston body + ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0)) + + # generate the horizontal piston extension stick + h_stick = Image.new("RGBA", (24,24), bgcolor) + temp = transform_image_side(side_t) + composite.alpha_over(h_stick, temp, (1,7), temp) + temp = transform_image_top(side_t.rotate(90)) + composite.alpha_over(h_stick, temp, (1,1), temp) + # Darken it + sidealpha = h_stick.split()[3] + h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85) + h_stick.putalpha(sidealpha) + + # generate the vertical piston extension stick + v_stick = Image.new("RGBA", (24,24), bgcolor) + temp = transform_image_side(side_t.rotate(90)) + composite.alpha_over(v_stick, temp, (12,6), temp) + temp = temp.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(v_stick, temp, (1,6), temp) + # Darken it + sidealpha = v_stick.split()[3] + v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85) + v_stick.putalpha(sidealpha) + + # Piston orientation is stored in the 3 first bits + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block((back_t, 12) ,None ,None ,side_t, side_t) + composite.alpha_over(img, v_stick, (0,-3), v_stick) + elif data & 0x07 == 0x1: # up + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(piston_t ,None ,None ,side_t, side_t) + composite.alpha_over(img, v_stick, (0,4), v_stick) + composite.alpha_over(img, img2, (0,0), img2) + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t ,None ,None ,side_t.rotate(90), None) + temp = transform_image_side(back_t).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, temp, (2,2), temp) + composite.alpha_over(img, h_stick, (6,3), h_stick) + elif data & 0x07 == 0x3: # west + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) + composite.alpha_over(img, h_stick, (0,0), h_stick) + composite.alpha_over(img, img2, (0,0), img2) + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270)) + composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) + elif data & 0x07 == 0x5: # south + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90)) + temp = transform_image_side(back_t) + composite.alpha_over(img2, temp, (10,2), temp) + composite.alpha_over(img, img2, (0,0), img2) + composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) + + return img + +# cobweb +sprite(blockid=30, index=11, nospawn=True) + +@material(blockid=31, data=range(3), transparent=True) +def tall_grass(blockid, data): + if data == 0: # dead shrub + texture = terrain_images[55] + elif data == 1: # tall grass + texture = terrain_images[39] + elif data == 2: # fern + texture = terrain_images[56] + + return build_billboard(texture) + +# dead bush +billboard(blockid=32, index=55) + +@material(blockid=35, data=range(16), solid=True) +def wool(blockid, data): + if data == 0: # white + texture = terrain_images[64] + elif data == 1: # orange + texture = terrain_images[210] + elif data == 2: # magenta + texture = terrain_images[194] + elif data == 3: # light blue + texture = terrain_images[178] + elif data == 4: # yellow + texture = terrain_images[162] + elif data == 5: # light green + texture = terrain_images[146] + elif data == 6: # pink + texture = terrain_images[130] + elif data == 7: # grey + texture = terrain_images[114] + elif data == 8: # light grey + texture = terrain_images[225] + elif data == 9: # cyan + texture = terrain_images[209] + elif data == 10: # purple + texture = terrain_images[193] + elif data == 11: # blue + texture = terrain_images[177] + elif data == 12: # brown + texture = terrain_images[161] + elif data == 13: # dark green + texture = terrain_images[145] + elif data == 14: # red + texture = terrain_images[129] + elif data == 15: # black + texture = terrain_images[113] + + return build_block(texture, texture) + +# dandelion +sprite(blockid=37, index=13) +# rose +sprite(blockid=38, index=12) +# brown mushroom +sprite(blockid=39, index=29) +# red mushroom +sprite(blockid=40, index=28) +# block of gold +block(blockid=41, top_index=23) +# block of iron +block(blockid=42, top_index=22) + +# double slabs and slabs +@material(blockid=[43, 44], data=range(6), transparent=(44,), solid=True) +def slabs(blockid, data): + if data == 0: # stone slab + top = terrain_images[6] + side = terrain_images[5] + elif data == 1: # stone slab + top = terrain_images[176] + side = terrain_images[192] + elif data == 2: # wooden slab + top = side = terrain_images[4] + elif data == 3: # cobblestone slab + top = side = terrain_images[16] + elif data == 4: # brick? + top = side = terrain_images[7] + elif data == 5: # stone brick? + top = side = terrain_images[54] + + if blockid == 43: # double slab + return build_block(top, side) + + # cut the side texture in half + mask = side.crop((0,8,16,16)) + side = Image.new(side.mode, side.size, bgcolor) + composite.alpha_over(side, mask,(0,0,16,8), mask) + + # plain slab + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + sidealpha = side.split()[3] + side = ImageEnhance.Brightness(side).enhance(0.9) + side.putalpha(sidealpha) + othersidealpha = otherside.split()[3] + otherside = ImageEnhance.Brightness(otherside).enhance(0.8) + otherside.putalpha(othersidealpha) + + img = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(img, side, (0,12), side) + composite.alpha_over(img, otherside, (12,12), otherside) + composite.alpha_over(img, top, (0,6), top) + + return img + +# brick block +block(blockid=45, top_index=7) +# TNT +block(blockid=46, top_index=9, side_index=8, nospawn=True) +# bookshelf +block(blockid=47, top_index=4, side_index=35) +# moss stone +block(blockid=48, top_index=36) +# obsidian +block(blockid=49, top_index=37) + +# torch, redstone torch (off), redstone torch(on) +@material(blockid=[50, 75, 76], data=[1, 2, 3, 4, 5], transparent=True) +def torches(blockid, data, north): + # first, north rotations + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + elif north == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 + + # choose the proper texture + if blockid == 50: # torch + small = terrain_images[80] + elif blockid == 75: # off redstone torch + small = terrain_images[115] + else: # on redstone torch + small = terrain_images[99] + + # compose a torch bigger than the normal + # (better for doing transformations) + torch = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(torch,small,(-4,-3)) + composite.alpha_over(torch,small,(-5,-2)) + composite.alpha_over(torch,small,(-3,-2)) + + # angle of inclination of the texture + rotation = 15 + + if data == 1: # pointing south + torch = torch.rotate(-rotation, Image.NEAREST) # nearest filter is more nitid. + img = build_full_block(None, None, None, torch, None, None) + + elif data == 2: # pointing north + torch = torch.rotate(rotation, Image.NEAREST) + img = build_full_block(None, None, torch, None, None, None) + + elif data == 3: # pointing west + torch = torch.rotate(rotation, Image.NEAREST) + img = build_full_block(None, torch, None, None, None, None) + + elif data == 4: # pointing east + torch = torch.rotate(-rotation, Image.NEAREST) + img = build_full_block(None, None, None, None, torch, None) + + elif data == 5: # standing on the floor + # compose a "3d torch". + img = Image.new("RGBA", (24,24), bgcolor) + + small_crop = small.crop((2,2,14,14)) + slice = small_crop.copy() + ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + composite.alpha_over(img, slice, (7,5)) + composite.alpha_over(img, small_crop, (6,6)) + composite.alpha_over(img, small_crop, (7,6)) + composite.alpha_over(img, slice, (7,7)) + + return img + +# fire +@material(blockid=51, data=range(16), transparent=True) +def fire(blockid, data): + firetexture = _load_image("fire.png") + side1 = transform_image_side(firetexture) + side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) + + img = Image.new("RGBA", (24,24), bgcolor) + + composite.alpha_over(img, side1, (12,0), side1) + composite.alpha_over(img, side2, (0,0), side2) + + composite.alpha_over(img, side1, (0,6), side1) + composite.alpha_over(img, side2, (12,6), side2) + + return img + +# monster spawner +block(blockid=52, top_index=34, transparent=True) + +# wooden, cobblestone, red brick, stone brick and netherbrick stairs. +@material(blockid=[53,67,108,109,114], data=range(4), transparent=True, solid=True, nospawn=True) +def stairs(blockid, data, north): + + # first, north rotations + if north == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 + + if blockid == 53: # wooden + texture = terrain_images[4] + elif blockid == 67: # cobblestone + texture = terrain_images[16] + elif blockid == 108: # red brick stairs + texture = terrain_images[7] + elif blockid == 109: # stone brick stairs + texture = terrain_images[54] + elif blockid == 114: # netherbrick stairs + texture = terrain_images[224] + + side = texture.copy() + half_block_u = texture.copy() # up, down, left, right + half_block_d = texture.copy() + half_block_l = texture.copy() + half_block_r = texture.copy() + + # generate needed geometries + ImageDraw.Draw(side).rectangle((0,0,7,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_u).rectangle((0,8,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_d).rectangle((0,0,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_l).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_r).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + if data == 0: # ascending south + img = build_full_block(half_block_r, None, None, half_block_d, side.transpose(Image.FLIP_LEFT_RIGHT)) + tmp1 = transform_image_side(half_block_u) + + # Darken the vertical part of the second step + sidealpha = tmp1.split()[3] + # darken it a bit more than usual, looks better + tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) + tmp1.putalpha(sidealpha) + + composite.alpha_over(img, tmp1, (6,4)) #workaround, fixes a hole + composite.alpha_over(img, tmp1, (6,3)) + tmp2 = transform_image_top(half_block_l) + composite.alpha_over(img, tmp2, (0,6)) + + elif data == 1: # ascending north + img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back + tmp1 = transform_image_top(half_block_r) + composite.alpha_over(img, tmp1, (0,6)) + tmp2 = build_full_block(half_block_l, None, None, texture, side) + composite.alpha_over(img, tmp2) + + elif data == 2: # ascending west + img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back + tmp1 = transform_image_top(half_block_u) + composite.alpha_over(img, tmp1, (0,6)) + tmp2 = build_full_block(half_block_d, None, None, side, texture) + composite.alpha_over(img, tmp2) + + elif data == 3: # ascending east + img = build_full_block(half_block_u, None, None, side.transpose(Image.FLIP_LEFT_RIGHT), half_block_d) + tmp1 = transform_image_side(half_block_u).transpose(Image.FLIP_LEFT_RIGHT) + + # Darken the vertical part of the second step + sidealpha = tmp1.split()[3] + # darken it a bit more than usual, looks better + tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) + tmp1.putalpha(sidealpha) + + composite.alpha_over(img, tmp1, (6,4)) #workaround, fixes a hole + composite.alpha_over(img, tmp1, (6,3)) + tmp2 = transform_image_top(half_block_d) + composite.alpha_over(img, tmp2, (0,6)) + + # touch up a (horrible) pixel + img.putpixel((18,3),(0,0,0,0)) + + return img + +# normal and locked chest (locked was the one used in april fools' day) +# uses pseudo-ancildata found in iterate.c +@material(blockid=[54,95], data=range(12), solid=True) +def chests(blockid, data): + # First two bits of the pseudo data store if it's a single chest + # or it's a double chest, first half or second half (left to right). + # The last two bits store the orientation. + + # No need for north stuff, uses pseudo data and rotates with the map + + top = terrain_images[25] + side = terrain_images[26] + + if data & 12 == 0: # single chest + front = terrain_images[27] + back = terrain_images[26] + + elif data & 12 == 4: # double, first half + front = terrain_images[41] + back = terrain_images[57] + + elif data & 12 == 8: # double, second half + front = terrain_images[42] + back = terrain_images[58] + + else: # just in case + front = terrain_images[25] + side = terrain_images[25] + back = terrain_images[25] + + if data & 3 == 0: # facing west + img = build_full_block(top, None, None, side, front) + + elif data & 3 == 1: # north + img = build_full_block(top, None, None, front, side) + + elif data & 3 == 2: # east + img = build_full_block(top, None, None, side, back) + + elif data & 3 == 3: # south + img = build_full_block(top, None, None, back, side) + + else: + img = build_full_block(top, None, None, back, side) + + return img + +# redstone wire +# uses pseudo-ancildata found in iterate.c +@material(blockid=55, data=range(128), transparent=True) +def wire(blockid, data): + + if data & 0b1000000 == 64: # powered redstone wire + redstone_wire_t = terrain_images[165] + redstone_wire_t = tintTexture(redstone_wire_t,(255,0,0)) + + redstone_cross_t = terrain_images[164] + redstone_cross_t = tintTexture(redstone_cross_t,(255,0,0)) + + + else: # unpowered redstone wire + redstone_wire_t = terrain_images[165] + redstone_wire_t = tintTexture(redstone_wire_t,(48,0,0)) + + redstone_cross_t = terrain_images[164] + redstone_cross_t = tintTexture(redstone_cross_t,(48,0,0)) + + # generate an image per redstone direction + branch_top_left = redstone_cross_t.copy() + ImageDraw.Draw(branch_top_left).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_top_right = redstone_cross_t.copy() + ImageDraw.Draw(branch_top_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_right).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_bottom_right = redstone_cross_t.copy() + ImageDraw.Draw(branch_bottom_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_right).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_bottom_left = redstone_cross_t.copy() + ImageDraw.Draw(branch_bottom_left).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # generate the bottom texture + if data & 0b111111 == 0: + bottom = redstone_cross_t.copy() + + elif data & 0b1111 == 10: #= 0b1010 redstone wire in the x direction + bottom = redstone_wire_t.copy() + + elif data & 0b1111 == 5: #= 0b0101 redstone wire in the y direction + bottom = redstone_wire_t.copy().rotate(90) + + else: + bottom = Image.new("RGBA", (16,16), bgcolor) + if (data & 0b0001) == 1: + composite.alpha_over(bottom,branch_top_left) + + if (data & 0b1000) == 8: + composite.alpha_over(bottom,branch_top_right) + + if (data & 0b0010) == 2: + composite.alpha_over(bottom,branch_bottom_left) + + if (data & 0b0100) == 4: + composite.alpha_over(bottom,branch_bottom_right) + + # check for going up redstone wire + if data & 0b100000 == 32: + side1 = redstone_wire_t.rotate(90) + else: + side1 = None + + if data & 0b010000 == 16: + side2 = redstone_wire_t.rotate(90) + else: + side2 = None + + img = build_full_block(None,side1,side2,None,None,bottom) + + return img + +# diamond ore +block(blockid=56, top_index=50) +# diamond block +block(blockid=57, top_index=24) + +# crafting table +# needs two different sides +@material(blockid=58, solid=True) +def crafting_table(blockid, data): + top = terrain_images[43] + side3 = terrain_images[43+16] + side4 = terrain_images[43+16+1] + + img = build_full_block(top, None, None, side3, side4, None) + return img + +# crops +@material(blockid=59, data=range(8), transparent=True, nospawn=True) +def crops(blockid, data): + raw_crop = terrain_images[88+data] + crop1 = transform_image_top(raw_crop) + crop2 = transform_image_side(raw_crop) + crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) + + img = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(img, crop1, (0,12), crop1) + composite.alpha_over(img, crop2, (6,3), crop2) + composite.alpha_over(img, crop3, (6,3), crop3) + return img + +# farmland +@material(blockid=60, data=range(9), solid=True) +def farmland(blockid, data): + top = terrain_images[86] + if data == 0: + top = terrain_images[87] + return build_block(top, terrain_images[2]) + +# signposts +@material(blockid=63, data=range(16), transparent=True) +def signpost(blockid, data, north): + + # first north rotations + if north == 'upper-left': + data = (data + 4) % 16 + elif north == 'upper-right': + data = (data + 8) % 16 + elif north == 'lower-right': + data = (data + 12) % 16 + + texture = terrain_images[4].copy() + # cut the planks to the size of a signpost + ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # If the signpost is looking directly to the image, draw some + # random dots, they will look as text. + if data in (0,1,2,3,4,5,15): + for i in range(15): + x = randint(4,11) + y = randint(3,7) + texture.putpixel((x,y),(0,0,0,255)) + + # Minecraft uses wood texture for the signpost stick + texture_stick = terrain_images[20] + texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) + ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + img = Image.new("RGBA", (24,24), bgcolor) + + # W N ~90 E S ~270 + angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) + angle = math.radians(angles[data]) + post = transform_image_angle(texture, angle) + + # choose the position of the "3D effect" + incrementx = 0 + if data in (1,6,7,8,9,14): + incrementx = -1 + elif data in (3,4,5,11,12,13): + incrementx = +1 + + composite.alpha_over(img, texture_stick,(11, 8),texture_stick) + # post2 is a brighter signpost pasted with a small shift, + # gives to the signpost some 3D effect. + post2 = ImageEnhance.Brightness(post).enhance(1.2) + composite.alpha_over(img, post2,(incrementx, -3),post2) + composite.alpha_over(img, post, (0,-2), post) + + return img + + +# wooden and iron door +@material(blockid=[64,71], data=range(16), transparent=True) +def door(blockid, data, north): + #Masked to not clobber block top/bottom & swung info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + if data & 0x8 == 0x8: # top of the door + raw_door = terrain_images[81 if blockid == 64 else 82] + else: # bottom of the door + raw_door = terrain_images[97 if blockid == 64 else 98] + + # if you want to render all doors as closed, then force + # force swung to be False + if data & 0x4 == 0x4: + swung=True + else: + swung=False + + # mask out the high bits to figure out the orientation + img = Image.new("RGBA", (24,24), bgcolor) + if (data & 0x03) == 0: # northeast corner + if not swung: + tex = transform_image_side(raw_door) + composite.alpha_over(img, tex, (0,6), tex) + else: + # flip first to set the doornob on the correct side + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + tex = tex.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + + if (data & 0x03) == 1: # southeast corner + if not swung: + tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + else: + tex = transform_image_side(raw_door) + composite.alpha_over(img, tex, (12,0), tex) + + if (data & 0x03) == 2: # southwest corner + if not swung: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + composite.alpha_over(img, tex, (12,0), tex) + else: + tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + + if (data & 0x03) == 3: # northwest corner + if not swung: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + else: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + composite.alpha_over(img, tex, (0,6), tex) + + return img + +# ladder +@material(blockd=65, data=[2, 3, 4, 5], transparent=True) +def ladder(blockid, data, north): + + # first north rotations + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + img = Image.new("RGBA", (24,24), bgcolor) + raw_texture = terrain_images[83] + + if data == 5: + # normally this ladder would be obsured by the block it's attached to + # but since ladders can apparently be placed on transparent blocks, we + # have to render this thing anyway. same for data == 2 + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (0,6), tex) + return generate_texture_tuple(img, blockID) + if data == 2: + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + return generate_texture_tuple(img, blockID) + if data == 3: + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + return generate_texture_tuple(img, blockID) + if data == 4: + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (12,0), tex) + return generate_texture_tuple(img, blockID) + + +# wall signs +@material(blockid=68, data=[2, 3, 4, 5], transparent=True) +def wall_sign(blockid, data, north): # wall sign + + # first north rotations + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + texture = terrain_images[4].copy() + # cut the planks to the size of a signpost + ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # draw some random black dots, they will look as text + """ don't draw text at the moment, they are used in blank for decoration + + if data in (3,4): + for i in range(15): + x = randint(4,11) + y = randint(3,7) + texture.putpixel((x,y),(0,0,0,255)) + """ + + img = Image.new("RGBA", (24,24), bgcolor) + + incrementx = 0 + if data == 2: # east + incrementx = +1 + sign = build_full_block(None, None, None, None, texture) + elif data == 3: # west + incrementx = -1 + sign = build_full_block(None, texture, None, None, None) + elif data == 4: # north + incrementx = +1 + sign = build_full_block(None, None, texture, None, None) + elif data == 5: # south + incrementx = -1 + sign = build_full_block(None, None, None, texture, None) + + sign2 = ImageEnhance.Brightness(sign).enhance(1.2) + composite.alpha_over(img, sign2,(incrementx, 2),sign2) + composite.alpha_over(img, sign, (0,3), sign) + + return img + +## +## not rendered: levers +## +@material(blockid=69, data=range(16), transparent=True) +def levers(blockid, data, north): + # place holder, used to mae the block transparent + return None + +# wooden and stone pressure plates +@material(blockid=[70, 72], data=[0,1], transparent=True) +def pressure_plate(blockid, data): + if blockid == 70: # stone + t = terrain_images[1].copy() + else: # wooden + t = terrain_images[4].copy() + + # cut out the outside border, pressure plates are smaller + # than a normal block + ImageDraw.Draw(t).rectangle((0,0,15,15),outline=(0,0,0,0)) + + # create the textures and a darker version to make a 3d by + # pasting them with an offstet of 1 pixel + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(t) + + alpha = top.split()[3] + topd = ImageEnhance.Brightness(top).enhance(0.8) + topd.putalpha(alpha) + + #show it 3d or 2d if unpressed or pressed + if data == 0: + composite.alpha_over(img,topd, (0,12),topd) + composite.alpha_over(img,top, (0,11),top) + elif data == 1: + composite.alpha_over(img,top, (0,12),top) + + return img + +# normal and glowing redstone ore +block(blockid=[73, 74], top_index=51) + +## +## not rendered: buttons +## +@material(blockid=77, data=range(16), transparent=True) +def buttons(blockid, data, north): + # place holder, used to make the block transparent + return None + +# snow +@material(blockid=78, data=range(8), transparent=True, solid=True) +def snow(blockid, data): + # still not rendered correctly: data other than 0 + + tex = terrain_images[66] + + # make the side image, top 3/4 transparent + mask = tex.crop((0,12,16,16)) + sidetex = Image.new(tex.mode, tex.size, bgcolor) + composite.alpha_over(sidetex, mask, (0,12,16,16), mask) + + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(tex) + side = transform_image_side(sidetex) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,9), top) + + return img + +# snow block +block(blockid=80, top_index=66) + +# cactus +@material(blockid=81, data=range(15), transparent=True, solid=True, nospawn=True) +def cactus(blockid, data): + top = terrain_images[69] + side = terrain_images[70] + + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + sidealpha = side.split()[3] + side = ImageEnhance.Brightness(side).enhance(0.9) + side.putalpha(sidealpha) + othersidealpha = otherside.split()[3] + otherside = ImageEnhance.Brightness(otherside).enhance(0.8) + otherside.putalpha(othersidealpha) + + composite.alpha_over(img, side, (1,6), side) + composite.alpha_over(img, otherside, (11,6), otherside) + composite.alpha_over(img, top, (0,0), top) + + return img + +# clay block +block(blockid=82, top_index=72) + +# sugar cane +@material(blockid=83, data=range(16), transparent=True) +def sugar_cane(blockid, data): + tex = terrain_images[73] + return build_sprite(tex) + +# jukebox +@material(blockid=84, data=range(16), solid=True) +def jukebox(blockid, data): + return build_block(terrain_images[75], terrain_images[74]) + +# nether and normal fences +# uses pseudo-ancildata found in iterate.c +@material(blockid=[85, 113], data=range(16), transparent=True, nospawn=True) +def fence(blockid, data): + # no need for north rotations, it uses pseudo data. + # create needed images for Big stick fence + if blockid == 85: # normal fence + fence_top = terrain_images[4].copy() + fence_side = terrain_images[4].copy() + fence_small_side = terrain_images[4].copy() + else: # netherbrick fence + fence_top = terrain_images[224].copy() + fence_side = terrain_images[224].copy() + fence_small_side = terrain_images[224].copy() + + # generate the textures of the fence + ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # Create the sides and the top of the big stick + fence_side = transform_image_side(fence_side) + fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) + fence_top = transform_image_top(fence_top) + + # Darken the sides slightly. These methods also affect the alpha layer, + # so save them first (we don't want to "darken" the alpha layer making + # the block transparent) + sidealpha = fence_side.split()[3] + fence_side = ImageEnhance.Brightness(fence_side).enhance(0.9) + fence_side.putalpha(sidealpha) + othersidealpha = fence_other_side.split()[3] + fence_other_side = ImageEnhance.Brightness(fence_other_side).enhance(0.8) + fence_other_side.putalpha(othersidealpha) + + # Compose the fence big stick + fence_big = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(fence_big,fence_side, (5,4),fence_side) + composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) + composite.alpha_over(fence_big,fence_top, (0,0),fence_top) + + # Now render the small sticks. + # Create needed images + ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # Create the sides and the top of the small sticks + fence_small_side = transform_image_side(fence_small_side) + fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) + + # Darken the sides slightly. These methods also affect the alpha layer, + # so save them first (we don't want to "darken" the alpha layer making + # the block transparent) + sidealpha = fence_small_other_side.split()[3] + fence_small_other_side = ImageEnhance.Brightness(fence_small_other_side).enhance(0.9) + fence_small_other_side.putalpha(sidealpha) + sidealpha = fence_small_side.split()[3] + fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) + fence_small_side.putalpha(sidealpha) + + # Create img to compose the fence + img = Image.new("RGBA", (24,24), bgcolor) + + # Position of fence small sticks in img. + # These postitions are strange because the small sticks of the + # fence are at the very left and at the very right of the 16x16 images + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) + + # +x axis points top right direction + # +y axis points bottom right direction + # First compose small sticks in the back of the image, + # then big stick and thecn small sticks in the front. + + if (data & 0b0001) == 1: + composite.alpha_over(img,fence_small_side, pos_top_left,fence_small_side) # top left + if (data & 0b1000) == 8: + composite.alpha_over(img,fence_small_other_side, pos_top_right,fence_small_other_side) # top right + + composite.alpha_over(img,fence_big,(0,0),fence_big) + + if (data & 0b0010) == 2: + composite.alpha_over(img,fence_small_other_side, pos_bottom_left,fence_small_other_side) # bottom left + if (data & 0b0100) == 4: + composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right + + return img + +# pumpkin +@material(blockid=[86, 91], data=range(4), solid=True) +def pumpkin(blockid, data, north): # pumpkins, jack-o-lantern + # north rotation + if north == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 + + # texture generation + top = terrain_images[102] + frontID = 119 if blockid == 86 else 120 + front = terrain_images[frontID] + side = terrain_images[118] + + if data == 0: # pointing west + img = build_full_block(top, None, None, side, front) + + elif data == 1: # pointing north + img = build_full_block(top, None, None, front, side) + + else: # in any other direction the front can't be seen + img = build_full_block(top, None, None, side, side) + + return img + +# netherrack +block(blockid=87, top_index=103) + +# soul sand +block(blockid=88, top_index=104) + +# glowstone +block(blockid=89, top_index=105) + +# portal +@material(blockid=90, data=[1, 2, 4, 8], transparent=True) +def portal(blockid, data): + # no north orientation uses pseudo data + portaltexture = _load_image("portal.png") + img = Image.new("RGBA", (24,24), bgcolor) + + side = transform_image_side(portaltexture) + otherside = side.transpose(Image.FLIP_TOP_BOTTOM) + + if data in (1,4): + composite.alpha_over(img, side, (5,4), side) + + if data in (2,8): + composite.alpha_over(img, otherside, (5,4), otherside) + + return img + +# cake! +# TODO is rendered un-bitten +@material(blockid=92, data=range(6), transparent=True, nospawn=True) +def cake(blockid, data): + + # choose textures for cake + top = terrain_images[121] + side = terrain_images[122] + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + # darken sides slightly + sidealpha = side.split()[3] + side = ImageEnhance.Brightness(side).enhance(0.9) + side.putalpha(sidealpha) + othersidealpha = otherside.split()[3] + otherside = ImageEnhance.Brightness(otherside).enhance(0.8) + otherside.putalpha(othersidealpha) + + img = Image.new("RGBA", (24,24), bgcolor) + + # composite the cake + composite.alpha_over(img, side, (1,6), side) + composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,6), top) + + return img + +# redstone repeaters ON and OFF +@material(blockid=[93,94], data=range(16), transparent=True, nospawn=True) +def repeater(blockid, data, north): + # north rotation + # Masked to not clobber delay info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + # generate the diode + top = terrain_images[131] if blockid == 93 else terrain_images[147] + side = terrain_images[5] + increment = 13 + + if (data & 0x3) == 0: # pointing east + pass + + if (data & 0x3) == 1: # pointing south + top = top.rotate(270) + + if (data & 0x3) == 2: # pointing west + top = top.rotate(180) + + if (data & 0x3) == 3: # pointing north + top = top.rotate(90) + + img = build_full_block( (top, increment), None, None, side, side) + + # compose a "3d" redstone torch + t = terrain_images[115].copy() if blockid == 93 else terrain_images[99].copy() + torch = Image.new("RGBA", (24,24), bgcolor) + + t_crop = t.crop((2,2,14,14)) + slice = t_crop.copy() + ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + composite.alpha_over(torch, slice, (6,4)) + composite.alpha_over(torch, t_crop, (5,5)) + composite.alpha_over(torch, t_crop, (6,5)) + composite.alpha_over(torch, slice, (6,6)) + + # paste redstone torches everywhere! + # the torch is too tall for the repeater, crop the bottom. + ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) + + # touch up the 3d effect with big rectangles, just in case, for other texture packs + ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) + + # torch positions for every redstone torch orientation. + # + # This is a horrible list of torch orientations. I tried to + # obtain these orientations by rotating the positions for one + # orientation, but pixel rounding is horrible and messes the + # torches. + + if (data & 0x3) == 0: # pointing east + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (-3,-1) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (2,2) + static_torch = (-3,-1) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (3,2) + static_torch = (-3,-1) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (4,3) + static_torch = (-3,-1) + + elif (data & 0x3) == 1: # pointing south + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (5,-1) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (0,2) + static_torch = (5,-1) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (-1,2) + static_torch = (5,-1) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (-2,3) + static_torch = (5,-1) + + elif (data & 0x3) == 2: # pointing west + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (5,3) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (0,0) + static_torch = (5,3) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (-1,0) + static_torch = (5,3) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (-2,-1) + static_torch = (5,3) + + elif (data & 0x3) == 3: # pointing north + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (-3,3) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (2,0) + static_torch = (-3,3) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (3,0) + static_torch = (-3,3) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (4,-1) + static_torch = (-3,3) + + # this paste order it's ok for east and south orientation + # but it's wrong for north and west orientations. But using the + # default texture pack the torches are small enough to no overlap. + composite.alpha_over(img, torch, static_torch, torch) + composite.alpha_over(img, torch, moving_torch, torch) + + return img + +# trapdoor +# TODO the trapdoor is looks like a sprite when opened, that's not good +@material(blockid=96, data=range(8), transparent=True, nospawn=True) +def trapdoor(blockid, data, north): + + # north rotation + # Masked to not clobber opened/closed info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + + # texture generation + texture = terrain_images[84] + if data & 0x4 == 0x4: # opened trapdoor + if data & 0x3 == 0: # west + img = build_full_block(None, None, None, None, texture) + if data & 0x3 == 1: # east + img = build_full_block(None, texture, None, None, None) + if data & 0x3 == 2: # south + img = build_full_block(None, None, texture, None, None) + if data & 0x3 == 3: # north + img = build_full_block(None, None, None, texture, None) + + elif data & 0x4 == 0: # closed trapdoor + img = build_full_block((texture, 12), None, None, texture, texture) + + return img + +# block with hidden silverfish (stone, cobblestone and stone brick) +@material(blockid=97, data=range(3), solid=True) +def hidden_silverfish(blockid, data): + if data == 0: # stone + t = terrain_images[1] + elif data == 1: # cobblestone + t = terrain_images[16] + elif data == 2: # stone brick + t = terrain_images[54] + + img = build_block(t, t) + + return img + +# stone brick +@material(blockid=98, data=range(3), solid=True) +def stone_brick(blockid, data): + if data == 0: # normal + t = terrain_images[54] + elif data == 1: # mossy + t = terrain_images[100] + else: # cracked + t = terrain_images[101] + + img = build_full_block(t, None, None, t, t) + + return img + +# huge brown and red mushroom +@material(blockid=[99,100], data=range(11), solid=True) +def huge_mushroom(blockid, data, north): + # north rotation + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 6 + elif data == 3: data = 9 + elif data == 4: data = 2 + elif data == 6: data = 8 + elif data == 7: data = 1 + elif data == 8: data = 4 + elif data == 9: data = 7 + elif north == 'upper-right': + if data == 1: data = 9 + elif data == 2: data = 8 + elif data == 3: data = 7 + elif data == 4: data = 6 + elif data == 6: data = 4 + elif data == 7: data = 3 + elif data == 8: data = 2 + elif data == 9: data = 1 + elif north == 'lower-right': + if data == 1: data = 7 + elif data == 2: data = 4 + elif data == 3: data = 1 + elif data == 4: data = 2 + elif data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 3 + + # texture generation + if blockid == 99: # brown + cap = terrain_images[126] + else: # red + cap = terrain_images[125] + + stem = terrain_images[141] + porous = terrain_images[142] + + if data == 0: # fleshy piece + img = build_full_block(porous, None, None, porous, porous) + + if data == 1: # north-east corner + img = build_full_block(cap, None, None, cap, porous) + + if data == 2: # east side + img = build_full_block(cap, None, None, porous, porous) + + if data == 3: # south-east corner + img = build_full_block(cap, None, None, porous, cap) + + if data == 4: # north side + img = build_full_block(cap, None, None, cap, porous) + + if data == 5: # top piece + img = build_full_block(cap, None, None, porous, porous) + + if data == 6: # south side + img = build_full_block(cap, None, None, cap, porous) + + if data == 7: # north-west corner + img = build_full_block(cap, None, None, cap, cap) + + if data == 8: # west side + img = build_full_block(cap, None, None, porous, cap) + + if data == 9: # south-west corner + img = build_full_block(cap, None, None, porous, cap) + + if data == 10: # stem + img = build_full_block(porous, None, None, stem, stem) + + return img + +# iron bars and glass pane +# TODO glass pane is not a sprite, it has a texture for the side, +# at the moment is not used +@material(blockid=[101,102], data=range(16), transparent=True, nospawn=True) +def panes(blockid, data): + # no north rotation, uses pseudo data + if blockid == 101: + # iron bars + t = terrain_images[85] + else: + # glass panes + t = terrain_images[49] + left = t.copy() + right = t.copy() + + # generate the four small pieces of the glass pane + ImageDraw.Draw(right).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(left).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + up_left = transform_image_side(left) + up_right = transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM) + dw_right = transform_image_side(right) + dw_left = transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM) + + # Create img to compose the texture + img = Image.new("RGBA", (24,24), bgcolor) + + # +x axis points top right direction + # +y axis points bottom right direction + # First compose things in the back of the image, + # then things in the front. + + if (data & 0b0001) == 1 or data == 0: + composite.alpha_over(img,up_left, (6,3),up_left) # top left + if (data & 0b1000) == 8 or data == 0: + composite.alpha_over(img,up_right, (6,3),up_right) # top right + if (data & 0b0010) == 2 or data == 0: + composite.alpha_over(img,dw_left, (6,3),dw_left) # bottom left + if (data & 0b0100) == 4 or data == 0: + composite.alpha_over(img,dw_right, (6,3),dw_right) # bottom right + + return img + +# melon +block(blockid=103, top_index=137, side_index=136, solid=True) + +# pumpkin and melon stem +# TODO To render it as in game needs from pseudo data and ancil data: +# once fully grown the stem bends to the melon/pumpkin block, +# at the moment only render the growing stem +@material(blockid=[104,105], data=range(8), transparent=True) +def stem(blockid, data, north): + # the ancildata value indicates how much of the texture + # is shown. + + # not fully grown stem or no pumpkin/melon touching it, + # straight up stem + t = terrain_images[111].copy() + img = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) + img = build_sprite(t) + if data & 7 == 7: + # fully grown stem gets brown color! + # there is a conditional in rendermode-normal.c to not + # tint the data value 7 + img = tintTexture(img, (211,169,116)) + return img + + +# vines +# TODO multiple sides of a block can contain vines! At the moment +# only pure directions are rendered +# (source http://www.minecraftwiki.net/wiki/Data_values#Vines) +@material(blockid=106, data=range(8), transparent=True) +def vines(blockid, data, north): + # north rotation + if north == 'upper-left': + if data == 1: data = 2 + elif data == 4: data = 8 + elif data == 8: data = 1 + elif data == 2: data = 4 + elif north == 'upper-right': + if data == 1: data = 4 + elif data == 4: data = 1 + elif data == 8: data = 2 + elif data == 2: data = 8 + elif north == 'lower-right': + if data == 1: data = 8 + elif data == 4: data = 2 + elif data == 8: data = 4 + elif data == 2: data = 1 + + # texture generation + img = Image.new("RGBA", (24,24), bgcolor) + raw_texture = terrain_images[143] + + if data == 2: # south + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (0,6), tex) + + if data == 1: # east + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + + if data == 4: # west + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + + if data == 8: # north + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (12,0), tex) + + return img + +# fence gates +@material(blockid=107, data=range(8), transparent=True, nospawn=True) +def fence_gate(blockid, data, north): + + # north rotation + opened = False + if data & 0x4: + data = data & 0x3 + opened = True + if north == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 + if opened: + data = data | 0x4 + + # create the closed gate side + gate_side = terrain_images[4].copy() + gate_side_draw = ImageDraw.Draw(gate_side) + gate_side_draw.rectangle((7,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((7,4,9,6),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((7,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((0,12,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((14,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # darken the sides slightly, as with the fences + sidealpha = gate_side.split()[3] + gate_side = ImageEnhance.Brightness(gate_side).enhance(0.9) + gate_side.putalpha(sidealpha) + + # create the other sides + mirror_gate_side = transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT)) + gate_side = transform_image_side(gate_side) + gate_other_side = gate_side.transpose(Image.FLIP_LEFT_RIGHT) + mirror_gate_other_side = mirror_gate_side.transpose(Image.FLIP_LEFT_RIGHT) + + # Create img to compose the fence gate + img = Image.new("RGBA", (24,24), bgcolor) + + if data & 0x4: + # opened + data = data & 0x3 + if data == 0: + composite.alpha_over(img, gate_side, (2,8), gate_side) + composite.alpha_over(img, gate_side, (13,3), gate_side) + elif data == 1: + composite.alpha_over(img, gate_other_side, (-1,3), gate_other_side) + composite.alpha_over(img, gate_other_side, (10,8), gate_other_side) + elif data == 2: + composite.alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side) + composite.alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side) + elif data == 3: + composite.alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side) + composite.alpha_over(img, mirror_gate_other_side, (13,7), mirror_gate_other_side) + else: + # closed + + # positions for pasting the fence sides, as with fences + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) + + if data == 0 or data == 2: + composite.alpha_over(img, gate_other_side, pos_top_right, gate_other_side) + composite.alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side) + elif data == 1 or data == 3: + composite.alpha_over(img, gate_side, pos_top_left, gate_side) + composite.alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side) + + return img + +# mycelium +block(blockid=110, top_index=78, side_index=77) + +# lilypad +# TODO the data-block orientation relation is not clear +@material(blockid=111, data=range(4), transparent=True) +def lilypad(blockid, data, north): + if north == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 + + t = terrain_images[76] # NOTE: using same data as stairs, no + # info in minepedia at the moment. + if data == 0: # pointing south + img = build_full_block(None, None, None, None, None, t) + elif data == 1: # pointing north + img = build_full_block(None, None, None, None, None, t.rotate(180)) + elif data == 2: # pointing west + img = build_full_block(None, None, None, None, None, t.rotate(270)) + elif data == 3: # pointing east + img = build_full_block(None, None, None, None, None, t.rotate(90)) + + return img + +# nether brick +block(blockid=112, top_index=224, side_index=224) + +# nether wart +@material(blockid=115, data=range(4), transparent=True) +def nether_wart(blockid, data): + if data == 0: # just come up + t = terrain_images[226] + elif data in (1, 2): + t = terrain_images[227] + else: # fully grown + t = terrain_images[228] + + # use the same technic as tall grass + img = build_billboard(t) + + return img + +# enchantment table +# TODO there's no book at the moment +@material(blockid=116, transparent=True) +def enchantment_table(blockid, data): + # no book at the moment + top = terrain_images[166] + side = terrain_images[182] + img = build_full_block((top, 4), None, None, side, side) + + return img + +# brewing stand +# TODO this is a place holder, is a 2d image pasted +@material(blockid=117, data=range(5), transparent=True) +def brewing_stand(blockid, data, north): + t = terrain_images[157] + img = build_billboard(t) + return img + +# cauldron +@material(blockid=118, data=range(4), transparent=True) +def cauldron(blockid, data): + side = terrain_images[154] + top = terrain_images[138] + bottom = terrain_images[139] + water = transform_image_top(_load_image("water.png")) + if data == 0: # empty + img = build_full_block(top, side, side, side, side) + if data == 1: # 1/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,8), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + if data == 2: # 2/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,4), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + if data == 3: # 3/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,0), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + + return img + +# end portal +@material(blockid=119, transparent=True) +def end_portal(blockid, data): + img = Image.new("RGBA", (24,24), bgcolor) + # generate a black texure with white, blue and grey dots resembling stars + t = Image.new("RGBA", (16,16), (0,0,0,255)) + for color in [(155,155,155,255), (100,255,100,255), (255,255,255,255)]: + for i in range(6): + x = randint(0,15) + y = randint(0,15) + t.putpixel((x,y),color) + + t = transform_image_top(t) + composite.alpha_over(img, t, (0,0), t) + + return img + +# end portal frame +@material(blockid=120, data=range(5), transparent=True) +def end_porta_frame(blockid, data): + # The bottom 2 bits are oritation info but seems there is no + # graphical difference between orientations + top = terrain_images[158] + eye_t = terrain_images[174] + side = terrain_images[159] + img = build_full_block((top, 4), None, None, side, side) + if data & 0x4 == 0x4: # ender eye on it + # generate the eye + eye_t = terrain_images[174].copy() + eye_t_s = terrain_images[174].copy() + # cut out from the texture the side and the top of the eye + ImageDraw.Draw(eye_t).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(eye_t_s).rectangle((0,4,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + # trnasform images and paste + eye = transform_image_top(eye_t) + eye_s = transform_image_side(eye_t_s) + eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, eye_s, (5,5), eye_s) + composite.alpha_over(img, eye_os, (9,5), eye_os) + composite.alpha_over(img, eye, (0,0), eye) + + return img + +# end stone +block(blockid=121, top_index=175) diff --git a/overviewer_core/util.py b/overviewer_core/util.py index d6c8959..2e51a55 100644 --- a/overviewer_core/util.py +++ b/overviewer_core/util.py @@ -52,7 +52,7 @@ def findGitHash(): try: import overviewer_version return overviewer_version.HASH - except: + except Exception: return "unknown" def findGitVersion(): @@ -68,9 +68,9 @@ def findGitVersion(): # and 0.1.3 into 0.1.3 line = '-'.join(line.split('-', 2)[:2]) return line.strip() - except: + except Exception: try: import overviewer_version return overviewer_version.VERSION - except: + except Exception: return "unknown" diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 745106a..decede4 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -266,7 +266,13 @@ class World(object): ## The chunk that holds the spawn location chunkX = spawnX/16 chunkY = spawnZ/16 - + + ## clamp spawnY to a sane value, in-chunk value + if spawnY < 0: + spawnY = 0 + if spawnY > 127: + spawnY = 127 + try: ## The filename of this chunk chunkFile = self.get_region_path(chunkX, chunkY) diff --git a/setup.py b/setup.py index cd1b7d0..2cfe65a 100755 --- a/setup.py +++ b/setup.py @@ -142,7 +142,7 @@ except AttributeError: try: pil_include = os.environ['PIL_INCLUDE_DIR'].split(os.pathsep) -except: +except Exception: pil_include = [ os.path.join(get_python_inc(plat_specific=1), 'Imaging') ] if not os.path.exists(pil_include[0]): pil_include = [ ] @@ -221,7 +221,7 @@ def generate_version_py(): f = open("overviewer_core/overviewer_version.py", "w") f.write(outstr) f.close() - except: + except Exception: print "WARNING: failed to build overviewer_version file" class CustomSDist(sdist):