Ember Entity Creator
This page contains development documentation on successfully finished GSoC-2008 project Entity Creator. It was coded in a branch in separate git repository. Now, it is fully merged in main repo.
Check the screencast which shows Entity Creator in work.
Process of creating entity:
- Parser is parsing specifications using standard Atlas methods.
- Lua script for each placeholder is called with values from GUI adapters. Result is transformed into XML structure and replace placeholder.
- Data is transferred to server to create entity.
Contents
Entity recipe
All information that entity creator uses to create entity is gathered together in one entity recipe file (*.entityrecipe). It has following structure:
<entityrecipes> <entityrecipe name="test"> <author>... Author name ...</author> <description> ... Brief recipe description ... </description> <entity> ... Entity Specification in semi Atlas-XML format ... </entity> <adapters> ... Description of GUI adapters ... </adapters> <bindings> ... Bindings between scripts and GUI adapters ... </bindings> <script><![CDATA[ ... Lua script ... ]]></script> </entityrecipe> </entityrecipes>
Semi Atlas-XML entity specification
It is the semi atlas-xml structure that have placeholders like $val
. DTD is described in documentation. Also, Atlas has additional restrictions on elements content. That is, for example, in <int>
element it is possible to store only integer values and so on. In our case, we want to store placeholders in form $val
so it cannot be valid Atlas structure and because of this Atlas-XML parser cannot be used.
Attribute type
specifies Eris type of entity.
Placeholder should be the only child in element. That is, it should be like <float name="foo">$val</float>
.
This element will be replaced with resulting value from GUI adapter. Attribute name
is maps will be preserved.
<entity type="foobar"> <atlas> <map> <int name="foo">13</int> <float name="meep">1.5</float> <string name="bar">$val</string> <list name="args"> <int>1</int> <int>2</int> <float>3.0</float> </list> </map> </atlas> </entity>
GUI adapters description
This part of recipe describe properties of GUI adapters. Attribute type
contains type ID of an adapter. Following types are supported:
- string — just a string, allows list of suggestion within child
<item>
elements but value is not limited to them, - number,
- size,
- position — position in 3D space,
- position2d — position in 2D space.
<adapters> <adapter name="value_of_a" type="string"> <item value="1">A is big</item> <item value="2">A is small</item> </adapter> <adapter name="value_of_b" type="number" /> <adapter name="value_of_c" type="position3d" /> </adapters>
GUI adapters bindings
This part of recipe describe bindings of GUI adapters to correspondent placeholders. Attribute func
contains a name of Lua function that should be called. If this attribute is missing than it will bind an adapter directly to the placeholder, i. e. it will copy adapter value without passing it through function.
<bindings> <bind name="foo"> <adapter name="value_of_a" /> </bind> <bind name="meep" func="calc_meep"> <adapter name="value_of_b" /> <adapter name="value_of_c" /> </bind> <bind name="bar" func="calc_bar"> <adapter name="value_of_a" /> <adapter name="value_of_c" /> </bind> </bindings>
Lua scripts
(this info should be added to pages describing Lua bindings in Ember when those would be written)
Reading params
Lua functions used in entity recipes receive bound adapters values as Atlas.Message.Element objects in order they are listed in bindings section of recipe. Current Atlas bindings support following types of Elements:
- string. Standard functions
param:isString()
andparam:asString()
. - float and integer. Lua doesn't distinct between floats and integers, so, for better interoperability better to use general “number” functions
param:isNum()
andparam:asNum()
. - list. This is the complex type. Standard functions
param:isList()
andparam:asList()
is available to check is it a list and convert it to Atlas.Message.ListType. To read it, Lua-style iterator over Atlas.Message.ListType is implemented:
for k,v in param:asList():pairs() do -- k is integer index -- v is Atlas.Message.Element on that index end
- map operations is not implemented yet. Though it should be almost the same as
EmberOgre.GUIAdaptersStore
.
Returning results
Lua functions used in entity recipes should return objects of type Atlas.Message.Element. Current Atlas bindings support creation of following types of Elements:
- string. It is created simply:
Atlas.Message.Element("abc")
. - float. Also simple:
Atlas.Message.Element(123)
. Note that Lua doesn’t distinct between integers and floars, so even if passed number is integer resulting element type would be float. - list. This is the complex type. Script need to create Atlas.Message.ListType first and fill it with
push_back
operation that allows to add elements into list. It allows to addAtlas.Message.Elements
to the end of list. Also, it is overloaded to instant addition of strings or floats without creating intermediate object:
local list = Atlas.Message.ListType() list:push_back(Atlas.Message.Element(123)) list:push_back("abc") list:push_back(123)
When script thinks that its list is fully formed, it needs to create Atlas.Message.Element from it and return it:
return Atlas.Message.Element(list)
- map operations is not implemented yet. Though it should be almost the same as
EmberOgre.GUIAdaptersStore
.
Additional library
Along with all standard Lua library, following functions are available in entity recipe scripts:
-
math.randelem
— returns random element from table -
math.randn(mean, stddev)
— returns normally distributed random number with correspondent mean and std.dev.
Example
Complete example of real entity recipe file.
<entityrecipes> <entityrecipe name="boulder"> <author>Alexey Torkhov</author> <description> This is boulder recipe </description> <entity type="boulder"> <atlas> <map> <list name="bbox">$bbox</list> <float name="mass">$mass</float> <string name="style">$style</string> </map> </atlas> </entity> <adapters> <adapter name="mass" type="number" title="Mass"/> <adapter name="massdev" type="number" title="Mass std. dev."/> <adapter name="style" type="string" title="Style" allowrandom="yes"> <item value="a">Boulder A</item> <item value="b">Boulder B</item> <item value="c">Boulder C</item> <item value="d">Boulder D</item> </adapter> </adapters> <bindings> <bind name="bbox" func="boulder.calcSize"> <adapter name="mass" /> <adapter name="massdev" /> </bind> <bind name="mass"> <adapter name="mass" /> </bind> <bind name="style"> <adapter name="style" /> </bind> </bindings> <script><![CDATA[ boulder = {} function boulder.calcSize(a, b) local density = 2500 * 0.52 -- our boulder is not cubic, 0.52 is rough volume of its size 1 local massmean = a:asNum() local massdev = b:asNum() local mass = math.abs(math.randn(massmean, massdev)) local volume = mass / density local len = volume^(1/3) local bbox = Atlas.Message.ListType() bbox:push_back(-len/2) bbox:push_back(-len/2) bbox:push_back(0) bbox:push_back(len/2) bbox:push_back(len/2) bbox:push_back(len) return Atlas.Message.Element(bbox) end ]]></script> </entityrecipe> </entityrecipes>