Using Ogre in Swift

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
berserkerviking
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 63
Joined: Tue May 02, 2017 8:15 pm
x 16

Using Ogre in Swift

Post by berserkerviking »

I'm interested in biomolecular visualization. Ogre has a lot of strengths in this area and I like that I can enhance the library if needed.
But I love programming in Swift (which is available on both macOS and Linux). I can literally program twice as fast in Swift as compared
to C++. So I decided to see if I could write a Swift program that uses Ogre. I succeeded in writing a simple rotating textured cube demo in Swift (see below
for the code).

So how did I do this? Swift interoperates with Objective-C, in that you can call Swift from Objective-C, and vice versa. But it doesn't (directly)
interoperate with C++. Fortunately, Apple supports something called "Objective-C++," which is like normal Objective-C, but you can intermix it with
C++. So you can get at C++ from Swift via Objective-C++ wrappers.

So I started with Sample_Hdr and simplified it to render only a textured cube. (And I added another directional light so it looks better).
After that, I modified the file type of the main app and gamestate .cpp's to be .mm files (this allowed me to have a C++ app which can make
calls to Objective-C). I then broke out the key methods of the gamestate into Objective-C code, which I called though this interface.

Code: Select all

@interface HdrAppObjc: NSObject
- (void)createScene01WithSceneManager:(SceneManagerObjc*)sceneManager;
- (void)update:(float)timeSinceLast;
@end
I then implemented a wrapper library which provides an Objective-C class for every Ogre class that I wanted to use, and which provides
Objective-C methods for every Ogre method I wanted to use. I then converted the main program code from C++ into Objective-C.
At this point, I had a working Objective-C program calling Ogre.

After that it was relatively trivial to convert it to Swift. The result is below.

The resulting code isn't quite as terse as what can be achieved in C++. And there is a bit of memory overhead for each class, which can be significant
for things like vectors and colourvalues. And, of course, there is the single indirection of calling overhead. But, all in all, I thought it turned out
pretty nice. And I can see myself using it in future Ogre projects. Of course, every time I encounter a new class or method that hasn't been wrapped
I will need to write the wrapper for it, but over time it could grow into a generally usable language binding. If you are interested in participating
in this, let me know.

The full code is too big to attach to this post, but I can publish it if people are interested.

Code: Select all

@objc public class HdrApp: NSObject
{
    private var _sceneNode: SceneNodeObjc?

    public func createScene01WithSceneManager(_ sceneManager: SceneManagerObjc)
    {
        let meshName = "Cube_d.mesh"
        let item = sceneManager.createItem(withMeshName: meshName, groupName:nil)
        item!.setDataBlock("Rocks")
        item!.setVisibilityFlags(0x000000001)
    
        _sceneNode = sceneManager.getRootSceneNode(.SCENE_DYNAMIC).createChildSceneNode(.SCENE_DYNAMIC)
        _sceneNode!.setPositionX(0.0, y:0.0, z:0.0)
        _sceneNode!.setScaleX(3.0, y:3.05, z:3.0)
        _sceneNode!.attachObject(item)
    
        let rootNode = sceneManager.getRootSceneNode()
    
        let light = sceneManager.createLight()
        let lightNode = rootNode!.createChildSceneNode()
        lightNode!.attachObject(light)
    
        light!.setPowerScale(97.0)
        light!.setType(LT_DIRECTIONAL)
        // Use normalise instead of normalisedCopy
        let lightDirection = Vector3Objc(x:-1.0, y:-1.0, z:-1.0)
        lightDirection!.normalise()
        light!.setDirection(lightDirection)
    
        // Use multiplyBy instead of '*'
        let upper = ColourValueObjc(red:0.3, green:0.5, blue:0.7)
        upper!.multiply(by:0.1 * 0.75 * 60.0)
        let lower = ColourValueObjc(red:0.6, green:0.45, blue:0.3)
        lower!.multiply(by:0.065 * 0.75 * 60.0)
        let ambientDir = light!.getDirection()
        // Use negate instead of unitary -
        ambientDir!.negate()
        let addend = Vector3Objc.unit_Y()
        addend!.multiply(by:0.2)
        ambientDir!.add(addend)
        sceneManager.setAmbientLightWithUpperHemisphere(upper, lowerHemisphere:lower, hemisphereDir:ambientDir)
    
        let light2 = sceneManager.createLight()
        let lightNode2 = rootNode!.createChildSceneNode()
        lightNode2!.attachObject(light2)
        light2!.setPowerScale(97.0)
        light2!.setType(LT_DIRECTIONAL)
        // Use normalise instead of normalisedCopy
        let lightDirection2 = Vector3Objc(x:1.0, y:-1.0, z:-1.0)
        lightDirection2!.normalise()
        light2!.setDirection(lightDirection2)
    }
    
    public func update(_ timeSinceLast: Float)
    {
        _sceneNode!.yaw(timeSinceLast * 10.0 * 0.125)
    }
}
Post Reply