canhorn / EventHorizon.Blazor.TypeScript.Interop.Generator

This project is a Blazor Interop C# Generator, has a sample against the BabylonJS library.
https://wonderful-pond-05f7b3b10.azurestaticapps.net/
MIT License
134 stars 21 forks source link

ShaderMaterial: How do you create a JS object based on an interface with no class implementation? #33

Open limefrogyank opened 3 years ago

limefrogyank commented 3 years ago

I tried adding ShaderMaterial to the generator project. However, one of the parameters for the constructor takes an IShaderMaterialOptions. That has no constructor in the Babylon library, it's just an interface. I have no idea how to use that via the generated classes... as far as I can tell, you need a typescript class implementing the interface.

limefrogyank commented 3 years ago

What I ended up doing is make a copy of the generated class for ShaderMaterial, then remove it from the generator, then modify the copied class in my own project to accept an object rather than the interface. The property accessors for options were changed to be similar to those for shaderPath.

I'm not sure if this will work, but it's not throwing any errors anymore.

limefrogyank commented 3 years ago

Ah, it works!

For those interested, I urge you to add "DebugLayer" to the generator classes and add this to any scene:

var debugLayer = new DebugLayer(scene);
            debugLayer.show();

It's nice to verify that the shaders have been loaded (even if they're not working as intended)

canhorn commented 3 years ago

Hello @limefrogyank,

Sorry for not getting back to you sooner on this issue! The generator should create an \<interface-name>CachedEntity that implements the interface. This generated CachedEntity does JsonConverter attributing, a plain object will bypass the caching layer, but in this case is probably not much of an issue.

Here is an example of a generated interface: IAudioEngine.cs

If you have any other issues or questions that would be appreciated!

And thanks for showing interest in this project!

badcommandorfilename commented 3 years ago

Hi @canhorn

How do you create a new instance of an interface-nameCachedEntity? If I look at https://github.com/canhorn/EventHorizon.Blazor.TypeScript.Interop.Generator/blob/7e927d2d8654e5f9237108a401f53e16a1f7c3e4/Sample/_generated/EventHorizon.Blazor.BabylonJS.WASM/IAudioEngine.cs#L223 it generates two constructors:

        public IAudioEngineCachedEntity() : base() { }

        public IAudioEngineCachedEntity(
            ICachedEntity entity
        ) : base(entity)
        {
        }

But neither of these actually calls EventHorizonBlazorInterop.New (e.g. Engine.cs)

and therefore __guid is always null.

When I try to set a property on a CachedEntity using EventHorizonBlazorInterop.Set it throws:

"TypeError: Cannot set properties of undefined ... at Object.set (http://localhost:5000/_content/EventHorizon.Blazor.Interop/interop-bridge.js:351:56)

canhorn commented 3 years ago

@badcommandorfilename I am still working on an pattern for the interface scenario, since these are interfaces they do not have constructors or concert implementation in the libraries. These are usually assumed to be json literals. But below I have a some work in progress C# and JavaScript, that creates an empty json object. And a C# method that can be used to correlate the JS and C# interface to the Cached Entity.

(I have not tested this fully)

function createEmptyObject() {
    return {};
}
public static IAudioEnginCachedEntity CreateNew() 
{
    return EventHorizonBlazorInterop.FuncClass(
        entity => new IAudioEnginCachedEntity(entity),
        new string[] { "createEmptyObject" }
    );
}

This will run the object through the caching layers giving it a GUID that will allow for it to be resolved, fixing the type error from the bridge.

I will see if I can get something into the generator or supporting library that will bake this into the implementations for the interface in C#.

badcommandorfilename commented 3 years ago

Thanks for confirming @canhorn !

I came up with a similar solution by shimming in:

            var entity = EventHorizonBlazorInterop.New(new string[] { "Object" }, null);
            ___guid = entity.___guid;

Into the parameterless constructors where needed.

If adding that to GenerateInteropClassStatement.cs seems like an acceptable solution I can make a PR, but if you've already got a fix in mind this is fine for me as a workaround.

I actually even tried just adding it to:

    public CachedEntityObject()
    {
        _invokableReference = DotNetObjectReference.Create(
            this
        );
        var entity = EventHorizonBlazorInterop.New(new string[] { "Object" }, null);
        ___guid = entity.___guid;
    }

And so far that hasn't broken anything (although it is probably allocating too many excess entity objects) =)