Render3D

LittleJS 3D Rendering Plugin

  • Adds a 3D scene that draws into the same WebGL canvas as the 2D game
  • Call new Render3DPlugin() in gameInit, then move render3D.camera and make EngineObject3D objects
  • EngineObject3D is an EngineObject with a 3D position, rotation and mesh
  • The 3D scene draws under the 2D sprites, so HUD and text land on top
  • Lighting is the sun plus ambient, with optional extra lights, fog and shadows
  • Any object or draw can bring its own Shader, a mainImage snippet the lighting then applies to
  • Build shapes with buildBox, buildSphere, buildGrid and buildLathe; the other builders, terrain, particles, camera controls and the OBJ loader are in the Render3D Extras plugin, which goes after this one
  • Requires the Math3D plugin

Classes

Camera3D
CameraControl3D
DirectionalLight3D
EngineObject3D
FirstPersonCamera3D
HeightMap
InstancedMesh3D
Light3D
Mesh
ParticleEmitter3D
Render3DPlugin
Trail3D
VoxelMap

Members

(static) render3D :Render3DPlugin

Global Render3D plugin object

Type:
  • Render3DPlugin

Methods

(static) buildBox(sizeopt, bevelopt, bevelSegmentsopt) → {Mesh}

Build a box centered on the origin, six flat faces with uvs covering each face

  • bevel cuts its edges and corners: 1 segment is a flat chamfer, more round them, and the biggest bevel, half the smallest side, rounds a cube into a ball
Parameters:
NameTypeAttributesDefaultDescription
sizeVector3 | number<optional>
1

Full size, a number for a cube

bevelnumber<optional>
0

Size of the cut on each edge, clamped to half the smallest side

bevelSegmentsnumber<optional>
1

Steps around each edge, 1 for a flat chamfer

Returns:
Type: 
Mesh

(static) buildCapsule(sizeopt, heightopt, sidesopt, ringsopt, smoothopt) → {Mesh}

Build a capsule standing on the Y axis, centered on the origin: a cylinder with a half sphere on each end

Parameters:
NameTypeAttributesDefaultDescription
sizenumber<optional>
1

Diameter

heightnumber<optional>
1

Total height including the rounded ends, at least the size

sidesnumber<optional>
16

Around

ringsnumber<optional>
4

On each end

smoothboolean<optional>

Defaults to render3D.smoothShading

Returns:
Type: 
Mesh

(static) buildCone(sizeopt, heightopt, sidesopt, smoothopt, cappedopt, bevelopt, bevelSegmentsopt) → {Mesh}

Build a cone standing on the Y axis, centered on the origin, the point up

Parameters:
NameTypeAttributesDefaultDescription
sizenumber<optional>
1

Diameter of the base

heightnumber<optional>
1
sidesnumber<optional>
16

Around

smoothboolean<optional>

Defaults to render3D.smoothShading

cappedboolean<optional>
true

Close the base

bevelnumber<optional>
0

Size of the cut on the base rim, clamped to the radius and half the slanted side

bevelSegmentsnumber<optional>
1

Steps around the rim, 1 for a flat chamfer

Returns:
Type: 
Mesh

(static) buildCylinder(sizeopt, heightopt, sidesopt, smoothopt, cappedopt, bevelopt, bevelSegmentsopt) → {Mesh}

Build a cylinder standing on the Y axis, centered on the origin

Parameters:
NameTypeAttributesDefaultDescription
sizenumber<optional>
1

Diameter

heightnumber<optional>
1
sidesnumber<optional>
16

Around

smoothboolean<optional>

Defaults to render3D.smoothShading

cappedboolean<optional>
true

Close the ends

bevelnumber<optional>
0

Size of the cut on the top and bottom rims, clamped to the radius and half the height

bevelSegmentsnumber<optional>
1

Steps around each rim, 1 for a flat chamfer

Returns:
Type: 
Mesh

(static) buildExtrude(pixels, sizeopt, depthopt) → {Mesh}

Turn a sprite into a 3D block model by giving its pixels thickness

  • A pixel counts as solid when it is more than half opaque
  • Each pixel keeps its own color, so white art takes the object's tint
  • Runs of matching pixels merge into one face, and side walls appear only at the sprite's edges
  • A texture's pixels are read once and kept, so redrawing a canvas texture will not change what this builds
  • Pixels can also be an array of rows, each a Color, a truthy value for white, or a falsy value for empty
Parameters:
NameTypeAttributesDefaultDescription
pixelsTileInfo | Array.<Array.<(Color|number|boolean)>>

A tile from a loaded texture, or rows of pixels, each a Color (empty when see through), a truthy value for white or a falsy value for empty

sizeVector2<optional>

World width and height of the whole tile, centered like buildBox

depthnumber<optional>
1

Thickness along Z

Returns:
Type: 
Mesh
Example
new EngineObject3D(vec3(), buildExtrude(tile(3, 16), vec2(2), .5)); // a chunky version of tile 3

(static) buildGrid(sizeopt, segmentsopt, coloropt, heightFunctionopt, smoothopt) → {Mesh}

Build a heightfield grid in the XZ plane centered on the origin

  • smooth rounds the lighting across cells and colors each corner
  • flat lights and colors each cell on its own, so a checkerboard stays crisp
  • doubleSided, a sheet seen from both sides; turn it off for ground only ever seen from above
  • One cell is a plain square, render3D.planeMesh and planeMeshDoubleSided are shared ones
Parameters:
NameTypeAttributesDefaultDescription
sizeVector2 | number<optional>

World size along X and Z, a number for a square

segmentsVector2 | number<optional>
1

Cells along X and Z, a number for both

colorColor | function<optional>

One Color for the whole grid, or (x, z) => Color

heightFunctionfunction<optional>

(x, z) => y, default flat

smoothboolean<optional>

Defaults to render3D.smoothShading with a heightFunction; a flat grid is flat shaded, since its light is even anyway and smoothing would only blend its cell colors; pass smooth to blend a gradient

Returns:
Type: 
Mesh
Example
const ground = buildGrid(vec2(20), 10, (x, z)=> (floor(x / 2) + floor(z / 2)) & 1 ? GRAY : WHITE); // 2 unit checks

(static) buildLathe(profile, sidesopt, smoothopt, cappedopt) → {Mesh}

Spin a flat outline around the Y axis to make a round shape, like a vase or a wheel

  • profile is [[radius, y], ...] from bottom to top
  • A profile that ends where it starts makes a closed ring like a donut
  • An end left open, with a radius and no cap, makes the mesh doubleSided so its inside shows
  • An end on the axis smooth shades as a round pole like a sphere's when its segment is within 45 degrees of level, and as a point like a cone's tip when it is steeper
Parameters:
NameTypeAttributesDefaultDescription
profileArray.<Array.<number>>
sidesnumber<optional>
16

Around the axis

smoothboolean<optional>

Defaults to render3D.smoothShading

cappedboolean<optional>
true

Close the ends that have a radius with flat discs

Returns:
Type: 
Mesh
Example
const vase = buildLathe([[0, -1], [.8, -.3], [.9, .2], [.4, .6], [0, 1]], 12);

(static) buildLoft(stations) → {Mesh}

Build a hull from a row of diamond shaped slices along Z, for ships, planes and cars

  • Each slice is [z, width, top, bottom, sideHeight]
  • sideHeight is 0 to 1 and puts the side corners between the bottom and the top
  • List the slices nose first, with the nose at the largest z
Parameters:
NameTypeDescription
stationsArray.<Array.<number>>
Returns:
Type: 
Mesh
Example
const hull = buildLoft([[1.2, .4, .2, -.1], [0, 1.4, .5, -.4], [-1, 1, .3, -.3]]);

(static) buildRibbon(points, widthopt, coloropt, closedopt, upopt) → {Mesh}

Build a lit ribbon along a path, for roads, tracks and walls

  • Each segment is a flat quad, the sides are across the path in the plane of the up vector
  • doubleSided, so it is seen and lit from below as well
Parameters:
NameTypeAttributesDefaultDescription
pointsArray.<Vector3>

Center line in order

widthnumber | Array.<number><optional>
1

Full width, one for all or one per point

colorColor | Array.<Color><optional>

One for all or one per point

closedboolean<optional>
false

Join the last point back to the first

upVector3<optional>

Which way the ribbon faces

Returns:
Type: 
Mesh
Example
const road = buildRibbon(trackPoints, 8, GRAY, true); // a loop of road

(static) buildSky(topColoropt, horizonColoropt, bottomColoropt, sidesopt, ringsopt) → {Mesh}

Build a sky dome: a sphere colored by direction, wound to be seen from inside

  • set it as render3D.sky and the pass draws it around the camera behind everything
Parameters:
NameTypeAttributesDefaultDescription
topColorColor<optional>

Straight up

horizonColorColor<optional>

Level with the camera

bottomColorColor<optional>

Straight down, what a camera looking at the ground sees past its edge; defaults to the horizon color

sidesnumber<optional>
16

Around

ringsnumber<optional>
8

Top to bottom

Returns:
Type: 
Mesh

(static) buildSphere(sizeopt, sidesopt, ringsopt, smoothopt) → {Mesh}

Build a sphere centered on the origin

Parameters:
NameTypeAttributesDefaultDescription
sizenumber<optional>
1

Diameter

sidesnumber<optional>
16

Around

ringsnumber<optional>
8

Top to bottom

smoothboolean<optional>

Defaults to render3D.smoothShading

Returns:
Type: 
Mesh

(static) buildText3D(text, sizeopt, depthopt, fontopt) → {Mesh}

Build a mesh of extruded text from an image font, the engine font by default so it needs no assets

  • Each glyph is extruded once per font and reused, the block is centered and faces +Z
  • Newlines stack downward, spaced a little wider than the character height so the sides do not collide
  • Every call builds a new mesh, dispose the old one when text changes often
  • Glyphs are white in the engine font, so the object's color tints the text
Parameters:
NameTypeAttributesDefaultDescription
textstring | number
sizenumber<optional>
1

Character height in world units

depthnumber<optional>
0.2

Thickness along Z

fontImageFont<optional>

Defaults to engineImageFont

Returns:
Type: 
Mesh
Example
new EngineObject3D(vec3(0, 2, 0), buildText3D('HELLO'), undefined, YELLOW);

(static) buildTorus(sizeopt, tubeSizeopt, sidesopt, tubeSidesopt, smoothopt) → {Mesh}

Build a donut lying flat around the Y axis

Parameters:
NameTypeAttributesDefaultDescription
sizenumber<optional>
1

Diameter of the whole donut, outside edge to outside edge

tubeSizenumber<optional>
0.3

Diameter of the tube

sidesnumber<optional>
16

Around the ring

tubeSidesnumber<optional>
8

Around the tube

smoothboolean<optional>

Defaults to render3D.smoothShading

Returns:
Type: 
Mesh

(static) debugBox3D(pos, sizeopt, coloropt, timeopt, rotationopt)

Draw a debug wireframe box

Parameters:
NameTypeAttributesDefaultDescription
posVector3

Center

sizeVector3 | number<optional>
1

Full size, a number for a cube

colorColor<optional>
timenumber<optional>
0

How long to show it, 0 is one frame

rotationVector3<optional>

vec3(pitch, yaw, roll)

(static) debugLine3D(posA, posB, coloropt, widthopt, timeopt)

Draw a debug line

Parameters:
NameTypeAttributesDefaultDescription
posAVector3
posBVector3
colorColor<optional>
widthnumber<optional>
timenumber<optional>
0

How long to show it, 0 is one frame

(static) debugPoint3D(pos, coloropt, timeopt, sizeopt)

Draw a debug point as a small cross of three lines

Parameters:
NameTypeAttributesDefaultDescription
posVector3
colorColor<optional>
timenumber<optional>
0

How long to show it, 0 is one frame

sizenumber<optional>
0.2

Length of the cross

(static) debugSphere3D(pos, sizeopt, coloropt, timeopt)

Draw a debug wireframe sphere as three rings

Parameters:
NameTypeAttributesDefaultDescription
posVector3

Center

sizenumber<optional>
1

Diameter

colorColor<optional>
timenumber<optional>
0

How long to show it, 0 is one frame

(static) engineObjectsCallback3D(pos, size, callback, objectsopt, testCentersopt)

Call a function for each EngineObject3D whose box overlaps a sphere or a box

  • An object destroyed by an earlier callback is skipped
Parameters:
NameTypeAttributesDefaultDescription
posVector3

Center of the area

sizeVector3 | number

Diameter of a sphere if a number, 0 for a point, full size of a box if a Vector3

callbackfunction
objectsArray.<EngineObject><optional>

Defaults to every object

testCentersboolean<optional>
false

Test only each object's center, see engineObjectsCollect3D

(static) engineObjectsCollect3D(pos, size, objectsopt, testCentersopt) → {Array.<EngineObject3D>}

Collect the EngineObject3D objects whose boxes overlap a sphere or a box, the 3D twin of engineObjectsCollect

  • Each object is its size3D box at its world position, turned as it is turned; lights, emitters and trails have no size and are never collected
Parameters:
NameTypeAttributesDefaultDescription
posVector3

Center of the area

sizeVector3 | number

Diameter of a sphere if a number, 0 for a point, full size of a box if a Vector3

objectsArray.<EngineObject><optional>

Defaults to every object

testCentersboolean<optional>
false

Test only each object's center, a little faster, and ignores object sizes

Returns:
Type: 
Array.<EngineObject3D>

(static) engineObjectsRaycast3D(ray, objectsopt) → {Array.<EngineObject3D>}

Collect every EngineObject3D a ray passes through, nearest first, the 3D twin of engineObjectsRaycast

  • The ray has no end, so everything along it counts however far away it is
  • Use render3D.pick for the nearest one on its own, with the distance to it
Parameters:
NameTypeAttributesDescription
rayRay3D

From render3D.screenToRay, or any ray

objectsArray.<EngineObject><optional>

Defaults to every object; only those with a mesh or a sprite count

Returns:
Type: 
Array.<EngineObject3D>

(async, static) loadOBJ(url, smoothopt) → {Promise.<Mesh>}

Fetch and parse an OBJ file

Parameters:
NameTypeAttributesDescription
urlstring
smoothboolean<optional>

Compute smooth normals when the file has none, defaults to render3D.smoothShading

Returns:
Type: 
Promise.<Mesh>
Example
const mesh = await loadOBJ('ship.obj'); // in an async gameInit

(static) normalMapFromHeight(size, heightFunction, strengthopt) → {TextureInfo}

Make a normal map from a height at each pixel, for bumps and grooves with no image file: the slope at each pixel from its neighbors' heights, taken around the edges so the map tiles; set it as an object's normalMap

Parameters:
NameTypeAttributesDefaultDescription
sizeVector2

In pixels

heightFunctionfunction

The height 0 to 1 at a pixel, x across and y down

strengthnumber<optional>
1

How steep the slopes are: a height change of 1 over one pixel leans the normal by strength

Returns:
  • Wraps; headless it has no image
Type: 
TextureInfo

(static) parseOBJ(text, smoothopt) → {Mesh}

Parse Wavefront OBJ text into a Mesh

  • Reads v, vt, vn and f lines with convex polygons of any size, materials and groups are ignored
  • Normals come from the file when every corner of a face has one, otherwise from the face
  • Use mesh.center() and mesh.fit(size) to bring a model of unknown units to the origin
  • Back faces are skipped like any mesh, set doubleSided for a model with open walls or single sided parts
Parameters:
NameTypeAttributesDescription
textstring
smoothboolean<optional>

Compute smooth normals for the faces the file gives none, defaults to render3D.smoothShading

Returns:
Type: 
Mesh
Example
new EngineObject3D(vec3(), parseOBJ(objText).center().fit(4));

Type Definitions

Particle3D

A particle as a ParticleEmitter3D's callbacks see it: one object the emitter reuses, set from the particle for each call and written back after it, so copy what you keep

Type:
  • Object
Properties
NameTypeDescription
emitterParticleEmitter3D

The emitter it is in

posVector3

Where it is, change it to move it

velocityVector3

How far it moves each frame

agenumber

Seconds it has lived

lifeTimenumber

Seconds it lives

destroyedboolean

Set by destroy

destroyfunction

End it this update, the destroy callback gets it

Particle3DCallback(particle)

Parameters:
NameTypeDescription
particleParticle3D

Particle3DCollideCallback(particle, level, pos) → {boolean|void}

Parameters:
NameTypeDescription
particleParticle3D
levelEngineObject3D

The HeightMap or VoxelMap it hit

posVector3

Where it hit

Returns:
  • true to stop it there; a callback that returns nothing lets it pass through
Type: 
boolean | void

VoxelHit

What VoxelMap.raycast finds: how far along the ray, the block's cell and type, and the normal of the face it comes in through

Type:
  • Object