vujadin / BabylonHx

Port of Babylon.js 3D engine to Haxe.
http:/paradoxplay.com/babylonhx
Apache License 2.0
188 stars 43 forks source link

Instancing support for native openGL #67

Closed Aatu closed 8 years ago

Aatu commented 8 years ago

I have been trying to get instancing to work on native platforms. Tested with lime on windows and html5 targets. This is not yet working. Gives no error but nothing is drawn on the screen either. HTML5 still works.

The problem I am trying to solve, is that with htlm5 engine uses ANGLE_instanced_arrays, which is not available with native OpenGL. Loading of extensions works a bit differently too. You don't load the extension, instead you load a specific function.

My hypothesis is that the api is identical on OpenGL and WebGL (while the extension/function names are different). So to that end I extracted the engine capabilities to different classes and abstracted the instancedArrays implementation so that it loads the correct extension/functions and calls them.

But it is not working. My understanding of OpenGL is pretty much at it's limits, and I was thinking if you might help me so we can get babylonhx instancing working:)

I also commented a bit of the code inline below.

vujadin commented 8 years ago

As far as I know ANGLE_instanced_arrays is OpenGL ES 2.0/WebGL 1.0 extension, so it should work on both html5 and on mobile devices with opengl es 2.0 support (opengl es 3.0 and webgl 2.0 will have this built at the core). If you want to get this to work on desktop native (win,linux,mac) you'll have to look for GL_ARB_instanced_arrays extension. Then you'll have to look for functions in Engine.hx class with ANGLE suffix (vertexAttribDivisorANGLE for example) and wrap that piece of code in #if #else block where if target is not html5 or mobile you'll call these functions without ANGLE suffix (just vertexAttribDivisor or maybe vertexAttribDivisorARB ?? ... not sure about this). In Engine.hx somewhere you'll find this line: this._glExtensions = GL.getSupportedExtensions(); and if you're targeting desktop it should contain 'GL_ARB_instanced_arrays'

Aatu commented 8 years ago

Indeed, I want to target desktop, and I pretty much did exactly what you instructed. Would love it you could take a look at the code in this PR:)

vujadin commented 8 years ago

Ok, I'll check this out later today when I get back home.

Aatu commented 8 years ago

Thanks a lot mate:)

vujadin commented 8 years ago

I've looked at your code and I've done the same but in a simpler way. Here's modified EngineCapabilities.hx:

#if (!mobile && cpp) 
typedef IAMethods = {
    vertexAttribDivisorANGLE: Dynamic,
    drawElementsInstancedANGLE: Dynamic,
    drawArraysInstancedANGLE: Dynamic
}
#end

@:expose('BABYLON.EngineCapabilities') class EngineCapabilities {

    public var maxTexturesImageUnits:Int;
    public var maxTextureSize:Int;
    public var maxCubemapTextureSize:Int;
    public var maxRenderTextureSize:Null<Int>;
    public var standardDerivatives:Null<Bool>;
    public var s3tc:Dynamic;
    public var textureFloat:Null<Bool>;
    public var textureAnisotropicFilterExtension:Dynamic;
    public var highPrecisionShaderSupported:Bool;
    public var maxAnisotropy:Int;

    #if (!mobile && cpp)
    public var instancedArrays:IAMethods;
    #else
    public var instancedArrays:Dynamic;
    #end

    public var uintIndices:Null<Bool>;

    public function new() {

    }

}

and in Engine.hx I have this:

#if (!mobile && cpp)
this._caps.instancedArrays = { 
    vertexAttribDivisorANGLE: GL.getExtension('glVertexAttribDivisorARB'),
    drawElementsInstancedANGLE: GL.getExtension('glDrawElementsInstancedARB'),
    drawArraysInstancedANGLE: GL.getExtension('glDrawElementsInstancedARB')
};
#else
this._caps.instancedArrays = GL.getExtension('ANGLE_instanced_arrays');
#end

With this minimal changes I can keep the rest of the code untuched/same.

But yes, it doesn't work, instanced meshes are not rendered :(

What is very strange to me is that this line GL.getExtension('glVertexAttribDivisorARB') actually returns an object (and not null which I would expect), but if I try to trace its type it shows "TClass(null)". So I'm not sure if this is the right way to get to 'glVertexAttribDivisorARB' function but the fact that returned object is not null is really confusing. I wouldn't expect that gl function can be accessed this way. Even stranger is that the code runs, the app doesn't hang or crash...

Maybe you can get more info from lime authors. I'm afraid I can't help you with this :(

Aatu commented 8 years ago

Crap. Sorry to hear that:/