Skip to main content

3D

import * as ThreeD from "tts/3d"

Perspective projection plus pen-drawn 3D: wireframes, filled triangles with back-face culling, and sprites placed in 3D space. See examples/3d-demo: WASD / arrows to walk and turn, Q/E to look up and down.

The 3D demo: a grid floor, wireframe boxes, a spinning filled pyramid and a floating gem sprite

import * as ThreeD from "tts/3d";
import * as Input from "tts/input";

whenFlag(() => {
me.visible = false; // the pen draws even while the sprite is hidden
ThreeD.setCamera(0, 60, -400, 0, -10);
forever(() => {
ThreeD.cam3d.yaw += Input.axisX() * 3;
ThreeD.moveForward(Input.axisY() * 8);
penClear();
setPenColor("#4C97FF");
ThreeD.grid(0, 80, 12);
setPenColor("#FFFFFF");
ThreeD.cube(0, 40, 200, 80);
});
});

World axes: x right, y up, z forward. Angles are in degrees; positive yaw turns right and positive pitch looks up.

Camera​

setCamera(x, y, z, yaw, pitch)position and orientation
moveForward(d) / strafe(d)walk relative to the yaw
setFov(scale)projection scale (300 by default; larger zooms in)
cam3d.camX, camY, camZ, yaw, pitch, fovthe shared camera; change it directly

Drawing​

Set the pen colour first; each primitive draws with the current pen.

line3d(x1, y1, z1, x2, y2, z2)a line (skipped if an end is behind the camera)
box(x, y, z, w, h, d) / cube(x, y, z, size)wireframe boxes
grid(height, cell, count)a floor grid
triangle3d(...9 numbers)a filled triangle
setCulling(true)skip triangles facing away (wind faces counter-clockwise as seen from outside)
line2d(x0, y0, x1, y1) / fillTriangle(...)screen-space versions, clipped to the stage

Projection and sprites​

project(x, y, z)returns false if the point is behind the camera; otherwise sets sx, sy, depth
placeSprite(x, y, z, baseSize)show this sprite at a 3D point, scaled by distance
rotate(x, z, angle)rotate around the y axis; sets rx, rz

Tips​

  • Draw 3D with one hidden sprite that has a tiny costume, and call penClear() at the start of each frame.
  • Put the drawing in a /** @warp */ function, or use the engine functions (already warped), so each frame draws all at once.
  • There's no depth buffer: draw far objects first, and use culling for closed shapes.
  • Pen drawing costs time per line, so keep scenes to a few hundred lines per frame.