Maplet

Builds interiors from carved and filled shapes, with Maplet+'s geometry: rooms, doorways, stairs and any convex shape, checked and saved as a model.

Commands

Import blitz.maplet As Maplet gives your program these, with Maplet. in front. Each name leads to its part of the reference.

Functions

Types

Guide

Maplet builds the insides of places: halls, cellars, castles, dungeons, caves. Start from solid rock and carve out the space you want, then fill back in what should be solid, such as a pillar, a gallery or a flight of stairs. Your program says what to carve and fill, and Maplet turns it into a model with the walls, floors and ceilings in place, ready to load and walk round.

The geometry comes from Maplet+, a remake of Mark Sibly's Maplet, the interior modeller that came with Blitz3D's early days. What you build here also opens in Maplet+ Extended, if you want to shape it further by hand.

A stone hall with a wooden stair climbing to a gallery

Try it

Open hall.bb and run it. It builds the hall in the picture, with an arched doorway into a side room, then lets you walk round inside.

; Builds a stone hall with a wooden gallery up a stair and an arched doorway
; into a side room, then lets you walk round it. Mouse looks, WASD walks,
; Escape quits.
Dialect "modern"
Import blitz.maplet As Maplet

; Carve the rooms out of solid rock. Later shapes win.
Local hall:Maplet.Interior = Maplet.CreateInterior("Hall")
hall.CarveBox("Hall", -6, 0, -8, 6, 7, 8)
hall.CarveBox("Side room", 6.6, 0, -3, 12, 3.5, 3)
hall.ArchedDoorway("Arch", 6.3, 0, 0, 2, 3, 1, 90)

; Fill a wooden gallery back in at the far end, with a stair up to it.
hall.SetTexture("wood")
hall.FillBox("Gallery", -6, 3.4, 4, 6, 3.6, 8)
hall.Stair("Stair", -4.5, 0, -1, 2, 3.6, 5, 12)

hall.AddLight("Lamp", 0, 5, 0, 255, 210, 160, 16)
hall.MarkStart(0, 0, -6)
; Build refuses to write the model if these fail.
hall.MustBeClear("Through the arch", 0, 1, 0, 10, 1, 0)
hall.MustStand("Top of the stair", -4.5, 3.6, 4.5)
If hall.Build("hall") = False Then RuntimeError hall.Error()
Delete hall

; Walk round what was built.
Graphics3D 960, 600, 0, 2
SetBuffer BackBuffer()
AmbientLight 80, 76, 72
Local level:Int = LoadMesh("hall/interior.glb")
EntityType level, 2
Local lamp:Int = CreateLight(2)
PositionEntity lamp, 0, 5, 0
LightColor lamp, 255, 210, 160
LightRange lamp, 16

Local player:Int = CreatePivot()
PositionEntity player, 0, 1, -6
EntityRadius player, 0.4, 0.9
EntityType player, 1
Collisions 1, 2, 2, 3
Local camera:Int = CreateCamera(player)
PositionEntity camera, 0, 0.7, 0
CameraRange camera, 0.05, 100

Local pitch:Float = 0
HidePointer
MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2
While Not KeyDown(1)
    TurnEntity player, 0, -MouseXSpeed() * 0.2, 0
    pitch = pitch + MouseYSpeed() * 0.2
    If pitch > 80 Then pitch = 80
    If pitch < -80 Then pitch = -80
    RotateEntity camera, pitch, 0, 0
    MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2
    MoveEntity player, (KeyDown(32) - KeyDown(30)) * 0.08, 0, (KeyDown(17) - KeyDown(31)) * 0.1
    TranslateEntity player, 0, -0.1, 0 ; Gravity: the floor stops the fall.
    UpdateWorld
    RenderWorld
    Text 10, 10, "Mouse looks, WASD walks, Escape quits."
    Flip
Wend
End

The hall is carved first, so the gallery and the stair, filled afterwards, are solid inside it. Each shape has a name. Build writes the model into the hall folder beside the program, and the rest of the program loads it like any other model.

How do I...

Join two rooms?

Leave a wall between them by carving them apart, then carve a doorway through it. A doorway stands on the floor in the middle of the wall, and faces along a yaw, as entities do: 0 faces along z, 90 along x. Make it a little deeper than the wall is thick.

house.CarveBox("Kitchen", 0, 0, 0, 6, 3, 5)
house.CarveBox("Hall", 6.2, 0, 1, 12, 3, 4)
house.Doorway("Kitchen door", 6.1, 0, 2.5, 1, 2.2, 0.4, 90)

Carving two rooms that touch makes one bigger room, with no wall at all.

Build stairs?

Carve the space the stair climbs through, then fill the steps in with Stair. For a tower, carve a cylinder and fill a SpiralStair round a post:

castle.Carve("cylinder", "Tower", 19, 0, 19, 10, 24, 10)
castle.Fill("cylinder", "Post", 19, 0, 19, 1.2, 24, 1.2)
castle.SpiralStair("Tower stair", 19, 0, 19, 0.6, 4.9, 23.5, 47)

Give a floor its own texture?

Each face takes the texture of the shape that made it, so a room's floor is the room's texture. Carve the room a little lower, then fill the floor back with the texture it should have:

hall.CarveBox("Hall", -6, -0.2, -8, 6, 7, 8)
hall.SetTexture("flagstone")
hall.FillBox("Floor", -6, -0.2, -8, 6, 0, 8)

Filling a slab below the room changes nothing, as the rock there is solid already.

Make shapes that aren't boxes?

Carve and Fill take a shape, a size and a turn: ramps, roof wedges, pyramids, cylinders and cones, at any angle. CarveSector makes curved corridors and round rooms. For anything else convex, add its corners with AddHullPoint and carve or fill the hull round them. Make other shapes from several pieces.

Know that the player can get round?

Add checks. MustBeClear checks that a straight line is open all the way, such as a corridor. MustStand checks that someone can stand at a place, with a floor below and head room above: check a stair a step at a time. If any check fails, Build returns False and leaves the last good model alone, and FailedCheck says what was in the way and where.

See what I've built?

SavePlan saves a picture looking down on the rooms below a height, with the rooms and any failed checks labelled. SaveView saves a picture from a camera you place. Both work straight after Build, even without Graphics3D.

Change it by hand?

Build also writes interior.mapletplus, which opens in Maplet+ Extended. To go on from a project, open it with LoadInterior and add to it.

Blip's tip

Name every shape. The names say what failed when a check fails, they label the history in Maplet+, and they keep a shape's ID the same each time you build, so a change to one shape changes only that shape.

Good to know

More examples

Reference

Needs
blitz.maplet.core.geometry, blitz.maplet.core.model, blitz.maplet.core.export, blitz.maplet.core.extended, blitz.maplet.core.persistence
Examples
castle.bb

0.1.0 unpublished preview. Builds the inside of a building, a dungeon or a cave from shapes: carve out space, fill solid back in, and get a model, with checks that rooms join up and stairs can be climbed. The geometry is Maplet+'s, the remake of Mark Sibly's Maplet. Extended only; requires object-lifecycle-v1.

Blitz3D+ comes with this module, so any Extended program can Import blitz.maplet. Open castle.bb and run it: it builds a castle keep with a round tower off each corner and a spiral stair in each tower, then lets you walk round it.

Dialect "modern"
Import blitz.maplet As Maplet

Local house:Maplet.Interior = Maplet.CreateInterior("House")
house.CarveBox("Kitchen", 0, 0, 0, 6, 3, 5)
house.CarveBox("Hall", 6.2, 0, 1, 12, 3, 4)
house.Doorway("Kitchen door", 6.1, 0, 2.5, 1, 2.2, 0.4, 90)
house.MustBeClear("Kitchen to hall", 3, 1, 2.5, 9, 1, 2.5)
house.AddLight("Lamp", 3, 2.6, 2.5)
If house.Build("house") Then
    Print "Built house/interior.glb"
Else
    Print house.Error()
EndIf
Delete house
End

How it works

An interior starts solid everywhere. Carve cuts space out of it and Fill puts solid back, in the order you give them, so a later shape wins: carve a room, then fill a pillar inside it. The walls, floors and ceilings are the faces between space and solid, and they face into the space, so the model is meant to be seen from inside.

Positions are Blitz3D+ world units, which the World Editor treats as metres, with y up. A shape stands on the point you give: that point is the centre of its base. Turns are degrees, as RotateEntity takes them: roll first, then pitch, then yaw; a yaw of 90 turns the shape's front from +z to -x.

Every shape has a name. Names identify the shapes in the saved project, so give each shape its own name; a repeated name is told apart by when it comes. Building the same shapes again gives the same files, IDs included.

Making an interior

DeclarationWhat it does
CreateInterior(name:Str = "Interior"):InteriorA new interior, solid everywhere. Yours: Delete it when done.
LoadInterior(file:Str, name:Str = ""):InteriorOpens a Maplet+ Extended project (.mapletplus), such as an interior.mapletplus that Build wrote or one shaped by hand in Maplet+, to build on. Its textures are found beside it, or where a full path names them, as Maplet+ names a texture picked in it. Returns Null when it cannot be read.
LoadError():StrWhy the last LoadInterior returned Null.

Shapes

MethodWhat it does
Interior.CarveBox(name:Str, x0:Float, y0:Float, z0:Float, x1:Float, y1:Float, z1:Float)Carves the box between two opposite corners. The quickest way to make a room.
Interior.FillBox(name:Str, x0:Float, y0:Float, z0:Float, x1:Float, y1:Float, z1:Float)Fills the box between two corners with solid: a floor, a wall, a block.
Interior.Carve(shape:Str, name:Str, x:Float, y:Float, z:Float, width:Float, height:Float, depth:Float, yaw:Float = 0, pitch:Float = 0, roll:Float = 0)Carves a shape standing on x,y,z, width across x, height up y and depth along z before it is turned. shape is "box", "ramp" (rising from nothing at its front to its full height at its back, +z), "wedge" (a roof, its ridge along z), "pyramid", "cylinder" or "cone". A pitch of 90 lays a cylinder along +z.
Interior.Fill(shape:Str, name:Str, x:Float, y:Float, z:Float, width:Float, height:Float, depth:Float, yaw:Float = 0, pitch:Float = 0, roll:Float = 0)Fills a shape with solid, placed as Carve places it.
Interior.CarveSector(name:Str, x:Float, y:Float, z:Float, innerRadius:Float, outerRadius:Float, startAngle:Float, sweep:Float, height:Float)Carves part of a ring round x,z between two radii, standing on y: from startAngle through sweep degrees. Angles go as yaw does: 0 points along +z and 90 along -x. An inner radius of 0 gives a slice of a cylinder: a curved corridor or a round room's alcove.
Interior.FillSector(name:Str, x:Float, y:Float, z:Float, innerRadius:Float, outerRadius:Float, startAngle:Float, sweep:Float, height:Float)Fills part of a ring: a curved wall, a step of a spiral stair.
Interior.AddHullPoint(x:Float, y:Float, z:Float)Adds a corner for the next hull.
Interior.CarveHull(name:Str)Carves the smallest convex shape round the points added since the last hull: up to 64, and not all flat. Any convex shape at all.
Interior.FillHull(name:Str)Fills the smallest convex shape round the points.

Settings for the shapes that follow:

MethodWhat it does
Interior.SetTexture(texture:Str)A picture file, relative to the program's folder, or a built-in look: "stone" (the first), "brick", "wood", "plaster", "flagstone" or "rock". A texture repeats every world unit. The built-in looks are drawn into the output folder as PNGs, so no picture files are needed. A picture other than a PNG or JPEG goes into the model as a PNG.
Interior.SetSides(sides:Int)How many flat sides make a cylinder, cone or curve: 3 to 64, 16 at first.
Interior.SetGroup(group:Str)The group that following shapes belong to in Maplet+'s History; "" for none.

Ready-made pieces

These add ordinary shapes, grouped under the piece's name. Copy and change them freely: they are a few lines each in the module's interior.bb.

MethodWhat it does
Interior.Doorway(name:Str, x:Float, y:Float, z:Float, width:Float, height:Float, depth:Float, yaw:Float = 0)Carves an opening standing on x,y,z, depth through, facing along yaw. Make depth a little more than the wall is thick.
Interior.ArchedDoorway(name:Str, x:Float, y:Float, z:Float, width:Float, height:Float, depth:Float, yaw:Float = 0)A doorway with a round top, height high at the middle.
Interior.Stair(name:Str, x:Float, y:Float, z:Float, width:Float, rise:Float, run:Float, steps:Int, yaw:Float = 0)Fills solid steps going up from x,y,z, the middle of the bottom step's front edge, rising rise over run along yaw. Carve the stairwell first.
Interior.SpiralStair(name:Str, x:Float, y:Float, z:Float, innerRadius:Float, outerRadius:Float, rise:Float, steps:Int, startAngle:Float = 0, turn:Float = 22.5, thickness:Float = 0.25)Fills a spiral of treads round x,z, each thickness thick, turning turn degrees and rising rise/steps from the one before. Carve the tower first.

Marks

Marks don't change the shape. They tell the World Editor and your game what is where, in interior.json.

MethodWhat it does
Interior.MarkRoom(name:Str, x0:Float, y0:Float, z0:Float, x1:Float, y1:Float, z1:Float)Names a room by its bounds. The World Editor makes each room a zone.
Interior.MarkStart(x:Float, y:Float, z:Float, yaw:Float = 0)Where the player starts: feet at x,y,z.
Interior.MarkPoint(name:Str, x:Float, y:Float, z:Float)A named place, such as where a chest or an enemy goes.
Interior.AddLight(name:Str, x:Float, y:Float, z:Float, red:Float = 255, green:Float = 255, blue:Float = 255, range:Float = 10)A point light, saved with the interior.

Checks

Build runs the checks on the finished shape. A failed check stops Build writing the model, so a broken interior never replaces a good one.

MethodWhat it checks
Interior.MustBeSpace(name:Str, x:Float, y:Float, z:Float)The point is open space.
Interior.MustBeSolid(name:Str, x:Float, y:Float, z:Float)The point is solid.
Interior.MustBeClear(name:Str, x0:Float, y0:Float, z0:Float, x1:Float, y1:Float, z1:Float)The straight line between the points is open all the way: a corridor, a doorway, a route.
Interior.MustStand(name:Str, x:Float, y:Float, z:Float, height:Float = 1.8)Someone can stand at x,y,z: a floor just below and height of space above. Check stairs with it, a step at a time.

Building

MethodWhat it does
Interior.Build:Int(folder:Str)Builds the shape, runs the checks and writes build.json in the folder (relative to the program's folder). When everything passes it also writes interior.glb, with its textures in it, interior.mapletplus, interior.json and textures, and returns True; an interior.b3d that an earlier version wrote there goes. When the World Editor builds the interior, it names the folder in B3D_MAPLET_OUTPUT, and Build writes there instead.
Interior.Error:Str()Why the last Build failed, or "".
Interior.FailedChecks:Int()How many checks failed.
Interior.FailedCheck:Str(index:Int)A failed check, 0 to FailedChecks()-1, with what was found: "Tower door: expected space at (15.5, 1.5, 15.5), found solid".
Interior.ShapeCount:Int()How many shapes have been carved or filled.
Interior.FaceCount:Int()How many faces the last Build made.

interior.glb loads with LoadMesh, with its textures in it, as matt materials: lit as any glTF model is, so give it lights, not only AmbientLight. Collide with it as you would with any level: EntityType and polygon Collisions. The World Editor can take its materials out of it to change them. textures holds the same pictures, for the project. interior.mapletplus opens in Maplet+ Extended. build.json holds the outcome, every check and any problems, for tools; interior.json holds the rooms, start, points and lights.

Building takes from a moment for a few rooms to some seconds for a few hundred shapes, such as the castle's spiral stairs. Build keeps the geometry after every 16 shapes in .build.cache, so building again after a change starts from the last of those before the first shape that changed: the castle builds in 20 seconds the first time, and in 3 when nothing or only its last shapes changed. build.json's reused says how many shapes it skipped. Delete .build.cache whenever you like; the next build makes it again. Its name starts with a dot, so games packaged for the web or the Mac leave it out.

Pictures

Pictures of the last build, which work even when checks failed, so you can see what went wrong. They are drawn in a hidden window and a world of their own, with or without Graphics3D, and never touch your scene.

MethodWhat it does
Interior.SavePlan:Int(file:Str, y:Float, size:Int = 1024)A floor plan, looking straight down from height y: everything below it, with the rooms, the start and any failed checks labelled. size is its width in pixels. A relative file goes in the last build's folder.
Interior.SaveView:Int(file:Str, x:Float, y:Float, z:Float, targetX:Float, targetY:Float, targetZ:Float, width:Int = 960, height:Int = 600, fov:Float = 75)A view from x,y,z looking at the target, lit by the interior's lights. A relative file goes in the last build's folder.

Limits

Shapes are convex pieces; make other shapes from several. A hull takes up to 64 points. Maplet+ maps each texture flat onto each face, repeating every world unit. The model has no lightmap: light it in the World Editor with Bake lighting, or with lights in your game.