» News | » My 3d Dev | » Papers | » Downloads | » Contact

News

9. Aug 2011 - LUA support successful implemented

It took some days, but now the engine support scripting using the Lua API and I'm very happy with it. My first plan was to use Python (my blender exporter is written in phyton too, what else ...). But this way I should run into problems since I want to use the scripting API also controling my GUI system inside an xml file. But Python use indention to mark code blocks ... In short: Python + inline xml tags = horribly combination (imho). So I decided to use LUA. "Why Lua?" you may ask. Lua.org say it best: "Lua is a powerful, fast, lightweight, embeddable scripting language." and this is definitely true. It is industry proven, comes with easy (but powerfull) syntax and contain only a few kb of code. Now I want to share a few decisions I made:

1st) I use only one virtual machine.

Why? This way I avoid the overhead of multiple Lua instances (multiple Lua_State). But more important: every part of the engine can communicate with/control each other part. A GUI control for example can zoom the camera, the scene time can control visual post-processing effects and the audio system have access to the 3d-vector class. To avoid polluting the global space each function are packed into tables like:
app.pause()
gui.processXML("myXML")
scene.findChild("earth")
visual.postProcess.enable("radial blur")

2nd) Use less but powerfull C bindings.

How? Lua communicate with the host application via stack. If you wish to pass parameters to C(++) and multiple returns back to Lua you always manipulate the stack. Having a variable amount of parameters can help to write one C(++) function for multiple purposes:
-- open the control
-- ctrl:open(false) close the control
function gui.control:open(value)
    gui.control._open(self._c_ptr, value ~= false and true or false)
    return self
end
-- close the control
function gui.control:close()
    gui.control._open(self._c_ptr, false)
    return self
end
-- returns the controls open state
function gui.control:opened()
    return gui.control._open(self._c_ptr)
end
This way the three lua functions called "open", "close" and "opened" refer to the same C function "_open" where the behavior is only controled by the parameters amount and type.

3rd) Some syntax sugar.

Which? Each GUI control contain a list of callback functions which describe there behavior an standard events like "onClick", "onChange" or "onClose". Let's say you want to count how often a button is clicked and show the result as the button text. If you have a register function (let's call it _registerEvent) than the code will something like this:
local btn = gui.newControl("button")
gui.control._registerEvent(btn._c_ptr, "onClick", function(self)
        self.userdata.countClicks = (self.userdata.countClicks or 0) + 1
       
self:text("You klicked me " .. self.userdata.countClicks .. " times.")
    end)
A more intuitive way each javascript programmer know is the following:
local btn = gui.newControl("button")
btn.onClick = function(self)
        self.userdata.countClicks = (self.userdata.countClicks or 0) + 1
       
self:text("You klicked me " .. self.userdata.countClicks .. " times.")
    end
You can achieve this by overwrite the metamethod "__newindex" this way:
gui.control.__newindex = function(ctrl, key, value)
    if key == "onClick" or key == "onInit" or key == "onChange" or key == "onClose" then
       
ctrl.key = value
        gui.control._registerEvent(ctrl._c_ptr, key, value);
    else
       
rawset(ctrl, key, value)
    end
end


14. Sep 2010 - God Rays

After reading Kenny Mitchell's "Volumetric Light Scattering as a Post-Process" paper (published in GPU Gems 3) I decided to give it a try. The result can be seen on the image to the left (zoom). In a nutshell, it's a post process image effect to simulate light scattering with some kind of radial blur from the light source center position. At this point I don't want to explain the technic in detail. Instead I want to share the modifications I made.

1st) Mitchell suggest to render the occluded geometry twice (one pass to create a light mask and a second pass with regular shading). Since I've access to the pixels depth informations (I'm using some kind of deferred rendering) it's easy to determine which pixel are in front of the light source and should be used as an occluder.

2nd) The second difference I made is at the final blending stage. Additative blending made my sky 'to bright'. In this case I'm more a fan of "screen blending":

      formula: final = 1.0 - ((1.0 - shafts) * (1.0 - scene));

3rd) The post-process approach requires the light source within the viewport. To simulate a smooth transition I use the angle between the view and the light vector as a strength factor squared and multiplied with the exposure parameter.

      formula: factor = pow(saturate(dot(view, light)), 2);

This factor should be premultiplied at the application level. If it's near zero, the complete effect can be skipped. This factor can also be based on the screen light distance to the viewport.

To see a sample how it looks at sunset click here and an image with higher exposer here.