Feature Request: Class identifiers for orxObjects

Hi,

I've started using scroll, and I find it very useful. One extra feature that I'd really make use of, is somehow enabling object class hierarchies in config files. For instance:

[ObjectBase]
; various object fields...
ClassIdentifier = MyObjectType

[DerivedObject1@ObjectBase]
; various other object fields...

[DerivedObject2@ObjectBase]
; even more different object fields...

With this config file, if I now register the identifier "MyObjectType" to one of my ScrollObject classes, all derived objects will also be created as one.

Motivation: The reason why I'm asking for this, is that I can then start introducing custom fields for my base classes, that get handled correctly by the corresponding C++ type.

Implementation: I think ScrollObjectBinder<ScrollObject>, the binder that's used when the object type doesn't match any registered class could be made to create an object as usual, and check for the existence of the ClassIdentifier field. If it exists and the value matches a registered class, it could use the
template<class O>
ScrollObject *ScrollObjectBinder<O>::CreateObject(orxOBJECT *_pstOrxObject, const orxSTRING _zInstanceName, ScrollObject::Flag _xFlags)
wrapper method of the corresponding type.

Thanks!

Comments

  • edited July 2012
    I'd like to support binding inheritance to a certain extent however I'm unclear how I'd do it with your current proposal.

    The issue being that the binding actually takes place at compile time and the config info (with the ClassIdentifier property) is only available at runtime.

    Lacking reflection mechanism I wouldn't be able to find the correct C++ class from the string anyway.

    What I can support though, is that if you bind a C++ class to a config section, any section inheriting from that config section will also default to that binding unless it has its own binding.
  • edited July 2012
    In my proposal, I suggest using a registered class, just like the binding works currently. I don't see why the ClassIdentifier field is any more runtime than the names of objects themselves. I think I caused a confusion my calling it "ClassIdentifier", you still need to register "MyObjectType" to a C++ type as usual. The way you proposed it would also work for me, but I feel like the ClassIdentifier field is more flexible in general. With that in place, you can attach an object type to a c++ type without having to inherit possibly irrelevant stuff.
  • edited July 2012
    Mmh then it's probably me lacking of sleep! :)

    My understanding was: the ClassIdentifier is a config property, from the syntax you used:
    [ObjectBase]
    ; various object fields...
    ClassIdentifier = MyObjectType

    Is that correct or am I already wrong?

    Assuming that's the case, that means I can't get the value of that property at compile time, only at runtime.

    However the template parameter of ScrollBindObject<Class>("ConfigSection") is resolved at compile time and I wouldn't be able to extract it from the ClassIdentifier property.

    But after reading your reply, it doesn't look like it was what you were proposing so I'm not sure anymore what the ClassIdentifier actually is if you still do the binding explicitly in code.

    I'll reread that tonight after getting a nap, I think. :)
  • edited July 2012
    It's my fault really, I'll compile a much more clear proposal shortly. Have a nice nap :)
  • edited July 2012
    Unfortunately that's not going to happen for quite some time as I still have a full day of work ahead, but thanks anyway. ;)
  • edited July 2012
    Hi Again,

    So, I've decided to describe what I want by actually doing it myself (poorly). I've basically injected the following code segment :
        if(poBinder == ScrollObjectBinderBase::GetBinder("none")) {
            orxConfig_PushSection(_zModelName); {
                poBinder = ScrollObjectBinderBase::GetBinder(orxFLAG_TEST(_xFlags, ScrollObject::FlagSave | ScrollObject::FlagRunTime) ? orxConfig_GetString("ClassIdentifier") : orxSTRING_EMPTY);
            } orxConfig_PopSection();
        }
    

    inside
    ScrollObject *ScrollBase::CreateObject(const orxSTRING _zModelName, ScrollObject::Flag _xFlags, const orxSTRING _zInstanceName)
    

    in the file ScrollBase.inl, right after

    // Gets binder
        poBinder = ScrollObjectBinderBase::GetBinder(orxFLAG_TEST(_xFlags, ScrollObject::FlagSave | ScrollObject::FlagRunTime) ? _zModelName : orxSTRING_EMPTY);
    

    So, what I'm basically doing is, if the object name doesn't match any of those that are registered, I'm checking if the "ClassIdentifier" field matches.

    This way:
    [StoneWall]
    ChildList	= StoneTop # BrickFace
    Graphic		= MageGraphic
    Position     = (1, 0.0, 0.0);
    ParentCamera = WorldScaler
    ClassIdentifier = myclassid
    

    gets created as a MyClass instance, if I do the registration as:

    void OrxScroll::BindObjects ()
    {
    ScrollBindObject<MyClass> ("myclassid");
    }

    This solution is clearly missing something, since the game crashes when I try to close it. I've traced the crash to the
    template<class O>
    void ScrollObjectBinder<O>::DeleteObject(ScrollObject *_poObject, const orxSTRING _zModelName)
    

    method, with O = ScrollObject. I guess some extra plumbing needs to be added.

    Cheers
  • edited July 2012
    I see what you are trying to do now, thanks for the example! :)

    So that will only work if you've already bound something to your C++ class named MyClass, otherwise the binder won't be found.

    In that case why not binding all the instances in code instead of having some bound in code and some bound via config info, that doesn't sound very consistent to me.

    In your case you're crashing because you've substituted the binder for the object creation but not for the object deletion, so your object will be deleted with the default binder (ScrollObject) and will not be able to free the memory bank slot.

    As I said, I'm not convinced by that hybrid approach as some bindings will be done via code and some via config. I think I prefer trying to mimic inheritance by using the config section parenting to find the default parent binder if none is provided. :)
  • edited July 2012
    Any kind of inheritance will work for my purposes :) I actually agree that your proposal would be more neat and consistent.
  • edited July 2012
    I'll try to do that soon-ish then! :)

    Btw, I've added orxDisplay_DrawMesh() but only the GLFW implementation is here for now.

    XY coordinates are in pixel and UV ones are normalized.

    I'll add an example on how to use it to the default orx playground (orxBounce) tomorrow.
  • edited July 2012
    Thanks and SUPER THANKS! I can port my deformable-body physics engine then :)
  • edited July 2012
    My pleasure!

    I've just modified the GLFW implementation so that it now behaves in a triangle strip way.

    I've added an example in orxBounce which will now display a (crude) trail behind the mouse cursor, at least it shows how it works. :)

    One can use the orxRender_GetScreenPosition() function to transforw world coordinates into screen ones, of course.

    I'll try to do the iOS implementation soon-ish and hopefully someone will be able to do the Android one in not so long. :)
  • edited July 2012
    Hi iarwain,

    I've just had the chance to check the trail. It's working really nice! The interface you provide is also very clean. I shall get down to porting my engine as soon as possible.

    One little question though; since not every mesh can be expressed as a simple triangle strip, one might have to call the orxDisplay_DrawMesh() function multiple times. Is this prohibitively expensive behind the scenes? or does even the worst case of drawing each triangle one by one with an individual call has an acceptable overhead (To put a number, let's say if I have 1000s of triangles)?

    I could try to do the Android bit, but I don't think I'll be able to get down to it in the near future, so someone will very likely beat me to it. I have a related question BTW. Whenever I try to dig into orx source code, I'm met with a barrier of macros that make it very hard to navigate. One method I use to familiarize myself with unfamiliar code, is to simply step through it with a debugger. Unfortunately, the same barrier of macros confuse the hell out of CDB. Do you have any hints as to how I should approach the orx source code? Which IDE/Debugger do you use personally for instance (I'm sure it handles the macros better)?
  • edited July 2012
    enobayram wrote:
    Hi iarwain,

    I've just had the chance to check the trail. It's working really nice! The interface you provide is also very clean. I shall get down to porting my engine as soon as possible.

    Glad you appreciate it! :)
    One little question though; since not every mesh can be expressed as a simple triangle strip, one might have to call the orxDisplay_DrawMesh() function multiple times. Is this prohibitively expensive behind the scenes? or does even the worst case of drawing each triangle one by one with an individual call has an acceptable overhead (To put a number, let's say if I have 1000s of triangles)?

    Calls will get batched behind the scene as much as possible. As long as there's no change in the current rendering context (ie. change of texture, change of filtering (smoothing) or change of blending mode), things gets batched.
    However the batch size is implementation defined, usually 2000 or 4000 triangles per batch. If you need to display bigger strips I recommend on slicing them in more than one call. If it's a too big limitation for you, we can try to find a compromise somewhere. :)
    I could try to do the Android bit, but I don't think I'll be able to get down to it in the near future, so someone will very likely beat me to it.

    Heh, no worries, I'm sure lydesik or faistoiplaisir will port the iOS or GLFW version at some point (or fix my port if I end up doing it myself! ;)).
    I have a related question BTW. Whenever I try to dig into orx source code, I'm met with a barrier of macros that make it very hard to navigate. One method I use to familiarize myself with unfamiliar code, is to simply step through it with a debugger. Unfortunately, the same barrier of macros confuse the hell out of CDB. Do you have any hints as to how I should approach the orx source code? Which IDE/Debugger do you use personally for instance (I'm sure it handles the macros better)?

    Mmh interesting.
    Which macros in particular? They're mostly used as an interface for the plugins (allowing to run in either hotplug or embedded mode) and for some casting+check helpers.

    When coming to a new project I usually use Source Navigator for that purpose. Other than that, I have no preferred editor really, it depends on which platform I am. On mac I use XCode, on windows I use Visual Studio Express and on Linux I use CodeLite.
    They all have pros and cons unfortunately.
  • edited September 2012
    Hi iarwain,

    Just as a reminder of what we've been discussing on this thread:

    if I have an orx object defined by the following config entry:

    [Car@Vehicle]
    ...

    and if I register a class for "Vehicle" in scroll, that Car would also be wrapped by that class.

    My alternative suggestion was:
    [Car]
    classId = Vehicle ; Note here that Vehicle here does not have to be the name of the class in C++, it'll still be registered.

    And it would work the same way.

    Is any of these two features implemented? I've later realized that I can achieve the same effect by writing my special register function that takes a type parameter and a string and walks all the config sections, registering all that include a classId field matching the string. Though I'd like to use the standard one if it's available, or to be implemented.
  • edited September 2012
    I haven't touched Scroll for quite a while, it's 2AM here so I guess I could give it a short try before jumping on some TF2. :)
  • edited September 2012
    Well, it's in. :)

    I also added a default ScrollObject binding to any config section that inherits from a config section [ScrollObject].

    Let me know if you have any issues.

    Cheers!
  • edited September 2012
    Thanks a billion! :)

    It works really nicely. Now I can truly define my custom object classes, with special fields!
  • edited September 2012
    My pleasure, sorry for the long delay! In the end it was a pretty straightforward change of a mere couple of lines.
  • edited September 2012
    Never mind, I didn't need it during that delay anyway :)
  • edited October 2012
    iarwain wrote:
    Well, it's in. :)

    I also added a default ScrollObject binding to any config section that inherits from a config section [ScrollObject].

    Let me know if you have any issues.

    Cheers!

    Is the syntax what was described earlier with the class id field, or do I just need to use [Object@Object]?
  • edited October 2012
    No field, just good ol' config inheritance works.

    If you have:
    In config:
    [Button]
    [GreenButton@Button]
    [GlowingButton@Button]
    [RedGlowingButton@GlowingButton]
    
    In code:
    ScrollBindObject<cButton>("Button");
    ScrollBindObject<cGlowingButton>("GlowingButton");
    

    Then you'll get:
    Button -> cButton
    GreenButton -> cButton
    GlowingButton -> cGlowingButton
    RedGlowingButton -> cGlowingButton
  • edited October 2012
    Excellent. You do great work on this thing, man. Keep it up!
Sign In or Register to comment.