Wannabe: making a Cabinet

I’ve copied several of the issues from the last post into the github project’s issues list. Herein, I work on issue #1: A Cabinet (Projection).  Read on to see how we can make these little squares a little more cubey.It turns out, I’ve already done this, at least partly.  I misnamed it “Isometric” which is something else and something very specific. We’ll revisit that soon, but not in this little article.

A Cabinet Projection basically has one face perfectly perpendicular to the viewer, with edges leading back set at an angle (often 45º) and shortened to de-emphasize them. For Wannabe, keep in mind we’re talking about simple cubes here.  So one face will be a square, and there’ll be two parallelograms coming off it.  Here’s what a cube could look like:


Good news is I have the square face already!  Well, that’s the easy bit, but at least it’s a starting point. We’ll have to use more than one pixel to represent height and then we’ll have to draw some sides and edges.

  1. Currently, to represent changes in z (height), this projection just subtracts 1 from x and y, effectively moving up and left.  I’ll need to dig out those 1s so I can give us more space to draw cabinet-style cubes.  Here’s the current logic:
    @Override public Rendered render(Camera camera, Position position, int pixelSize) {
      position = camera.translate(position);
      rendered.left = pixelSize * position.x - position.z + camera.uiPosition.left;
      rendered.top = pixelSize * position.y - position.z + camera.uiPosition.top;
      rendered.size = pixelSize;
      return rendered;
  2. Let me try to remember what that’s doing: camera.uiPosition is always the pixel x and y of center of the display.  We subtract 1 x and 1 y pixel from that point for each increasing z.  Finally we add the voxel’s x and y values in, multiplied by the “pixel” size (which is the width/height of a voxel’s face).  And note these last values can be negative if it is left of or above the camera’s position (that’s the “translate” call on the first line of the method.)
  3. Let’s clean it up, then, and make a z multiplier explicit:
    final int hOffsetPerZ = -1;
    final int vOffsetPerZ = -1;
    @Override public Rendered render(Camera camera, Position position, int pixelSize) {
      position = camera.translate(position);
      int horizOffset = hOffsetPerZ * position.z + pixelSize * position.x;
      int vertOffset = vOffsetPerZ * position.z + pixelSize * position.y;
      rendered.left = camera.uiPosition.left + horizOffset;
      rendered.top = camera.uiPosition.top + vertOffset;
      rendered.size = pixelSize;
      return rendered;
  4. All we need to do is pass both of those values as constructor parameters.

Now for the (intermediate) fruits of these labors, left is the standard -1, -1, and right is one with -5, -5, both on a 20px square:
cab11 cab55
Interesting!  Notice how increasing the depth really makes the second shot much harder to comprehend.  It’s far more chaotic.  (You can see this with the PseudoPerspective projection as well.)  Adjacent voxels look disassociated from nearby neighbors if the height is different.  How to fix? My gut says: Add the missing sides!

Now, the Projection class doesn’t do any drawing, it only plot points where the drawing should occur. RenderTypes take the Rendered output from a projection and actually draw the pixels on the display.  Unfortunately we don’t have enough information in Rendered right now to know how deep to display.  I’ll add a couple of fields to Rendered so I can make this RenderType:

  filledThreeDSquareWithCabinetSides {
    @Override void draw(Graphics g, Rendered r) {
      g.fill3DRect(r.left, r.top, r.size, r.size, true);
      int bottom = r.top + r.size;
      int right = r.left + r.size;
      // TODO need to handle cases where I need to draw from any set of corners.
      // From bottom-left:
      g.drawLine(r.left, bottom, r.left + r.hDepth, bottom + r.vDepth);
      // From bottom-right:
      g.drawLine(right , bottom, right + r.hDepth, bottom + r.vDepth);
      // From top-right
      g.drawLine(right, r.top, right + r.hDepth, r.top + r.vDepth);

… and shoot, these values are negative, so my lines are going the wrong way!  Left shows what that looks like, and right shows the lines drawn the correct direction.

upside-down rightside-up

It’s taking shape, but I’ve introduced some bugs I need to fix.  The most obvious for now is to be able to draw from any corner.  Filling in the sides will probably help too. We’ll hit that in the next entry.

You can find the commits related to this article in this github pull.

This entry was posted in Wannabe and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *