-- Point-light shadows inside geometry: two bulbs in a closed room built from ordinary boxes (their -- faces point outward, so from inside only back faces show), with pillars. One bulb hangs under a -- cone shade on a stand; the other sits inside a cage of bars and rings. Inside, the walls are lit -- with no seams, the shade throws its shadow on the ceiling and the cage its stripes across the -- corners; outside, the room stays dark. local font = fontLoad("Singe/FreeSansBold.ttf", 28) local frames = 0 fontSelect(font) discPlay() sceneEnable(true) sceneSetBackground(0, 0, 0, 0) sceneSetAmbient(8, 8, 12) local plaster = materialNew() materialSetColor(plaster, 200, 190, 170) materialSetRoughness(plaster, 0.95) materialSetDoubleSided(plaster, true) local brass = materialNew() materialSetColor(brass, 180, 140, 70) materialSetMetallic(brass, 0.7) materialSetRoughness(brass, 0.4) materialSetDoubleSided(brass, true) local glow = materialNew() materialSetColor(glow, 255, 240, 200) materialSetEmissive(glow, 255, 230, 170) materialSetUnlit(glow, true) -- The room: six slabs 8 x 4 x 8 inside, all outward facing like any box. local function slab(x, y, z, w, h, d) local node = nodeNew() nodeSetMesh(node, meshBox(w, h, d), plaster) nodeSetPosition(node, x, y, z) return node end slab(0, -0.1, 0, 8.4, 0.2, 8.4) slab(0, 4.1, 0, 8.4, 0.2, 8.4) slab(-4.1, 2, 0, 0.2, 4.4, 8.4) slab(4.1, 2, 0, 0.2, 4.4, 8.4) slab(0, 2, -4.1, 8.4, 4.4, 0.2) slab(0, 2, 4.1, 8.4, 4.4, 0.2) -- Pillars and a crate. for i, p in ipairs({ { -2.5, -2.5 }, { 2.5, -2.5 }, { -2.5, 2.5 }, { 2.5, 2.5 } }) do local pillar = nodeNew() nodeSetMesh(pillar, meshCylinder(0.25, 4, 16), plaster) nodeSetPosition(pillar, p[1], 2, p[2]) end local crate = nodeNew() nodeSetMesh(crate, meshBox(0.8, 0.8, 0.8), brass) nodeSetPosition(crate, 2.2, 0.4, -1.8) -- The lamp: a stand, a cone shade above the bulb, which hangs just below its rim so the shade -- throws a ring of shadow on the ceiling and the bulb lights everything below. local stand = nodeNew() nodeSetMesh(stand, meshCylinder(0.04, 1.4, 8), brass) nodeSetPosition(stand, -0.5, 0.7, -0.3) local shade = nodeNew() nodeSetMesh(shade, meshCone(0.45, 0.5, 24), brass) nodeSetPosition(shade, -0.5, 2.05, -0.3) local bulbMesh = nodeNew() nodeSetMesh(bulbMesh, meshSphere(0.08, 12), glow) nodeSetPosition(bulbMesh, -0.5, 1.55, -0.3) nodeSetShadow(bulbMesh, false) -- The bulb's own glass casts nothing. local bulb = lightNew(LIGHT_POINT) nodeSetPosition(bulb, -0.5, 1.55, -0.3) lightSetIntensity(bulb, 6) lightSetShadow(bulb, true) -- The second lamp: a bulb inside a cage of eight bars and three rings of short segments, its own -- glass excused from casting. The bars are the harsh case: thin casters right beside the light. local cx, cy, cz = 2.2, 2.1, 1.6 local function bar(x, y, z, rx, ry, rz, radius, h) local node = nodeNew() nodeSetMesh(node, meshCylinder(radius, h, 8), brass) nodeSetPosition(node, x, y, z) nodeSetRotation(node, rx, ry, rz) return node end for i = 0, 7 do local a = i / 8 * 2 * math.pi bar(cx + 0.35 * math.cos(a), cy, cz + 0.35 * math.sin(a), 0, 0, 0, 0.02, 1.0) end for _, dy in ipairs({ -0.5, 0, 0.5 }) do for i = 0, 7 do local a = (i + 0.5) / 8 * 2 * math.pi bar(cx + 0.35 * math.cos(a), cy + dy, cz + 0.35 * math.sin(a), 0, -math.deg(a), 90, 0.015, 0.28) end end local cagedMesh = nodeNew() nodeSetMesh(cagedMesh, meshSphere(0.06, 12), glow) nodeSetPosition(cagedMesh, cx, cy, cz) nodeSetShadow(cagedMesh, false) local caged = lightNew(LIGHT_POINT) nodeSetPosition(caged, cx, cy, cz) lightSetIntensity(caged, 4) lightSetColor(caged, 255, 230, 190) lightSetShadow(caged, true) local camera = nodeNew() cameraSet(camera) cameraSetPerspective(70, 0.05, 60) function onOverlayUpdate() frames = frames + 1 overlayClear() if frames < 50 then -- Inside: from a corner, looking across the room at the shaded lamp, pillars and the far wall. nodeSetPosition(camera, 3.2 - frames * 0.006, 1.6, -3.2) nodeLookAt(camera, -1.5, 1.4, 1.5) elseif frames < 95 then -- Inside, looking up at the ceiling above the shade. nodeSetPosition(camera, 1.5, 0.8, -1.5) nodeLookAt(camera, -0.5, 3.8, -0.3) elseif frames < 140 then -- Under the cage, looking up at its stripes running over the ceiling corner. nodeSetPosition(camera, 1.6, 0.5, 1.0) nodeLookAt(camera, 3.6, 3.8, 3.6) else -- Outside: the closed room from above; nothing of either bulb may leak out. nodeSetPosition(camera, 9, 7, 9) nodeLookAt(camera, 0, 1.5, 0) end fontPrint(20, 20, "Bulbs in a room, frame " .. frames) if frames == 40 or frames == 85 or frames == 130 or frames == 175 then singeScreenshot() end if frames == 180 then singeQuit() end end