Importing Wavefront/.OBJ-files from Magica Voxel to Bevy with Palettes

written on 10 Dec 2022

games

voxel

bevy

rust


a plane object imported with texture into Bevy

For an experiment I wanted to create some voxel-models in Magica Voxel and use them with the Bevy game engine. The problem I encountered was that my model did not have the the same colors in Bevy. This post is to hopefully help anyone who attempts the same.

First off, my experience with 3D-models and game engines is very limited, so apologies if I get some things wrong.

With Bevy I am using the bevy_obj-plugin to be able to load Wavefront/.obj-files using the asset loader. For those not familiar with Bevy; it’s a pretty cool game engine being developed in Rust.

Problem

a plane model inside of magica voxel

The problem arises when we want to use our exported .OBJ-file from Magica Voxel. We load the mesh using the asset loader and use it using a standard bundle that gives us some nice things like shadows.

let plane_mesh_handle = asset_server.load("models/plane.obj");

commands.spawn(PbrBundle {
    mesh: plane_mesh_handle.clone(),
    ..default()
});

This code makes our plane look like in the image below, which is missing its intended colors. My expectation was that Magica Voxel saved the colors in the vertex data being exported to the .obj-file, but this is not the case for me. This might be possible, but I at least have not figures out how.

The solution

When exporting the model to Wavefront/.OBJ we generate three files. The first is the actual .obj file with the mesh data, the second is an .mtl file with the material properties of the objects, and the third is the exported palette we used for our model as a .png.

Inside our .mtl file (output below) we can see our object is expected to use the palette as a material, and currently we are not supplying our mesh with any material in Bevy.

# MagicaVoxel @ Ephtracy

newmtl palette
illum 1
Ka 0.000 0.000 0.000
Kd 1.000 1.000 1.000
Ks 0.000 0.000 0.000
map_Kd plane.png

The solution is simply to load the palette/.png file using the asset loader, and supplying it as a material to our mesh:

let plane_mesh_handle = asset_server.load("models/plane.obj");
let plane_mesh_palette = asset_server.load("models/plane.png");

commands.spawn(PbrBundle {
    mesh: plane_mesh_handle.clone(),
    material: materials.add(StandardMaterial { 
        base_color_texture: Some(plane_mesh_palette.clone()),
        ..default() }),
    ..default()
});

This results in the object we want!

a plane object imported with texture into Bevy