Placing Butterfly Origami

This week I implemented something I thought would be simple. But as it turns out, even the simplest of features can introduce a few interesting problems to solve. So this is the chronicle of how I implemented Butterfly Origami.

It is sometimes said that the original origami was a butterfly, based on a poem written by Ihara Saikaku in the 1680s. While there’s evidence that the crane and other origami forms came even earlier, I still thought it would be interesting to implement butterfly origami into the mod as a nod to this idea.

I decided to implement it only as a decorative block that players can place around the world. Alongside the butterfly banners, it’s one of a few small butterfly-adjacent features I want to add in order to give the mod a bit of variety overall.

Butterfly Origami Block


I thought I could just add a block using one of Minecraft’s vanilla classes, but it turns out the specific behaviours I wanted for the block meant that I would have to implement my own custom block. Specifically, I wanted the block to have the following features:

  • It needs to be placeable on all surfaces vertical or horizontal.
  • On the horizontal plane it needs to be able to face any direction.
  • If the surface it is placed on breaks, it needs to also drop (similar to item frames or signs).

Many blocks in Minecraft have different textures/models depending on the direction they are facing, but none have more than 6 – UP, DOWN, NORTH, EAST, SOUTH, and WEST. The facing property on the block state is usually used to set up the textures and orientation, but the problem with this is that there aren’t enough directions.

This is all that the vanilla blocks in Minecraft need. Logs adjust their textures based on the direction, torches only need to handle four directions, item frames look the same in in all four directions. But the origami block isn’t square like the item frame, so can’t be supported with just 6 facings.

When the block is placed on a horizontal plane (i.e. UP or DOWN), then it will always face in the same direction. I want the block to be able to face all four directions in this case. Luckily, there is another property available by default that I can use instead.

The Jigsaw block that is usually only used for terrain generation has an orientation property that covers all of my use cases. There are 12 possible orientations, namely:

  • DOWN_EAST
  • DOWN_NORTH
  • DOWN_SOUTH
  • DOWN_WEST
  • UP_EAST
  • UP_NORTH
  • UP_SOUTH
  • UP_WEST
  • WEST_UP
  • EAST_UP
  • NORTH_UP
  • SOUTH_UP

Now I have a property I can use, I can start to implement the block itself. The Origami block uses the Orientation property and has 6 shapes depending on where it has been placed. I set it to have no collision, reduce its strength and give it a default sound type.

/**
 * A class to represent a placed butterfly origami block.
 */
public class ButterflyOrigamiBlock extends Block {

    // The block's facing property.
    public static final EnumProperty<FrontAndTop> ORIENTATION;

    // The various shapes.
    private final VoxelShape northAabb;
    private final VoxelShape southAabb;
    private final VoxelShape eastAabb;
    private final VoxelShape westAabb;
    private final VoxelShape upAabb;
    private final VoxelShape downAabb;

    /**
     * Construction
     */
    public ButterflyOrigamiBlock() {
        super(Properties.of()
                .noCollission()
                .strength(0.5F, 2.5F)
                .sound(SoundType.PINK_PETALS));

        this.registerDefaultState(this.defaultBlockState().setValue(ORIENTATION, FrontAndTop.NORTH_UP));

        this.upAabb = Block.box(1.0, 0.0, 1.0, 15.0, 4.0, 15.0);
        this.downAabb = Block.box(1.0, 12.0, 1.0, 15.0, 16.0, 15.0);
        this.northAabb = Block.box(1.0, 1.0, 12.0, 15.0, 15.0, 16.0);
        this.southAabb = Block.box(1.0, 1.0, 0.0, 15.0, 15.0, 4.0);
        this.eastAabb = Block.box(0.0, 1.0, 1.0, 4.0, 15.0, 15.0);
        this.westAabb = Block.box(12.0, 1.0, 1.0, 16.0, 15.0, 15.0);
    }

    /**
     * Create the basic definition for the block state.
     * @param blockStateBuilder The builder being used to create the block state.
     */
    @Override
    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> blockStateBuilder) {
        blockStateBuilder.add(ORIENTATION);
    }

    static {
        ORIENTATION = BlockStateProperties.ORIENTATION;
    }
}

The getStateForPlacement() method uses the side of a block the player clicked, and the direction the player is facing to set the correct orientation.

    /**
     * Get the default direction depending on where the player placed the origami.
     * @param blockPlaceContext The context for the block place action.
     * @return The new block state.
     */
    @Nullable
    @Override
    public BlockState getStateForPlacement(BlockPlaceContext blockPlaceContext) {
        FrontAndTop orientation;

        Direction direction = blockPlaceContext.getClickedFace();
        switch (direction) {
            case NORTH -> orientation = FrontAndTop.NORTH_UP;
            case SOUTH -> orientation = FrontAndTop.SOUTH_UP;
            case EAST -> orientation = FrontAndTop.EAST_UP;
            case WEST -> orientation = FrontAndTop.WEST_UP;

            default -> {
                Direction horizontalDirection = blockPlaceContext.getHorizontalDirection();
                switch (horizontalDirection) {
                    case EAST -> orientation = direction == Direction.UP ? FrontAndTop.UP_EAST : FrontAndTop.DOWN_EAST;
                    case WEST -> orientation = direction == Direction.UP ? FrontAndTop.UP_WEST : FrontAndTop.DOWN_WEST;


                    case SOUTH -> orientation = direction == Direction.UP ? FrontAndTop.UP_SOUTH : FrontAndTop.DOWN_SOUTH;

                    default -> orientation = direction == Direction.UP ? FrontAndTop.UP_NORTH : FrontAndTop.DOWN_NORTH;
                }
            }
        }
        return defaultBlockState().setValue(ORIENTATION, orientation);
    }

We can then use this orientation to set the block’s collision box. By using the front() method we can get the same direction that we would get if we were using the facing property instead.

    /**
     * Get the shape of an origami block for collision detection.
     * @param blockState The block state.
     * @param blockGetter Accessor to world blocks.
     * @param pos The position of the block.
     * @param collisionContext The context for this collision.
     * @return The shape of the block.
     */
    @NotNull
    @Override
    @SuppressWarnings("deprecation")
    public VoxelShape getShape(@NotNull BlockState blockState,
                               @NotNull BlockGetter blockGetter,
                               @NotNull BlockPos pos,
                               @NotNull CollisionContext collisionContext) {
        Direction direction = blockState.getValue(ORIENTATION).front();
        return switch (direction) {
            case NORTH -> this.northAabb;
            case SOUTH -> this.southAabb;
            case EAST -> this.eastAabb;
            case WEST -> this.westAabb;
            case DOWN -> this.downAabb;
            default -> this.upAabb;
        };
    }

The third requirement, that the origami should drop if it’s surface is removed, is implemented by responding to block updates. The updateShape() method is called whenever a nearby block is updated, and we can check here if the block it has been placed on has disappeared. The canSurvive() method is used to determine if the surface is still valid for the origami to remain in place.

    /**
     * Determines whether the block can stay placed.
     * @param blockState The current block state.
     * @param levelReader Access to the level.
     * @param pos The block's position.
     * @return TRUE if the block can survive, FALSE otherwise.
     */
    @Override
    @SuppressWarnings("deprecation")
    public boolean canSurvive(BlockState blockState,
                              LevelReader levelReader,
                              BlockPos pos) {
        Direction direction = blockState.getValue(ORIENTATION).front();
        BlockPos blockPos = pos.relative(direction.getOpposite());
        return levelReader.getBlockState(blockPos).isFaceSturdy(levelReader, blockPos, direction);
    }

    /**
     * Called on a block update.
     * @param thisBlockState The current block state.
     * @param direction The direction of the block update.
     * @param otherBlockState The state of the block that caused the update.
     * @param levelAccessor The current level.
     * @param thisBlockPos The current block position.
     * @param otherBlockPos The position of the block that caused the update.
     * @return The updated block state.
     */
    @NotNull
    @SuppressWarnings("deprecation")

    public BlockState updateShape(BlockState thisBlockState,
                                  Direction direction,
                                  @NotNull BlockState otherBlockState,
                                  @NotNull LevelAccessor levelAccessor,
                                  @NotNull BlockPos thisBlockPos,
                                  @NotNull BlockPos otherBlockPos) {
        return direction.getOpposite() ==
                thisBlockState.getValue(ORIENTATION).front() && !thisBlockState.canSurvive(levelAccessor, thisBlockPos) ?
                Blocks.AIR.defaultBlockState() :
                super.updateShape(thisBlockState, direction, otherBlockState, levelAccessor, thisBlockPos, otherBlockPos);
    }

With the new class created, I can now register 16 blocks, one for each colour, as well as block items for each. Essentially I just follow the Block Checklist and Item Checklist to remind myself of any steps I might miss. For the Creative Inventory, I list them under COLORED_BLOCKS – it seems an appropriate place for them.

Block State


The block state uses several variants based on the Orientation property of the block. The model used is the same, but the x and y rotations are set to make the block face the right way. In total, there are 12 variants that will ensure the model is oriented correctly. For the vertical directions (NORTH, SOUTH, EAST, and WEST), the block will always face upwards.

{
  "variants": {
    "orientation=down_east": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 180,
      "y": 270
    },
    "orientation=down_north": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 180,
      "y": 180
    },
    "orientation=down_south": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 180
    },
    "orientation=down_west": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 180,
      "y": 90
    },
    "orientation=east_up": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 270,
      "y": 270
    },
    "orientation=north_up": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 270,
      "y": 180
    },
    "orientation=south_up": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 270
    },
    "orientation=up_east": {
      "model": "butterflies:block/butterfly_origami_black",
      "y": 270
    },
    "orientation=up_north": {
      "model": "butterflies:block/butterfly_origami_black",
      "y": 180
    },
    "orientation=up_south": {
      "model": "butterflies:block/butterfly_origami_black"
    },
    "orientation=up_west": {
      "model": "butterflies:block/butterfly_origami_black",
      "y": 90
    },
    "orientation=west_up": {
      "model": "butterflies:block/butterfly_origami_black",
      "x": 270,
      "y": 90
    }
  }
}

Model


To create the model, I looked up some reference images and used Blockbench to create a pixelated version of the origami. I want it to come in all 16 default colours, so I didn’t worry about texturing the block for now. It came out looking very simple, although it uses quite a few cuboids to get its unique shape.

After exporting this to a JSON file, I was able to use this to create a model that would serve as the base for all 16 colours. The main file contains the model’s cube elements, but doesn’t set the texture.

{
  "textures": {
    "particle": "#all"
  },
  "elements": [
    {
      "from": [14, 3, 1],
      "to": [15, 4, 2],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 1], "texture": "#all"}
      }
    },
    {
      "from": [1, 3, 1],
      "to": [2, 4, 2],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 1], "texture": "#all"}
      }
    },
    {
      "from": [10, 1, 2],
      "to": [12, 2, 3],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 1], "texture": "#all"}
      }
    },
    {
      "from": [4, 1, 2],
      "to": [6, 2, 3],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 1], "texture": "#all"}
      }
    },
    {
      "from": [9, 1, 3],
      "to": [11, 2, 7],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 4], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 4], "texture": "#all"}
      }
    },
    {
      "from": [5, 1, 3],
      "to": [7, 2, 7],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 4], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 4], "texture": "#all"}
      }
    },
    {
      "from": [11, 1, 8],
      "to": [13, 2, 13],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 5, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 5, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 5], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 5], "texture": "#all"}
      }
    },
    {
      "from": [3, 1, 8],
      "to": [5, 2, 13],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 5, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 5, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 5], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 5], "texture": "#all"}
      }
    },
    {
      "from": [10, 1, 7],
      "to": [11, 2, 9],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 2], "texture": "#all"}
      }
    },
    {
      "from": [5, 1, 7],
      "to": [6, 2, 9],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 2], "texture": "#all"}
      }
    },
    {
      "from": [13, 1, 9],
      "to": [14, 2, 11],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 2], "texture": "#all"}
      }
    },
    {
      "from": [2, 1, 9],
      "to": [3, 2, 11],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 2], "texture": "#all"}
      }
    },
    {
      "from": [11, 1, 13],
      "to": [12, 2, 15],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 2], "texture": "#all"}
      }
    },
    {
      "from": [4, 1, 13],
      "to": [5, 2, 15],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 2], "texture": "#all"}
      }
    },
    {
      "from": [7, 0, 3],
      "to": [9, 1, 10],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 7, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 7, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 7], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 7], "texture": "#all"}
      }
    },
    {
      "from": [9, 0, 7],
      "to": [10, 1, 11],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 4], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 4], "texture": "#all"}
      }
    },
    {
      "from": [6, 0, 7],
      "to": [7, 1, 11],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 4], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 4], "texture": "#all"}
      }
    },
    {
      "from": [10, 0, 9],
      "to": [11, 1, 13],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 4], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 4], "texture": "#all"}
      }
    },
    {
      "from": [5, 0, 9],
      "to": [6, 1, 13],
      "faces": {
        "north": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 1, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 4, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 1, 4], "texture": "#all"},
        "down": {"uv": [0, 0, 1, 4], "texture": "#all"}
      }
    },
    {
      "from": [12, 2, 1],
      "to": [14, 3, 3],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 2], "texture": "#all"}
      }
    },
    {
      "from": [2, 2, 1],
      "to": [4, 3, 3],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 2], "texture": "#all"}
      }
    },
    {
      "from": [11, 2, 3],
      "to": [14, 3, 5],
      "faces": {
        "north": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 3, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 3, 2], "texture": "#all"}
      }
    },
    {
      "from": [2, 2, 3],
      "to": [5, 3, 5],
      "faces": {
        "north": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 3, 2], "texture": "#all"},
        "down": {"uv": [0, 0, 3, 2], "texture": "#all"}
      }
    },
    {
      "from": [11, 2, 5],
      "to": [13, 3, 8],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 3], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 3], "texture": "#all"}
      }
    },
    {
      "from": [3, 2, 5],
      "to": [5, 3, 8],
      "faces": {
        "north": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "east": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "south": {"uv": [0, 0, 2, 1], "texture": "#all"},
        "west": {"uv": [0, 0, 3, 1], "texture": "#all"},
        "up": {"uv": [0, 0, 2, 3], "texture": "#all"},
        "down": {"uv": [0, 0, 2, 3], "texture": "#all"}
      }
    }
  ]
}

For the textures, I use one model for each colour. These models inherit from the base model above, and set only the texture. For now I just use Minecraft’s wool block textures, but I may change that in the future.

{
  "parent": "butterflies:block/butterfly_origami",
  "textures": {
    "all": "minecraft:block/black_wool"
  }
}

Finishing Up


After all this work is complete, I also add item models, recipes (and recipe advancements), as well as loot tables to make sure they drop an item when destroyed. Rather than creating all 16 sets of JSON data, I only created the data for the black origami. I then use the Python data generator with some small modifications in order to generate the files need for the other 15 blocks.

This feature is now technically complete. There are a few things I may change. I’m not happy with the inventory icons for the origami. I might also adjust the block model and textures to make it look a little but nicer in game, and I may give it its own unique sounds rather than reusing a vanilla sound.

But in terms of the technical implementation it is finished, and I’m happy with the progress so far.