Water ... that adapts itself to the flow, that breaks everything like a sword
RTTI: Changes for better
Get link
Facebook
Twitter
Pinterest
Email
Other Apps
-
As some of you may know White Rabbit Engine use it's ownRun Time Type Information system. And well like with everything else sometimes come time when you need to upgrade stuff. Recently I had another occasion to do iteration over this system.
In this post I will try to give you a little bit insight how m RTTI look is engine, what changed and what are plans for future. But well let's start from begin.
Beginning
When I was designing own RTTI one of most important aspect was simplicity of use. When you adding RTTI to class it should be straight forward. Minimal amount of code you need to add and simple syntax. I think that in the end I achieve the goal but I won't say that it was like that right away. But well decide yourselves:
* Header
/*
==========================================================================
Some Rtti class
==========================================================================
*/
class CMyRttiClass : public CRttiObject
{
RTTI_CLASS(CMyRttiClass , CRttiObject);
public:
CMyRttiClass( void );
private:
wrStringID m_name;
CMeshResHandle m_meshResource;
void setResource( int32 a_idx );
};
* Source
// Includes
RTTI_DECLARATION_BEGIN(CMyRttiClass)
RTTI_ATTRIB(m_name)
.setAlias("Name")
.setEditorParams("[Unique]");
RTTI_ATTRIB(m_meshResource)
.setAlias("Mesh Resource")
.bindSetCallback(&CMyRttiClass ::setResource)
.setEditorParams("[Ext:'msh','mshxml']");
RTTI_DECLARATION_END
// Rest of code
...
Or use serialization of attributes that are added in declaration.
In above code all RTT_xyz are macros. They unwrap in meantime of compilation and sadly recently there was problem with parsing them by Visual Assist. It's intellisense stopped working in all my *.cpp with RTTI declarations which made me rally sad :(.
Occasion to changes for better
This was the moment for changes:] For some time I planed to switch part of RTTI on offline generated code. So I took my python project generator and added extra step. It parsing headers file in project where RTTI is enabled and generate from it "rtti_*.cpp" containing RTTI implementation .
* RttiGenerator.py
#!/usr/bin/python
import os
import re
#======================================================================
# This is RTTI code generator. It convert RTTI informations from class
# header to seperate generated *.cpp file.
#
# TEMPLATE FORMAT:
# Template file format is easy it's split on sections:
# [[SECTION_NAME]]
# Where you can select which part export by conditions:
# @if(conditionName)
# @else
# @endif
# And use variables (check : RttiGenerator.processTemplate(...))
#
# REMARKS:
# There is few advantages of this way of resolving RTTI code:
# * There is no issue with Visual Assist parsxing (yep there was issue
# when RTTI used macros)
# * You can step nicely into code with debugger.
# * Saving compilation time because generated code will end in separate
# object.
#======================================================================
class RttiGenerator:
def __init__(self, slnDir, rttiTemplatesDir, pch):
self.gen = None
self.slnDir = slnDir
self.pch = pch
self.templatePath = os.path.join( rttiTemplatesDir ,'template.rtti')
def processTemplate(template):
template = template.replace('{', '{{')
template = template.replace('}', '}}')
# Variables to use in template sctipt
template = template.replace('$(PCH)', '{pch}')
template = template.replace('$(CLASS)', '{classThis}')
template = template.replace('$(CLASS_BASE)', '{classBase}')
template = template.replace('$(FILE)', '{File}')
return template
def loadTemplate(self, path):
templateFile = open(path, 'r')
templateName = None
self.template = dict()
for line in templateFile:
line = RttiGenerator.processTemplate(line)
templateSectionRE = re.search('\[\[(\w+)\]\]', line)
if templateSectionRE != None:
templateName = templateSectionRE.group(1)
content = []
self.template[templateName] = content
elif templateName != None:
if line[0] == '@' or len(content) == 0:
content += [line, '']
else:
content[-1] += line
def getTemplate(self, section, conditions):
output = ''
stack = []
for line in self.template[section]:
conditionIf = re.match('@if\((.*)\)', line)
if conditionIf != None:
stack += [conditions[conditionIf.group(1)]]
continue
conditionElse = re.match('@else', line)
if conditionElse != None:
stack[-1] = not stack[-1]
continue
conditionEndif = re.match('@endif', line)
if conditionEndif != None:
stack = stack[0:len(stack)-1]
continue
if len(stack) == 0 or stack[-1] == True:
output += line
return output
def generate(self, path):
self.gen = None
pathRel = os.path.relpath(path, self.slnDir)
file = open(path, 'r')
conditions = dict()
conditions['Pch'] = self.pch != None
for line in file:
line = line.strip()
isInterface = False
searchRtti = re.match('RTTI\s*\(\s*(\w+)\s*\,\s*(\w+)\s*\)\s*[;]{0,1}', line)
if searchRtti == None:
searchRtti = re.match('RTTI_CLASS\s*\(\s*(\w+)\s*\,\s*(\w+)\s*\)\s*[;]{0,1}', line)
if searchRtti == None:
searchRtti = re.match('RTTI_INTERFACE\s*\(\s*(\w+)\s*\,\s*(\w+)\s*\)\s*[;]{0,1}', line)
isInterface = (searchRtti != None)
conditions['Interface'] = isInterface
if searchRtti != None:
classThis = searchRtti.group(1)
classBase = searchRtti.group(2)
if self.gen == None:
# Lazy loading of template file
self.loadTemplate(self.templatePath)
# Appending of formated header
fileHeader = self.getTemplate('HEADER', conditions)
self.gen = fileHeader.format(File=pathRel,
pch=self.pch,
classThis=classThis,
classBase=classBase)
# Appending of formated class header
classHeader = self.getTemplate('CLASS_HEADER', conditions)
self.gen += classHeader.format(File=pathRel,
pch=self.pch,
classThis=classThis,
classBase=classBase)
return self.gen
Above script is copy-paste from script I use. I think that comments in it tell most of the stuff about usage so I will skip duplicating it. So process works and allow me to do a lot more than before. There is only one issue: right now this step is done in meantime of project generation. So it not update when file change. Because of that I have already plan to include it in building process but well I will do it when I will find some time.
Even more nice changes
Other things I changed is style of casting. You probably don't remember it from above. I also had sometimes problems with remembering it's syntax :D So recently I decided to change it on something more intuitive :
This may not look like a big change but it's making stuff easier. Comfort of code writing is one of things we always need to take into account. If you think twice how to use something then it's mean that maybe something is wrong with it.
Plans for future
Right now except adding RTTI generation as building step I plan to move even more RTTI code from macros to generated *.cpp. There is also incoming revision of code :/ This maybe not be the funniest part but I want to check if all comments in it are still up to date and clarify few stuff there for behavior of system.
On begin I will say that I didn't finished everything I planed for this week. I started work on harbor part of citadela island (you can see how it look bellow) and done some WIP of cave location. From more technical things I worked or still work on: - mechanism of load screens - two types of controlling character (classic and new mode) - pad support to game. And that's all for this week.
This time light topic of UI designing :] But before this let's discuss how the hierarchy in the engine works. So it was easier to understand these, let's look at the tooling of it: We have there: World (ID:0) Loaded Stream (Citadel Island) Loaded SubStream (CI.Citadel) Unloaded SubStream (CI.Logic) Folders (f.ex. Park) Entities (f.ex. Ferry) If you are curious what a Stream is, you can think about it as a scene or a level. Now that we know all of this let's look a little bit closer at what we have. Streams and SubStreams are exactly the same I change the name based on their position in the hierarchy. The Folders may look different than Entities but they are exactly the same. They are Entities with a folder component. If we go this way streams are also just Entities with a Stream Component. This leaves us with a bunch of the entities under the World . Which as you can suspect is just an Entity with ID equal to 0. If everything is an entity, what was the reason I
Yesterday I experienced another bunch of fun while using my laptop. My normal working environment is a desktop with an AMD graphics card, while choosing a laptop it was important for me to get some Nvidia GPU so I could test code on both machines. This was a smart decision as there are so many differences between them. This time I encounter another one. History Like always a little bit of the background: while doing tooling you need to think about nice interactive objects selections. The beginning was rough as I decided to use a physics ray-trace system to execute picking. This was a simple system where I was sanding a ray thru the world and tried to pick objects that collide with it. The issue was: every object needed to have a collision body it did not work very well with objects that had an alpha mask did not support geometry that was deformed in shaders. I did not like any of these limitations so I decided one day to change the whole thing. I took this time a different approach.
Comments
Post a Comment