Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Announce new projects or updates of Irrlicht Engine related tools, games, and applications.
Also check the Wiki
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by roxaz »

@peteym5: trust me - you do not need dx10 or dx11. Does your game visual quality match that of crysis2? its dx9! Even crysis3 can be hacked to run on dx9 and it looks damn good. Advice: do not limit yourself with flashy names, even directx8 is capable of good visual quality :)
giulioz
Posts: 8
Joined: Mon Jun 24, 2013 2:21 pm

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by giulioz »

Hi man, there is a little bug, I have corrected it.
When Matrix.MultiplyWith1x4Matrix() is called, there is no modification to the parameters or return value, I have fixed with this:

Code: Select all

array<float>^ MultiplyWith1x4Matrix(array<float>^ m1x4)
    {
        LIME_ASSERT(m1x4 != nullptr);
        LIME_ASSERT(m1x4->Length == 4);
 
        float f[4] = { m1x4[0], m1x4[1], m1x4[2], m1x4[3] };
        m_NativeValue->multiplyWith1x4Matrix(f);
        return gcnew array<float> { f[0], f[1], f[2], f[3] };
    }
instead of:

Code: Select all

void MultiplyWith1x4Matrix(array<float>^ m1x4)
    {
        LIME_ASSERT(m1x4 != nullptr);
        LIME_ASSERT(m1x4->Length == 4);
 
        float f[4] = { m1x4[0], m1x4[1], m1x4[2], m1x4[3] };
        m_NativeValue->multiplyWith1x4Matrix(f);
    }
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by greenya »

This is definitely a bug and thank you for reporting it.

I have changed the method next way (i do not return a value since native Irrlicht doesn't, it changes input value for performance reasons):

Code: Select all

    void MultiplyWith1x4Matrix(array<float>^% m1x4)
    {
        LIME_ASSERT(m1x4 != nullptr);
        LIME_ASSERT(m1x4->Length == 4);
 
        float f[4] = { m1x4[0], m1x4[1], m1x4[2], m1x4[3] };
        m_NativeValue->multiplyWith1x4Matrix(f);
 
        m1x4[0] = f[0];
        m1x4[1] = f[1];
        m1x4[2] = f[2];
        m1x4[3] = f[3];
    }
Now it can be used next way:

Code: Select all

    Matrix matrix = new Matrix(new Vector3Df(1, 2, 3));
    float[] arr = new float[] { 1, 2, 3, 100 }; // if you want to save original values, you have to do smth like "float[] arrOriginal = arr.Clone() as float[];"
    matrix.MultiplyWith1x4Matrix(ref arr);
    // now arr == { 101, 202, 303, 100 }
P.S.: this fix currently only in svn trunk.
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by roxaz »

Since we are speaking of bugs i noticed that Node.Position.Set(10, 10, 10) does not move node at all, Node.Position = new Vector3Df(10, 10, 10) does however.
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by greenya »

This is because Node.Position returns a copy of Vector3Df. There is no "const" access in C# like C++ has, C# has "const" and "readonly" and they are not the "const" C++ has. So i cannot return const object so calling like "Node.Position.X += 5;" or "Node.Position.Set(10,10,10);" would lead to compiling error. In order to mock C++ const, i have to create Vector3DfConst class, which should derive from Vector3Df but allow only "const" member, the members which doesn't change internal state of the object.

Why not just return a reference on Vecro3Df when accesing Node.Position?
Other solution is to return Vecror3Df as a reference, so "Node.Position.X += 5;" will work just fine. But there is a pitfall, next example demonstrates it:

Code: Select all

node2.Position = node1.Postion;
node2.Position.X += 5;
Its obvious that the aim of the code above is to align node2 to node1, so we set position of node2 to be the same as node1 and then we moved node2 a bit by X axis. This is how it looks like and how it works just now. But if Node.Position will return a reference, then after code above node2 and node1 has the same position starting from "node2.Position = node1.Postion;" and it doesn't matter which position you change, they both will move since they share same Vector3Df. The line with assigning a position should look like "node2.Position = new Vector3Df(node1.Postion);" or "node2.Position.Set(node1.Postion);" to work as intended if i Node.Position returns a reference.

That is why i return a copy. This is slower but more safe and more obvious. If you write some code which needs to read same Node.Position in the loop million times and it will not get changed until loop ends for sure, its better to cache it in local variable for performance reasons.

Why Vector3Df is a reference type?
This issue do come from the fact that Vector3Df (and all other "IrrlichtLime.Core" classes) ideally should be "value" types. But my main idea of the wrapper was to wrap even base types, since the long term support of the project becomes an ease. I mean delegating all the calls to a native Irrlicht calls is much more easy and stable than you implement all the "core" types in managed language (each time new bug gets fixed in the source code, Lime will need to be updated too; right now, if Irrlicht has nothing updated in "include" folder all what is necessary to make Lime up-to-date is to rebuild it with new Irrlicht version; it will build just fine since interface hasn't changed -- the "include" folder defines the interface between the wrapper and Irrlicht).

Ok. Does everything returns a copy?
In most cases yes, But there are exceptions like Node.GetMaterial(), it returns a reference. That is why you can write something like:

Code: Select all

node.GetMaterial(0).SetTexture(0, driver.GetTexture("../../media/faerie2.bmp"));
node.GetMaterial(0).Lighting = false;
node.GetMaterial(0).NormalizeNormals = true;
But "Node.SetMaterial(int num, Material newMaterialToCopyFrom)" does copy the input material as you might get from the name of the argument.

How do i suppose to predict when do i get a copy and when - a reference?
All the types which are inherited from IrrlichtLime::NativeValue<T> do wraps some type from Irrlicht which is a struct (a value type). NativeValue<T> has private member "bool m_DeleteOnFinalize". You cannot access it from your code in common way, but you can check its value while debugging the code. For the returned value from Node.Position or Node.GetMaterial() or other methods if "m_DeleteOnFinalize" is "true" then you got a copy otherwise a reference.
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by roxaz »

thanks for detailed response, that clears up everything
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by roxaz »

I would like to contribute few little patches.

1. Setting relative positions for CursorControl. For some reason relative position is set using setPosition(f32,f32) or setPosition(vector2d<f32>), but obtained using getRelativePosition(). Obvious inconsistency probably was reason for this to be missing.

Code: Select all

Index: CursorControl.cpp
===================================================================
--- CursorControl.cpp   (revision 493)
+++ CursorControl.cpp   (working copy)
@@ -55,6 +55,12 @@
    return gcnew Vector2Df(m_CursorControl->getRelativePosition());
 }
 
+void CursorControl::RelativePosition::set(Vector2Df^ value)
+{
+   LIME_ASSERT(value != nullptr);
+   m_CursorControl->setPosition(*value->m_NativeValue);
+}
+
 bool CursorControl::Visible::get()
 {
    return m_CursorControl->isVisible();
Index: CursorControl.h
===================================================================
--- CursorControl.h (revision 493)
+++ CursorControl.h (working copy)
@@ -18,7 +18,7 @@
 
    property CursorPlatformBehavior PlatformBehavior { CursorPlatformBehavior get(); void set(CursorPlatformBehavior value); }
    property Vector2Di^ Position { Vector2Di^ get(); void set(Vector2Di^ value); }
-   property Vector2Df^ RelativePosition { Vector2Df^ get(); }
+   property Vector2Df^ RelativePosition { Vector2Df^ get(); void set(Vector2Df^ value); }
    property bool Visible { bool get(); void set(bool value); }
 
    virtual String^ ToString() override;
 
2. Suggestion to make scene node Position getter/setter and Scale setter virtuals. This would allow more freedom for developer when some exotic behaviors are required.

Code: Select all

Index: SceneNode.h
===================================================================
--- SceneNode.h (revision 493)
+++ SceneNode.h (working copy)
@@ -59,10 +59,10 @@
    property int MaterialCount { int get(); }
    property String^ Name { String^ get(); void set(String^ value); }
    property SceneNode^ Parent { SceneNode^ get(); void set(SceneNode^ value); }
-   property Vector3Df^ Position { Vector3Df^ get(); void set(Vector3Df^ value); }
+   property Vector3Df^ Position { virtual Vector3Df^ get(); virtual void set(Vector3Df^ value); }
    property Matrix^ RelativeTransformation { Matrix^ get(); }
    property Vector3Df^ Rotation { Vector3Df^ get(); virtual void set(Vector3Df^ value); }
-   property Vector3Df^ Scale { Vector3Df^ get(); void set(Vector3Df^ value); }
+   property Vector3Df^ Scale { Vector3Df^ get(); virtual void set(Vector3Df^ value); }
    property Scene::SceneManager^ SceneManager { Scene::SceneManager^ get(); protected: void set(Scene::SceneManager^ value); }
    property Scene::TriangleSelector^ TriangleSelector { Scene::TriangleSelector^ get(); void set(Scene::TriangleSelector^ value); }
    property bool TrulyVisible { bool get(); }
 


3. My .net-managed irrlicht object deletion i talked about earlier. In case you greenya find time testing it - it would save you 3 minutes.

Code: Select all

Index: ReferenceCounted.cpp
===================================================================
--- ReferenceCounted.cpp    (revision 493)
+++ ReferenceCounted.cpp    (working copy)
@@ -28,6 +28,8 @@
 ReferenceCounted::ReferenceCounted(irr::IReferenceCounted* referenceCounted_or_null)
 {
    m_ReferenceCounted = referenceCounted_or_null;
+   if( referenceCounted_or_null != nullptr )
+       Grab();
 }
 
 bool ReferenceCounted::Drop()
@@ -42,6 +44,12 @@
    m_ReferenceCounted->grab();
 }
 
+ReferenceCounted::!ReferenceCounted()
+{
+   if( m_ReferenceCounted != nullptr )
+       Drop();
+}
+
 String^ ReferenceCounted::DebugName::get()
 {
    LIME_ASSERT(m_ReferenceCounted != nullptr);
Index: ReferenceCounted.h
===================================================================
--- ReferenceCounted.h  (revision 493)
+++ ReferenceCounted.h  (working copy)
@@ -21,6 +21,9 @@
    property String^ DebugName { String^ get(); }
    property int ReferenceCount { int get(); }
 
+   ~ReferenceCounted(){}
+   !ReferenceCounted();
+
 internal:
 
    ReferenceCounted(irr::IReferenceCounted* referenceCounted_or_null);
 
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by greenya »

Hi.

1. Yes, that was a missing, I have added CursorControl.RelativePosition setter. Thank you for notification!

2. Maybe you're right, but why only setters and only for Position and Scale? Maybe include Rotation too? (nothing committed about this for now)

3. I have applied your patch and for my quick tests it doesn't work for OpenGL renderer. It crashes every example on exit if it uses OpenGL.
It crashes in Drop(), because somehow m_ReferenceCounted is not null but drop() cannot be called since count of references is 0.
I decided to change it to next:

Code: Select all

bool ReferenceCounted::Drop()
{
    LIME_ASSERT(m_ReferenceCounted != nullptr);
 
    bool b = m_ReferenceCounted->drop();
    if (b)
        m_ReferenceCounted = nullptr;
 
    return b;
}
So it does the same but i set m_ReferenceCounted to null additionally. This is silly change because it is not consistant, you still can have couple of pointers and if you call drop from one of them and its m_ReferenceCounted became null, it will not change anyhting about other pointers which points to the same object. So i just want to say that our wrapper' ReferenceCounted.Drop() cannot track real null or ReferenceCounter value of the object. This is why, I still believe that for stability its necessary to force user manually do "dropping" calls.

Anyway, the change above helped for all examples up to L01.TexturePainting. This example can work and not. To increase chance of crash i do next: start app, choose OpenGL and hit Run, next steps do quickly: draw a line, change to different texture size ... repeat this 5+ times and close app (click on the "X" of texture or "X" or main window) -- the app will crash:
Image
and the view under Visual Studio:
Image
So as you may see, m_ReferenceCounted is not null, its just somehow internally Irrlicht or something has disposed resource and now GC trying to finilize it. I don't know why it happens, since we do Grab() and want our Drop(). Besides as you see from the screenshot above, DebugName is "CGUIWindow" - not a texture object. Please test it your self and tell the results.
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by roxaz »

greenya wrote:2. Maybe you're right, but why only setters and only for Position and Scale? Maybe include Rotation too? (nothing committed about this for now)
Im all for freedom. Make them all virtual! :)
greenya wrote:3. I have applied your patch and for my quick tests it doesn't work for OpenGL renderer. It crashes every example on exit if it uses OpenGL.
It crashes in Drop(), because somehow m_ReferenceCounted is not null but drop() cannot be called since count of references is 0.
I decided to change it to next:

Code: Select all

bool ReferenceCounted::Drop()
{
    LIME_ASSERT(m_ReferenceCounted != nullptr);
 
    bool b = m_ReferenceCounted->drop();
    if (b)
        m_ReferenceCounted = nullptr;
 
    return b;
}
So it does the same but i set m_ReferenceCounted to null additionally.
That was expected behavior because device was dropped manually. This check and setting object pointer to null is indeed required, i missed that.
greenya wrote:This is silly change because it is not consistant, you still can have couple of pointers and if you call drop from one of them and its m_ReferenceCounted became null, it will not change anyhting about other pointers which points to the same object. So i just want to say that our wrapper' ReferenceCounted.Drop() cannot track real null or ReferenceCounter value of the object. This is why, I still believe that for stability its necessary to force user manually do "dropping" calls.
Well.. It is even more true with manually-managed reference counts. Who will save us from programming mistake that drops object one time too much and all managed wrapper objects get native object swept from underneath of them? This is exactly why automatic management of reference counts (since .NET does it for objects already) would be best. Provided it worked of course.
greenya wrote:Anyway, the change above helped for all examples up to L01.TexturePainting. This example can work and not. To increase chance of crash i do next: start app, choose OpenGL and hit Run, next steps do quickly: draw a line, change to different texture size ... repeat this 5+ times and close app (click on the "X" of texture or "X" or main window) -- the app will crash:
and the view under Visual Studio:
So as you may see, m_ReferenceCounted is not null, its just somehow internally Irrlicht or something has disposed resource and now GC trying to finilize it. I don't know why it happens, since we do Grab() and want our Drop(). Besides as you see from the screenshot above, DebugName is "CGUIWindow" - not a texture object. Please test it your self and tell the results.
And this crash is very peculiar! Even if debug name says CGUIWindow (no idea why) - it actually crashes in COpenGLTexture dtor when executing glDeleteTextures(1, &TextureName);! In directx9 (also could not crash neither in burnings nor in software mode) mode it releases texture just fine. Mind you same ref count management! This is very strange and really screams for a problem in somewhere else than automatic reference management. One thing i noticed is GC running on other thread than main one. This might really be fault of OpenGL:
Can I create and dispose of resources in a different thread than the one the OpenGL context is in?
If you make the context current (wglMakeCurrent for Windows). However, it does not buy you much, if anything at all, so it's not worth the trouble.
It is recommended that you call wglMakeCurrent(NULL, NULL) if GL context is current on another thread, then call wglMakeCurrent(dc, glrc) in the other thread.
https://www.opengl.org/wiki/OpenGL_and_multithreading
Source: https://www.opengl.org/wiki/OpenGL_and_multithreading
Sounds to me like this problem can be solved. Would be perfect if someone who actually knows what they are doing with opengl lend us a hand here. Or at least provide some input on the issue.
peteym5
Posts: 21
Joined: Wed Sep 12, 2012 3:56 pm

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by peteym5 »

Irrlicht Lime and Materials

I managed to port a few of the Irrlicht Lime C# to VB.NET. I am considering it as a new possible engine to drive the 3D graphics in a game project I have laying around.

One thing I need it to do is to be able to alter colors of 3D models loaded it, on top of textures. I have done this my changing the materials Ambient, Diffuse, Emissive, Spectacler, properties. However when I tried to change these with Irrlicht Lime, I get all types of weird results. There are currently to real samples working with materials yet. I know many games do work with materials to change colors of objects and is something I like to see developed for the next version of Irrlicht Lime.
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by greenya »

Hello.

If you want to get vb.net source of some example which is on c#, you can use any online converter, for example google for "convert c# to vb.net". Maybe all the code of an example you will not be able to convert without problems, but i have no problems when converting the source of each method separately.

The material system in Lime is the same as in native Irrlicht Engine. Some simple things: if you need to draw some mesh node using specific color, you can set its vertex colors using next line

Code: Select all

scene.MeshManipulator.SetVertexColors(node.Mesh, new Color(20, 80, 180));
By default (if you create mesh using CeometryCreator or scene's "addXXX" methods) all the vertex colors are white, but if mesh loaded after some 3d model editor - it can be up to editor what vertex colors will be. Some materials do blend the vertex color and the texture. For example, if you have a mesh node with a texture and it uses MaterialType.Solid, you can change the vertex colors from white to any other and you will get the texture and the shading of that color blended together. You can use it for visualisation of selection in your scene or some kind of focus. Keep in mind, this doesn't work with all the material types, for example if you have mesh node with MaterialType.NormalMapSolid with 2 textures with nice bump, setting vertex colors will do not affect rendering result.
peteym5
Posts: 21
Joined: Wed Sep 12, 2012 3:56 pm

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by peteym5 »

I have my old VB.NET 2010 sample working. I tried the scene.MeshManipulator.SetVertexColors and it did color the object to whatever Red Green and Blue parameters I set it to. The method I used with the Truevision3D engine would be similar to MeshName.SetMaterial. The Material will have individual parameters for Ambient, Diffuse, Emmisive, Spectaclar, and power. I use this to make some objects have different reflective and translucent appearance, like some seem to be made of glass or metal.

I am also trying "SetMaterial" methods with SceneNode, I can change the texture parameters, but the Ambient, Diffuse, Emmisive, Spectaclar, and Shininess settings are not doing anything.
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by greenya »

Please provide small code which i can use to reproduce the problem.
I will check if the behavior is different from native Irrlicht Engine.
peteym5
Posts: 21
Joined: Wed Sep 12, 2012 3:56 pm

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by peteym5 »

This is not the complete code, but does show some of the things I am attempting.

Code: Select all

        
        Dim buf0 As MeshBuffer = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit)
        Dim vertices(7 * 2 - 1) As Vertex3D
        Dim indices(5 * 6 - 1) As UShort
        Dim indices4 As UShort() = New UShort() {0, 1, 2, 1, 3, 2}
 
        materialx = New Video.Material
        materialx.SetTexture(0, Driver.GetTexture("media/wall02.jpg"))
        materialx.BackfaceCulling = False
        materialx.Type = MaterialType.Solid
        materialx.Shininess = 128
        materialx.Fog = False
        materialx.SetFlag(MaterialFlag.Lighting, False)
        materialx.SetFlag(MaterialFlag.NormalizeNormals, True)
        materialx.Lighting = False
        materialx.AmbientColor = New Color(0, 0, 0, 128)
        materialx.DiffuseColor = New Color(0, 0, 0, 128)
        materialx.EmissiveColor = New Color(0, 0, 0, 0)
        materialx.SpecularColor = New Color(64, 32, 0, 128)
 
 For i = 0 To 4
       For j = 0 To 1024 Step 128
            vertices(v + 0) = New Vertex3D(50, 100, j, 50, 0, j, New Color(255, 255, 255, 128), 0, i)
            vertices(v + 1) = New Vertex3D(50, 0, j, 50, 0, j, New Color(255, 255, 255, 128), 1, i)
            v = v + 2
            i = i + 1
        Next j
        iMesh(i) = IrrlichtLime.Scene.Mesh.Create
        iMesh(i).AddMeshBuffer(buf0)
        buf0.Append(vertices, indices)
        buf0.SetMaterial(materialx)
        buf0.RecalculateBoundingBox()
        buf0.Drop()
        iNode(i) = smgr.AddMeshSceneNode(WallMesh(0))
        iNode(i).SetMaterial(0, materialx)
       
        iNode(i).SetMaterialFlag(MaterialFlag.ColorMaterial, True)
        'iNode(i).SetMaterial(0, materialx)
        iMesh(i).SetMaterialFlag(MaterialFlag.Lighting, False)
        iMesh(i).SetMaterialFlag(MaterialFlag.ColorMaterial, True)
 
 
            iNode(i).SetMaterialFlag(MaterialFlag.BackFaceCulling, False)
            iNode(i).SetMaterialFlag(MaterialFlag.Lighting, False)
            iNode(i).SetMaterialFlag(MaterialFlag.NormalizeNormals, False)
            iNode(i).SetMaterialFlag(MaterialFlag.BlendOperation, True)
            iNode(i).SetMaterialTexture(0, Driver.GetTexture("media/wall02.jpg"))
            iNode(i).SetMaterialFlag(MaterialFlag.ColorMaterial, False)
            iNode(i).SetMaterialType(MaterialType.Solid)
            iNode(i).SetMaterial(0, materialx)
        Next i
        iNode(0).Position = New Vector3Df(-2048, 512, 512)
        iNode(1).Position = New Vector3Df(-2048, -512, 512)
        iNode(2).Position = New Vector3Df(-2048, 512, -512)
        iNode(3).Position = New Vector3Df(-2048, -512, -512)
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: Irrlicht Lime is a .NET wrapper for Irrlicht Engine

Post by greenya »

This code doesn't help me to reproduce the problem.

Even if i resolve some unknown variables i still have some like WallMesh array that i don't have and i don't have the meshes which it contains (loaded from files?)

Your "small peace of code which reproduces the problem" must compile and run without problems. If it will do that, i will translate it to C++ for native Irrlicht Engine. Keep in mind, that the code must be small, i'm not going to translate whole the project with bunch of classes. I do expect literary single method which creates IrrlichtDevice, sets up the scene, adds camera, runs the main loop and drops the device after all. I do expect this to be like 20-30 lines of code all the code (the code which is minimum to reproduce the problem, it must not contain anything not related to the problem). If its necessary to have a model file or texture or any other resources and you do use them in your code, the archive with that resources must be provided either.

After i translate the code on C++ and check it with native Irrlicht Engine there are two possible results:
- the rendering result is identical; if so, and if its not how its suppose to be, then its bug of the engine, not the wrapper;
- the rendering result is different; in most cases it means the wrapper has a bug and i will have to fix it.
Post Reply