I'm building the original SimCity (open-sourced) icons in MagicaVoxel such that rendering them closely resembles the original. However, I'm having trouble understanding exactly what I'm building.

The original graphics have a kind of overhead view, but it isn't quite isometric. I'm not sure what to call this perspective. And, more importantly, I'm unclear how to achieve it in MagicaVoxel with ease.

The best I've been able to do is create the voxel object at the same "resolution" as the original bitmaps. Then, when it comes time to render, I position the voxel space with perspective camera positioned around x:87 y:80 z:105. Isometric and orthographic resist my attempts to make them mimic the source perspective. MagicaVoxel and the original SimCity tile

Rendering this results in a GIANT image with my graphic occupying a tiny portion of the upper corner, and in the spirit of pixel-perfection this is only approximating the perspective of the original artwork. I feel there must be some better way to achieve what I'm looking for.

The render by MagicaVoxel using a wildly-offset camera approach

The big problem with this approach is that different tiles have different resolutions and keeping everything proportional to one another using a "eyeball it until the angles look right" approach is too hard to maintain consistency from image to image. I should be able to draw any tile and get consistent, uniform render results. But maybe I'm expecting too much?

In strict perspective mode, perspective lines converge to the center. perspective mode sample

In isometric and orthographic mode, the buildings flatten out and show no depth isometric mode sample

I am not opposed to exporting models into Blender for rendering, if there is a better solution in that application

Update: Using orthographic camera set as shown achieves something approximating the original tile art, but needs to be skewed back into correct square proportions (trying to unskew it in Photoshop resulted in a blurry mess) enter image description here

  • This question just makes me in a good mood. 😀 May I ask why you need to recreate all the graphics as voxels when you want them to be rendered exactly like the original anyway? Is it because you want to make additional tiles?
    – Wolff
    Jul 12 '21 at 13:10
  • Or are you trying to render them at "higher resolution" than the original?
    – Wolff
    Jul 12 '21 at 13:15
  • @Wolff It was just a "something to tinker with on a weekend project" that turned into a "huh, what exactly is going on here?" bigger question. I've been enjoying MagicaVoxel and I like the hybrid high-res/pixelated look it creates. "Updating" those old graphics just made for a neat way to enjoy the old look in a new way, but that led to the rabbit hole of this post. Starting with exact voxel representations could act as a base for more detailed revisions down the line. But for now, "Gotta start somewhere" 🤷‍♂️ Jul 12 '21 at 23:49
  • Ah, I thought so. I was just tinkering myself with using HTML Canvas and JavaScript to render cabinet projection using the slice PNG you can export from MagicaVoxel. This is my result with the monu10 model that comes with the program.
    – Wolff
    Jul 13 '21 at 0:03
  • That's neat. Didn't know about the slice PNG export. I guess each slice is drawn at an offset from the previous one? For maintaining a pure pixelized look that's a great option. Jul 13 '21 at 0:24

You're not getting correct results because the original SimCity uses a Cabinet projection — a subtype of Oblique projection and I doubt MagicaVoxel supports it.

enter image description here

Personally I don't know any software that could render in that projection but I found a tutorial of rendering an oblique projection in the free Blender.

In the tutorial author creates a specific camera setup and a UV-map to distort the orthographic render to cabinet projection. What's happening in the tutorial could be not very straightforward if you don't know Blender but it's easy to follow. Here's the scene with the UV-file included and camera set up for top-down projection instead of side projection like in the tutorial if you want to play with it.

I used the scene it to recreate this reference:

enter image description here

My result (don't mind the geometry/materials, the goal was a proof of concept):

enter image description here

Here's how the scene looks inside the Blender:

enter image description here

  • 2
    "cabinet projection" That's the vocabulary I was missing to follow-through on researching this. Thank you very much. I can muddle my way around Blender enough to follow this, I think. I appreciate you taking a stab at it as well and for the Dropbox file; it looks really great. Cheers! Jul 11 '21 at 7:34
  • @christopherdrum it might be worth asking magicavoxel devs if its possible to make it so that you have access to the camera matrix.
    – joojaa
    Jul 11 '21 at 8:14
  • @Sergey Kritskiy Interestingly, I see his first description for a solution was basically what I was stumbling upon at the end of my post. Render in orthographic, then skew the render to fit. The UV mapping trick seems to kind of be doing this as well, if I understand the transformation pipeline correctly. It's all in one render step instead of two, but that's what's happening, yes? Jul 11 '21 at 8:25
  • @joojaa You know, that's a good point about just asking the dev directly. Now that I understand what I'm asking for, I can ask an informed question. Jul 11 '21 at 8:27
  • @christopherdrum yes, exactly: uv-map just skews the whole thing. Note that the render still isn't exactly orthographic, camera matrix was modified by track constraint Jul 11 '21 at 8:33

This is perhaps not the answer you want to hear, but POV-Ray can render in this projection without any tricks or cheats.

It works by using its orthographic projection, but directly using the up, right, and direction keywords instead of the more common look_at. Doing this gives us a helpful "Parse Warning: Camera vectors are not perpendicular." but of course that is exactly what we want.

I made a randomly generated scene with some exaggerated patterned boxes to show the effect, and using a parallel light source so that the shadows are also projected correctly:

Randomly generated POV-Ray render in an oblique projection

The up and right vectors defines the size of the viewport and should have the same aspect ratio as the output image. As can be seen, the voxels are "pixel perfect" (voxel perfect?) in the XY plane, and every angle is 45°.

(For brevity, the river is not included here. It is left as an exercise to the reader.)

#version 3.7;
global_settings { assumed_gamma 1 radiosity { count 500 error_bound 0.03 } }

camera {
  right x*640/8
  up    z*480/8
  location -100*<1,-2,1> // These two lines (combined with orthographic)
  direction 100*<1,-2,1> //  are the key to the projection

light_source {
  100*<-1,2,1> color rgb 1.2
  parallel point_at 0    // Parallel for straight uniform shadows

plane {  y, 0 pigment { rgb 1 } }
plane { -y, -300 no_shadow finish { emission 1 } pigment { rgb 1 } }

// Create our little town
#include "stdinc.inc"
#declare S = seed(87);
#macro rndi(a) int(rand(S)*a) #end
#macro Block() Center_Object( union {
    #local c = CHSL2RGB(VRand(S)*<40,.2,.2>+<10,.3,.2>);
    #local T = pigment { cells color_map { [0 color c/5] [1 color c] } }
      #local K= 12-<rndi(b),7-b*3-rndi(5)/2,rndi(b)>*2;
      union {
        difference { box {y,K} box {xz,K-xz+y} pigment{T} }
        box { 1,K-1 pigment { rgb .6 } }
        box { -xz,K*xz+1 pigment { rgb <.3,.5,.2> } }
        translate -2*rndi(b*2)*xz rotate y*90*mod(b+int(c.x*100),4)
  }, xz )
#macro stack(O,N,D) union { object{O} Align_Object(N,D,max_extent(O)+2) } #end
#macro Row() stack( stack( Block(), Block(), -x), Block(), -x ) #end
object { stack( Row(), Row(), -z ) translate <-26,0,-12> }
  • 1
    The "no tricks or cheats" is a big plus for me to try out this method. I've heard of, but never touched, POV-Ray. Thanks for the sample code to get my feet wet; the render looks spot-on. From one Amiga enthusiast to another, thank you! Jul 11 '21 at 22:19
  • @christopherdrum POV-Ray isn't exactly user-friendly, but I used it a lot 25 years ago on my Amiga. It may be possible to export wireframe meshes into POV-Ray for a final render using this projection.
    – pipe
    Jul 11 '21 at 22:48
  • this is so cool! can it load external files to render? Jul 12 '21 at 6:15
  • I see POV-Ray can be set as a renderer in Blender, but documentation says only materials can be adjusted through the POV scripting language. So the camera would appear to be inaccessible. Despite that support, POV exporting doesn't seem possible. Mesh distortion in Blender is currently looking like the "easiest" path forward. Feels like we're circling around a definitive solution, but I can't see it. Jul 12 '21 at 8:28
  • 1
    @SergeyKritskiy I don't think POV-Ray can import many object formats directly, but it does have a number of triangle/mesh-type representations so it should be possible to convert a lot of formats. I know there are 3DS and STL converters at least. In OPs case perhaps I would try to get MagicaVoxel to export the voxels as cubes, and write a small script to convert it into simple box { <1,2,3>,<1,2,3>+1 } statements.
    – pipe
    Jul 26 '21 at 5:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.