Skip to content

Instantly share code, notes, and snippets.

@25A0
Last active December 20, 2017 21:27
Show Gist options
  • Save 25A0/163db59974d970525b14b5a4a74df598 to your computer and use it in GitHub Desktop.
Save 25A0/163db59974d970525b14b5a4a74df598 to your computer and use it in GitHub Desktop.
Random Things Voxel Projector guide

Random Things Voxel Projector guide

This guide will explain step by step how to display MagicalVoxel models in modded Minecraft, using the Voxel Projector from Random Things.

Tested with Random Things v3.7.7.1 for Minecraft v1.10.2 in singleplayer.

This is a Work-In-Progress block. This procedure might not work for later or earlier versions of Random Things.

  1. Get a MagicalVoxel model (e.g. from https://github.com/ephtracy/voxel-model, or create your own).
  2. Open your Minecraft directory (the one that contains the directories saves, screenshots, mods, and so on). If you're unsure where that directory is, open the Minecraft launcher, click Launch Options, click the profile that you usually use, and click the small green arrow to the right of Game directory.
  3. In your Minecraft directory, create a new directory called voxmodels.
  4. Move or copy the model from step 1 into that new directory.
  5. Extract the colour palette. Most MagicalVoxel models have an embedded colour palette. However, Random Things currently doesn't seem to use that palette. Instead, you will need to provide the palette in a separate file. If you have more than one model, you will need one palette for each model. You have three four options here:
    • The easiest way: (Thanks to lumien231 (the Random Things developer) for pointing this out on Reddit)
      1. Download MagicalVoxel from https://ephtracy.github.io/
      2. Open MagicalVoxel, and load the model, using the Open button in the bottom-right corner.
      3. Save the embedded palette, using the Save button in the bottom-left corner. Assuming that your model is called my_model.vox, save the palette as my_model.act in the voxmodels directory from step 3.
    • The lazy way: Use the default palette default_palette.act from this gist. Models may look weird when they're displayed with the default palette instead of the embedded palette. Store the palette in the voxmodels directory from step 3. Then, assuming that your model is called my_model.vox, you will need to rename the palette to my_model.act.
    • The easy way: Use the nifty Python3 script from this gist. Open a Windows command prompt and do the following:
        cd "path/to/your/Minecraft/directory/"
        cd "voxmodels"
        python3 extract_palette.py my_model.vox my_model.act
      
    • The hard way: Manually extract the palette. You will need to read and write binary data for this.
      1. Open the model with a hex editor of your choice.
      2. See https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt for the .vox format specs. Understanding the structure of the model file will make it easier to follow these steps. All numbers are Little Endian.
      3. The .vox file contains multiple chunks. Each chunk begins with a four-character ID. Locate the chunk with ID RGBA. If you can't find it, then the model may not contain an embedded palette. In this case, you can use the default palette from this gist, as described in the lazy way above. If the model does contain an RGBA chunk, then proceed as follows:
      4. Skip 8 bytes after the characters "RGBA", and copy the following 1024 bytes. These bytes contain the 256 embedded colours, encoded as rgba32 (32 bit per colour, 8 bit for R, G, B, A each).
      5. Assuming that your model is called my_model.vox, copy this data to a new file my_model.act.
      6. Now, remove every fourth byte from that file. Random Things requires an .act palette, which contains 256 colours in rgb24 encoding (24 bit per colour, 8 bit for R, G, B each, no alpha channel). Since the .vox file contains rgba32 colours, we need to strip the alpha channel from the embedded colours to create a valid .act palette. After this step, you should be left with 768 bytes.
  6. You should now have a file my_model.vox and a file my_model.act, both stored in a directory voxmodels within your Minecraft directory. You can repeat steps 1-5 to add more models. Note that you will need a separate palette for each model.
  7. Before you can see the models in-game, you will need to reload your world. In singleplayer, exit to the main menu and then return to your world. Restarting the game works, too.
  8. Craft the Voxel Projector from Random Things, place it somewhere, and right-click to open its GUI. You should see three sliders and a grey box. If everything went well then the grey box should contain the model(s) that you placed in the voxmodels directory. Click on the name of a model to display it.
# Copyright (c) 2017 Moritz Neikes
# Released to the public domain.
# Extracts an .act palette from a .vox model.
# Note that not all .vox models have an embedded palette.
# If the model does not contain a palette, the default
# palette is returned as an .act palette.
# See https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
# for the .vox file format specification.
import sys
import struct
# The default palette. Split into two because calls are
# limited to 255 arguments, but the palette contains 256
# colours.
default_palette = struct.pack("<128L", \
0x00000000, 0xffffffff, 0xffccffff, 0xff99ffff,
0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff,
0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff,
0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff,
0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff,
0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff,
0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff,
0xff6633ff, 0xff3333ff, 0xff0033ff, 0xffff00ff,
0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff,
0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc,
0xff66ffcc, 0xff33ffcc, 0xff00ffcc, 0xffffcccc,
0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc,
0xff00cccc, 0xffff99cc, 0xffcc99cc, 0xff9999cc,
0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc,
0xffcc66cc, 0xff9966cc, 0xff6666cc, 0xff3366cc,
0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc,
0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc,
0xffcc00cc, 0xff9900cc, 0xff6600cc, 0xff3300cc,
0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99,
0xff66ff99, 0xff33ff99, 0xff00ff99, 0xffffcc99,
0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99,
0xff00cc99, 0xffff9999, 0xffcc9999, 0xff999999,
0xff669999, 0xff339999, 0xff009999, 0xffff6699,
0xffcc6699, 0xff996699, 0xff666699, 0xff336699,
0xff006699, 0xffff3399, 0xffcc3399, 0xff993399,
0xff663399, 0xff333399, 0xff003399, 0xffff0099,
0xffcc0099, 0xff990099, 0xff660099, 0xff330099,
0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66,
0xff66ff66, 0xff33ff66, 0xff00ff66, 0xffffcc66,
0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66,
0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966,
0xff669966, 0xff339966, 0xff009966, 0xffff6666) + \
struct.pack("<128L", \
0xffcc6666, 0xff996666, 0xff666666, 0xff336666,
0xff006666, 0xffff3366, 0xffcc3366, 0xff993366,
0xff663366, 0xff333366, 0xff003366, 0xffff0066,
0xffcc0066, 0xff990066, 0xff660066, 0xff330066,
0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33,
0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33,
0xffcccc33, 0xff99cc33, 0xff66cc33, 0xff33cc33,
0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933,
0xff669933, 0xff339933, 0xff009933, 0xffff6633,
0xffcc6633, 0xff996633, 0xff666633, 0xff336633,
0xff006633, 0xffff3333, 0xffcc3333, 0xff993333,
0xff663333, 0xff333333, 0xff003333, 0xffff0033,
0xffcc0033, 0xff990033, 0xff660033, 0xff330033,
0xff000033, 0xffffff00, 0xffccff00, 0xff99ff00,
0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00,
0xffcccc00, 0xff99cc00, 0xff66cc00, 0xff33cc00,
0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900,
0xff669900, 0xff339900, 0xff009900, 0xffff6600,
0xffcc6600, 0xff996600, 0xff666600, 0xff336600,
0xff006600, 0xffff3300, 0xffcc3300, 0xff993300,
0xff663300, 0xff333300, 0xff003300, 0xffff0000,
0xffcc0000, 0xff990000, 0xff660000, 0xff330000,
0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa,
0xff000088, 0xff000077, 0xff000055, 0xff000044,
0xff000022, 0xff000011, 0xff00ee00, 0xff00dd00,
0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700,
0xff005500, 0xff004400, 0xff002200, 0xff001100,
0xffee0000, 0xffdd0000, 0xffbb0000, 0xffaa0000,
0xff880000, 0xff770000, 0xff550000, 0xff440000,
0xff220000, 0xff110000, 0xffeeeeee, 0xffdddddd,
0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777,
0xff555555, 0xff444444, 0xff222222, 0xff111111
)
def parse(buffer, offset, fstring):
size = struct.calcsize(fstring)
return offset + size, \
struct.unpack_from(fstring, buffer, offset)
def export_palette(palette_filename, buffer, offset):
with open(palette_filename, "wb") as out:
for i in range(0, 256):
offset, result = parse(buffer, offset, "BBBB")
out.write(struct.pack("BBB", result[0], result[1], result[2]))
def print_usage():
print(
"""
Usage:
%s voxel-model.vox voxel-model.act
where voxel-model.vox is the magicalVoxel model from which
the palette will be extracted, and voxel-model.act is the
file to which the extracted palette will be written.
""" % sys.argv[0], file=sys.stderr)
def abort_error(msg):
print("Error: %s" % msg, file=sys.stderr)
sys.exit(1)
def main():
if len(sys.argv) < 3:
print_usage()
sys.exit(1)
model_filename = sys.argv[1]
palette_filename = sys.argv[2]
with open(model_filename, 'rb') as model:
# Parse model file
try:
buffer = model.read(-1)
offset = 0
# Parse header
offset, result = parse(buffer, offset, "<4sL")
assert result[0] == b"VOX ", "Invalid .vox model: Missing 'VOX ' at offset 0"
assert result[1] == 150, "Cannot parse .vox models of version %d" % result[1]
# Next chunk should be MAIN
chunk_hdr_fst = "<4sLL"
offset, result = parse(buffer, offset, chunk_hdr_fst)
assert result[0] == b"MAIN", "Invalid .vox model: Cannot find 'MAIN' chunk."
# While parsing our way through the chunks, we
# need to check how many bytes of content are
# left, so that we know when to stop.
content_start = offset
total_content = result[1] + result[2]
found_rgba_chunk = True
# Now search for the RGBA chunk
while offset - content_start < total_content:
# Make sure that the remaining, unread
# content can still contain a full chunk.
remaining = total_content - (offset - content_start)
assert struct.calcsize(chunk_hdr_fst) <= remaining, \
"Remaining content does not contain a full chunk"
offset, result = parse(buffer, offset, chunk_hdr_fst)
if result[0] == b'RGBA':
# We found the RGBA chunk.
# Make sure that it contains the expected number
# of colours
assert result[1] == 1024, "The embedded palette uses an encoding we didn't expect."
# Now export the palette.
export_palette(palette_filename, buffer, offset)
found_rgba_chunk = True
break
else:
# Skip the content of this chunk
offset += result[1] + result[2]
if not found_rgba_chunk:
# export the default palette
export_palette(palette_filename, default_palette, 0)
except Exception as e:
abort_error(e)
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment