Multiple inheritance SWIG bindings

Problems building or running the engine, queries about how to use features etc.
User avatar
Arthurfernandes
Halfling
Posts: 42
Joined: Mon Oct 28, 2024 10:50 pm
x 7

Multiple inheritance SWIG bindings

Post by Arthurfernandes »

Ogre Version: 14.3.4
Operating System: Win 10
Render System: DX11

Hey everyone!

In my journey to use Ogre in C# with SWIG bindings, I'm facing some problems. Until now, I was able to make some quick and dirty changes in the .i files so that SWIG generates the correct bindings. But now I have a big problem: C++ supports multiple inheritance, but C# doesn't. The class I'm trying to use is ParticleSystem, and this class inherits from both StringInterface and MovableObject. By default, SWIG takes the first base class—in this case, StringInterface—and uses it normally, but the second base class is ignored. The problem is, I need ParticleSystem to be a MovableObject; otherwise, I won't be able to attach it to a SceneNode and use it in my scene.

The solution that Mogre used was to transform StringInterface into an interface and use it in C# together with MovableObject.

Code: Select all

public interface IStringInterface
{
    unsafe static implicit operator IStringInterface(Ogre.StringInterface* t)
    {
        //IL_003f: Expected I, but got I8
        //IL_0060: Expected I, but got I8
        //IL_0075: Expected I, but got I8
        if (0L == (nint)t)
        {
            return null;
        }

    CLRObject* ptr = (CLRObject*)global::_003CModule_003E.__RTDynamicCast(t, 0, System.Runtime.CompilerServices.Unsafe.AsPointer(ref global::_003CModule_003E._003F_003F_R0_003FAVStringInterface_0040Ogre_0040_0040_00408), System.Runtime.CompilerServices.Unsafe.AsPointer(ref global::_003CModule_003E._003F_003F_R0_003FAVCLRObject_0040_0040_00408), 0);
    if (0L == (nint)ptr)
    {
        throw new System.Exception("The native class that implements Ogre::StringInterface isn't a subclass of CLRObject. Cannot create the CLR wrapper object.");
    }

    ulong num = *(ulong*)((ulong)(nint)ptr + 8uL);
    object obj;
    if (num == 0L)
    {
        obj = null;
    }
    else
    {
        IntPtr intPtr = new IntPtr((void*)num);
        obj = ((GCHandle)intPtr).Target;
    }

    if (null == obj)
    {
        ((delegate* unmanaged[Cdecl, Cdecl]<IntPtr, void>)(*(ulong*)(*(long*)ptr + 8)))((nint)ptr);
        num = *(ulong*)((ulong)(nint)ptr + 8uL);
        if (num == 0L)
        {
            obj = null;
        }
        else
        {
            IntPtr intPtr2 = new IntPtr((void*)num);
            obj = ((GCHandle)intPtr2).Target;
        }
    }

    return (IStringInterface)obj;
}

unsafe static implicit operator Ogre.StringInterface*(IStringInterface t)
{
    //IL_000e: Expected I, but got I8
    //IL_000d->IL000d: Incompatible stack types: I vs I8
    return (Ogre.StringInterface*)((t == null) ? 0 : ((nint)t._GetNativePtr()));
}

unsafe Ogre.StringInterface* _GetNativePtr();

[return: MarshalAs(UnmanagedType.U1)]
bool SetParameter(string name, string value);

void SetParameterList(Const_NameValuePairList paramList);

string GetParameter(string name);

void CopyParametersTo(IStringInterface dest);
}

I don't have much knowledge of SWIG, and I'm running out of solutions. Can anyone help?

paroj
OGRE Team Member
OGRE Team Member
Posts: 2151
Joined: Sun Mar 30, 2014 2:51 pm
x 1156

Re: Multiple inheritance SWIG bindings

Post by paroj »

you could just reorder the base classes as the StringInterface properties are also exported as normal methods

User avatar
Arthurfernandes
Halfling
Posts: 42
Joined: Mon Oct 28, 2024 10:50 pm
x 7

Re: Multiple inheritance SWIG bindings

Post by Arthurfernandes »

paroj wrote: Tue Apr 01, 2025 9:26 pm

you could just reorder the base classes as the StringInterface properties are also exported as normal methods

What you're saying is to do this:

Code: Select all

class _OgreExport ParticleSystem : public MovableObject, public StringInterface

Right?

The problem is that, for some random reason, SampleBrowser stopped working, and when SWIG makes the bindings, I can use ParticleSystem in AttachObject from a SceneNode, but I get an error saying that it is already attached to a SceneNode or a Bone, but it is literally the first time I'm trying to do it.

Code: Select all

public void AddParticleToScene()
{
    ParticleSystem.setDefaultNonVisibleUpdateTimeout(5);

ParticleSystem ps;

ps = scnMgr.createParticleSystem("Fireworks", "Examples/Fireworks");
scnMgr.getRootSceneNode().attachObject(ps);

ps = scnMgr.createParticleSystem("Nimbus", "Examples/GreenyNimbus");
scnMgr.getRootSceneNode().attachObject(ps);

ps = scnMgr.createParticleSystem("Rain", "Examples/Rain");
ps.fastForward(5);
scnMgr.getRootSceneNode().createChildSceneNode(new Vector3(0, 1000, 0)).attachObject(ps);

ps = scnMgr.createParticleSystem("Aureola", "Examples/Aureola");
scnMgr.getRootSceneNode().attachObject(ps);

SceneNode mFountainPivot = scnMgr.getRootSceneNode().createChildSceneNode();

ps = scnMgr.createParticleSystem("Fountain1", "Examples/PurpleFountain");
mFountainPivot.createChildSceneNode(new Vector3(200, -100, 0), new Quaternion(new Radian(new Degree(20)), new Vector3(0, 0, 1))).attachObject(ps);

ps = scnMgr.createParticleSystem("Fountain2", "Examples/PurpleFountain");
mFountainPivot.createChildSceneNode(new Vector3(-200, -100, 0), new Quaternion(new Radian(new Degree(-20)), new Vector3(0, 0, 1))).attachObject(ps);
}

I'm getting the error from:

Code: Select all

scnMgr.getRootSceneNode().attachObject(ps);

and the message is:

Code: Select all

"System.ApplicationException: 'Ogre::RuntimeAssertionException::RuntimeAssertionException: !obj->isAttached() failed. Object already attached to a SceneNode or a Bone in Ogre::SceneNode::attachObject at D:\3D\Ferramentas\Ogre-14.3.4\OgreMain\src\OgreSceneNode.cpp (line 102)'"

I'm using it just like in the examples, so it should work, I think.

paroj
OGRE Team Member
OGRE Team Member
Posts: 2151
Joined: Sun Mar 30, 2014 2:51 pm
x 1156

Re: Multiple inheritance SWIG bindings

Post by paroj »

sounds like you need to do a clean rebuild of your project

User avatar
Arthurfernandes
Halfling
Posts: 42
Joined: Mon Oct 28, 2024 10:50 pm
x 7

Re: Multiple inheritance SWIG bindings

Post by Arthurfernandes »

paroj wrote: Tue Apr 01, 2025 10:57 pm

sounds like you need to do a clean rebuild of your project

It is not working at all. Every time I change the order, the SampleBrowser crashes after loading a few things from Ogre. The problem is not the SampleBrowser, but I think that if it breaks every time I make the change, it means something is not right.

I ran SampleBrowser from Visual Studio, and the error when it crashes is this:

Code: Select all

Exception thrown at 0x00007FFC274C30F0 (OgreMain.dll) in SampleBrowser.exe: 0xC0000005: Access violation reading location 0x0000000000000000.

Unhandled exception at 0x00007FFC274C30F0 (OgreMain.dll) in SampleBrowser.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
paroj
OGRE Team Member
OGRE Team Member
Posts: 2151
Joined: Sun Mar 30, 2014 2:51 pm
x 1156

Re: Multiple inheritance SWIG bindings

Post by paroj »

try to create a PR with this change and see whether it passes

paroj
OGRE Team Member
OGRE Team Member
Posts: 2151
Joined: Sun Mar 30, 2014 2:51 pm
x 1156

Re: Multiple inheritance SWIG bindings

Post by paroj »

explanation by deepseek:

Code: Select all

struct Base1 { int x; };
struct Base2 { int y; };
struct Derived : Base1, Base2 {};

Derived d;
Base2* b2 = &d; // Points to the Base2 subobject (offset may exist)
void* v = b2;   // Store Base2* as void*
// WRONG: Direct cast from void* to Derived* skips adjustment!
Derived* d2 = static_cast<Derived*>(v); // Likely UB (wrong address)

// To recover, must cast back via the complete object:
Derived* d2 = static_cast<Derived*>(static_cast<Base2*>(v)); // Safe
User avatar
Arthurfernandes
Halfling
Posts: 42
Joined: Mon Oct 28, 2024 10:50 pm
x 7

Re: Multiple inheritance SWIG bindings

Post by Arthurfernandes »

paroj wrote: Fri Apr 04, 2025 9:49 pm

explanation by deepseek:

Code: Select all

struct Base1 { int x; };
struct Base2 { int y; };
struct Derived : Base1, Base2 {};

Derived d;
Base2* b2 = &d; // Points to the Base2 subobject (offset may exist)
void* v = b2;   // Store Base2* as void*
// WRONG: Direct cast from void* to Derived* skips adjustment!
Derived* d2 = static_cast<Derived*>(v); // Likely UB (wrong address)

// To recover, must cast back via the complete object:
Derived* d2 = static_cast<Derived*>(static_cast<Base2*>(v)); // Safe

Everything is working perfectly with your pull request now. Thanks a lot for the support, @paroj !