The LÖVR framework promises lightweight beginner-friendly 3D and VR programming using Lua. Let’s experiment!
I’m always looking for ways to make games and interactive experiences without relying on major pre-packaged game engines. It’s kind of my thing. I’m thrilled that folks who don’t code have popular tools to bring their designs to life, but I personally like the idea of working on skills and knowledge I can apply more universally instead of spending the same amount of time trying to memorize the menu system in this week’s version of Unity.
It’s not elitism. I’ve been at this a long time–since before Unity, the Xbox, and even consumer broadband–and being capable under the hood is how to survive. Now more than ever, admittedly, I’d like to be out from under the influence of major engine companies and their increasingly weird business moves.
When I stumbled into a 3D/VR approximation of the LÖVE framework, I was cautiously optimistic.
LÖVE is a free and open-source framework for 2D games using Lua. When I released a set of educational mini-games I’d developed for my young son, LÖVE made it pretty simple to do. I coded a PC version using Atom, then used their porting solutions to test and deploy in the Google Play and iOS stores. It didn’t exactly take the world by storm, but I learned a lot and had a good time doing it. My only wish was that LÖVE was set up for 3D projects as well. Community members made several attempts, but it seemed like nothing took off.
I’ll admit I never got to know the names or faces behind the LÖVE framework, so it’s not easy for me to tell if there’s any official connection between LÖVE and LÖVR. The branding made the messaging pretty clear: this is 3D and VR development in Lua for LÖVE fans. That’s good enough for me.
In this post, I’ll experiment with LÖVR and see if I can create a simple 3D project that runs on my laptop–I may branch out into VR later. It’s worth noting that lovr.org does warn you that the framework is primarily intended for VR experiences, so things may get a little weird. I’ll be setting up for development on my Asus RoG laptop running Windows 11.
Installation on Windows could not have been any simpler. A small zip archive is available on the Downloads page, and extracting it gives you a lovr executable that shows the “no game” message. Similar to LÖVE, a LÖVR game consists of this executable paired with a project folder of your choosing.
Configuration (for my purposes)
With LÖVR being all about VR by default, you’ll notice stereoscopic rendering right away, even when running on the PC. In addition, LÖVR activates input for VR testing that uses WASD + left mouse to emulate movement, strafing, and free look which actually allows you to walk around this scene freely.
The next step in the Getting Started tutorial was to start your code project and do a little Hello World screen, so I tried disabling VR mode while things were still simple.
Fortunately, LÖVR project configuration works just like configuration in LÖVE. I added the simple Hello World code in a main.lua file in my new project directory as explained in the tutorial–I kept this directory in the same directory as my lovr.exe file for simplicity while testing–and I also added the following in a file called conf.lua:
function lovr.conf(t) t.modules.headset = false end
It was that simple! On the next test, I’d lost the fancy test input controls, but I’d gained simple 3D scene rendering.
Let’s get 3D
LÖVR does provide functions to create simple 3D shapes with nothing but a line of code, but I’m pretty unlikely to use this, so I wanted to skip straight to use of textured models. I like to prototype with models from Kenney.nl–Kenney does a great job and he’s become a bit of a friend around social–so I grabbed the very pleasant Survival Kit and dragged a simple tree into my project in GLB format.
I only needed one line to load it and one to draw it.
function lovr.load() model = lovr.graphics.newModel('tree.glb') end function lovr.draw() model:draw(0,0,-2) end
We have a tree!
I didn’t just lose my input when I disabled headset mode, I also have to take over camera positioning. In fact, I kind of need to “fake” the concept of a camera by manipulating the first of the two view poses and updating it as needed. After some parameter fiddling, I found a setting I liked to establish a default height.
Remember that Lua uses 1-based indexes instead of 0-based like many programming languages, so the parameters here are specifying the view pose to manipulate (the first), followed by x axis 0, y axis 0.5, and z axis 0.
A placeholder sky, a couple more models brought in, and I feel like I’m looking around in my 3D world for the first time!
function lovr.load() modelTree = lovr.graphics.newModel('tree.glb') modelGrassLarge = lovr.graphics.newModel('grassLarge.glb') modelRockFlatGrass = lovr.graphics.newModel('rockFlatGrass.glb') lovr.graphics.setViewPose(1,0,0.5,0) lovr.graphics.setBackgroundColor(0.56, 0.87, 1) end function lovr.draw() modelTree:draw(0,0,-2) modelGrassLarge:draw(0,0,-2) modelRockFlatGrass:draw(0,-1,-2,4,1.6,0,1,0) end
This is a good point to split posts. The most obvious omission at this point is lighting, and lighting in LÖVR requires thinking in shaders right from the start. It’s not devastatingly complicated, but it’s a good task to start with for the next session.
After that, input control (probably using a simple library), interactivity, and more!
Update: The next part of the series exploring lighting with shaders is available now!