A game engine with Lua & C++

For a while now I’ve been working on writing a new game engine in C++. It has been a long process, and I’ve restarted it a number of times with each successive time getting closer and closer to a usable engine. Getting caught in a loop of writing and rewriting a project sucks, so I’m forcing myself to stick with the current iteration.

This is the Wake engine. Right now it is at the stage that I can create shaders and draw meshes. The engine itself is largely in C++, with scripting in Lua. Getting Lua scripts to work how I wanted was pretty time consuming: I had to bind a large amount of classes in order to allow the kind of math you need in games to be performed from within the scripts. I managed to bind all the vector and matrix classes from GLM along with the quaternion class.

I’m not going to go too much into the design of the engine just yet, as it is still in flux, but here’s the bit of code that it took to render the above video:

-- First we need to create the shader to display the cube.
-- Wake doesn't currently have any built in shaders, so we need to define our own here.
-- The first argument to Shader.new is the vertex shader. The second is the fragment shader.
local shader = Shader.new(
[[
#version 330 core
layout (location = 0) in vec3 position;

uniform mat4 view;
uniform mat4 projection;
uniform mat4 model;

void main()
{
  gl_Position = projection * view * model * vec4(position, 1.0);
}
]],
[[
#version 330 core
uniform float time;

out vec4 outColor;

void main()
{
  outColor = vec4((sin(time) + 1) / 2, (cos(time) + 1) / 2, ((sin(time) + 1) / 2 + (cos(time) + 1) / 2) / 2, 1.0);
}
]]
)

local shaderTime = shader:getUniform("time")
local shaderView = shader:getUniform("view")
local shaderProj = shader:getUniform("projection")
local shaderModel = shader:getUniform("model")

-- There's no mesh generation functions at the moment, so we have to
-- define the cube manually for now. The first argument is the list of vertices,
-- the second is the list of indices.
local mesh = Mesh.new(
{
  Vertex.new{-1, -1, 1},
  Vertex.new{1, -1, 1},
  Vertex.new{1, 1, 1},
  Vertex.new{-1, 1, 1},
  Vertex.new{-1, -1, -1},
  Vertex.new{1, -1, -1},
  Vertex.new{1, 1, -1}
  Vertex.new{-1, 1, -1}
},
{
  2, 1, 0,
  0, 3, 2,

  6, 5, 1,
  1, 2, 6,

  5, 6, 7,
  7, 4, 5,

  3, 0, 4,
  4, 7, 3,

  1, 5, 4,
  4, 0, 1,

  6, 2, 3,
  3, 7, 6
})

-- White background (r, g, b, a)
engine.setClearColor(1, 1, 1, 1)

local view = math.lookAt({4, 3, 3}, {0, 0, 0}, {0, 1, 0})
local projection = math.perspective(math.radians(45), 800 / 600, 0.1, 1000)

-- Events in wake are bound using the 'bind' function
engine.tick:bind(function(dt)
  -- Use the shader
  shader:use()
  shaderTime:set1f(engine.getTime())
  shaderView:setMatrix4(view)
  shaderProj:setMatrix4(projection)

  local rot = engine.getTime() / 2
  local mat = math.rotate(rot, {1, 0, 0})
  mat = math.translate(mat, {math.sin(engine.getTime() / 2) * 2, 0, 0})
  shaderModel:setMatrix4(mat)

  -- Draw the mesh
  mesh:draw()
end)

The Wake engine is released under the MIT license, and source code can be found here.

Leave a Reply