/HaScene

🖼️ A Simple ASCII 3D Scene Editor & Soft Rasterizer written in Haskell

Primary LanguageHaskell

🖼️ HaScene

HaScene is a simple ASCII 3D scene editor written in Haskell, using Functional Programming. It is a final project for CSE 230 Programming Languages at UCSD, inspired by this famous piece of C code. The editor is capable of reading 3D .obj models from files, rendering a simple 3D scene with shading using ASCII characters through soft rasterization, and manipulating the position and rotation of different 3D objects, and manipulate the position and rotation of different 3D object. The UI is implemented using the brick library

Demo

[Full Demo Youtube Link]

bunny

cube

Run

  1. Prepare a Haskell environment on Linux (We haven't tested the application on other platforms). You could refer to this article: Getting start with Haskell.

  2. Clone this responsory:

git clone https://github.com/TaKeTube/HaScene.git
  1. Run stack build:
stack build
  1. Execute HaScene:
stack exec HaScene

Controls

Camera Mode

You can move the camera in this mode.

w / a / s / d: forward / backward / down / right

⇧ Shift + w: up

⇧ Shift + s: down

Editing Mode

Press i to enter / exit this mode. You can manipulate 3D objects in this mode.

j / k: select 3D object from the object list

Translation

w / a / s / d: forward / backward / down / right

⇧ Shift + w: up

⇧ Shift + s: down

Rotation

Assume z-up, right-handed.

/ : rotate along y / z axis

⇧ Shift + : rotate along x axis

Change Scene

Though the default scene is hard-coded into the file, the 3D model files (.obj files) are not.

You can edit the hard coded scene in HaScene.hs, defaultScene function. Here is the default example:

defaultScene :: String -> IO [Mesh]
defaultScene filename = do
  temp <- buildMesh "src/models/hat.obj" "hat"
  let obj1 = translateMesh Move (translate 1 Right (V3 0 0 (-1)) (V3 0 0 0)) $
             translateMesh Scale (V3 1.5 0 0) temp

  temp <- buildMesh "src/models/cube.obj" "cube"
  let obj2 = translateMesh Move (translate 2 Left (V3 0 0 (-1)) (V3 0 0 0)) $
             translateMesh Scale (V3 0.8 0 0) temp

  temp <- buildMesh "src/models/bunny.obj" "bunny"
  let obj3 = translateMesh Move (translate 8 Left (V3 0 0 (-1)) (V3 0 0 0)) $
             translateMesh Move (translate 1 Down (V3 0 0 (-1)) (V3 0 0 0)) $
             translateMesh Scale (V3 30.0 0 0) temp

  temp <- buildMesh "src/models/pirate-ship.obj" "ship"
  let obj4 = translateMesh Move (translate 5 Right (V3 0 0 (-1)) (V3 0 0 0)) temp

  temp <- buildMesh "src/models/dragon.obj" "dragon"
  let obj5 = translateMesh Move (translate 10 Forward (V3 0 0 (-1)) (V3 0 0 0)) $
              translateMesh Scale (V3 1.2 0 0) temp

  return [obj1, obj2, obj3, obj4, obj5]

Design

HaScene consists of 3 parts: UI, rendering, and main control.

The main control part consists of the core logic, including keyboard input, object manipulation, and reading 3D model files, among others.

The UI part is implemented using the brick library.

The rendering part is a soft rasterizer.

Block Diagram for the Soft Rasterizer

As you can see from the diagram, we have features like MVP transformation, z-culling, blackface culling, simple Lambert shading, perspective-correct interpolation, scan line algorithm, z-buffering, etc.

block_diagram