KhronosGroup / glTF-CSharp-Loader

C# Reference Loader for glTF
Other
213 stars 60 forks source link

[Discussion] Considering to add an F# example too #10

Closed realvictorprm closed 6 years ago

realvictorprm commented 6 years ago

I've played around with F# and the json files of glTF. Result is that I can generate easily a single support file which reduces manual work of schema mapping without loosing control.

For the source code see my gist here.


With F# it's a lot easier to parse glTF without loosing much flexibility. To achieve this it's needed to use a JsonTypeProvider plus a auto generated support file which provides both an abstract visitor class and schema classes depending on the specification.

The dependencies of the generated file just consist of FSharp.Data and the standard library. An example output of the generator would be:

module GeneratedSchemaDefinitions

open FSharp.Data
open System.IO

type AccessorSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\accessor.schema.json">
type AccessorSparseIndicesSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\accessor.sparse.indices.schema.json">
type AccessorSparseSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\accessor.sparse.schema.json">
type AccessorSparseValuesSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\accessor.sparse.values.schema.json">
type AnimationChannelSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\animation.channel.schema.json">
type AnimationChannelTargetSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\animation.channel.target.schema.json">
type AnimationSamplerSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\animation.sampler.schema.json">
type AnimationSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\animation.schema.json">
type AssetSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\asset.schema.json">
type BufferSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\buffer.schema.json">
type BufferViewSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\bufferView.schema.json">
type CameraOrthographicSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\camera.orthographic.schema.json">
type CameraPerspectiveSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\camera.perspective.schema.json">
type CameraSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\camera.schema.json">
type ExtensionSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\extension.schema.json">
type ExtrasSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\extras.schema.json">
type GlTFSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\glTF.schema.json">
type GlTFChildOfRootPropertySchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\glTFChildOfRootProperty.schema.json">
type GlTFidSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\glTFid.schema.json">
type GlTFPropertySchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\glTFProperty.schema.json">
type ImageSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\image.schema.json">
type MaterialNormalTextureInfoSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\material.normalTextureInfo.schema.json">
type MaterialOcclusionTextureInfoSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\material.occlusionTextureInfo.schema.json">
type MaterialPbrMetallicRoughnessSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\material.pbrMetallicRoughness.schema.json">
type MaterialSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\material.schema.json">
type MeshPrimitiveSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\mesh.primitive.schema.json">
type MeshSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\mesh.schema.json">
type NodeSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\node.schema.json">
type SamplerSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\sampler.schema.json">
type SceneSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\scene.schema.json">
type SkinSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\skin.schema.json">
type TextureSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\texture.schema.json">
type TextureInfoSchema = JsonProvider< @"E:\Development\glTF\specification\2.0\schema\textureInfo.schema.json">
[<AbstractClass>]
type Generator () =
    abstract member VisitAccessorSchema : AccessorSchema.Root -> unit
    abstract member VisitAccessorSparseIndicesSchema : AccessorSparseIndicesSchema.Root -> unit
    abstract member VisitAccessorSparseSchema : AccessorSparseSchema.Root -> unit
    abstract member VisitAccessorSparseValuesSchema : AccessorSparseValuesSchema.Root -> unit
    abstract member VisitAnimationChannelSchema : AnimationChannelSchema.Root -> unit
    abstract member VisitAnimationChannelTargetSchema : AnimationChannelTargetSchema.Root -> unit
    abstract member VisitAnimationSamplerSchema : AnimationSamplerSchema.Root -> unit
    abstract member VisitAnimationSchema : AnimationSchema.Root -> unit
    abstract member VisitAssetSchema : AssetSchema.Root -> unit
    abstract member VisitBufferSchema : BufferSchema.Root -> unit
    abstract member VisitBufferViewSchema : BufferViewSchema.Root -> unit
    abstract member VisitCameraOrthographicSchema : CameraOrthographicSchema.Root -> unit
    abstract member VisitCameraPerspectiveSchema : CameraPerspectiveSchema.Root -> unit
    abstract member VisitCameraSchema : CameraSchema.Root -> unit
    abstract member VisitExtensionSchema : ExtensionSchema.Root -> unit
    abstract member VisitExtrasSchema : ExtrasSchema.Root -> unit
    abstract member VisitGlTFSchema : GlTFSchema.Root -> unit
    abstract member VisitGlTFChildOfRootPropertySchema : GlTFChildOfRootPropertySchema.Root -> unit
    abstract member VisitGlTFidSchema : GlTFidSchema.Root -> unit
    abstract member VisitGlTFPropertySchema : GlTFPropertySchema.Root -> unit
    abstract member VisitImageSchema : ImageSchema.Root -> unit
    abstract member VisitMaterialNormalTextureInfoSchema : MaterialNormalTextureInfoSchema.Root -> unit
    abstract member VisitMaterialOcclusionTextureInfoSchema : MaterialOcclusionTextureInfoSchema.Root -> unit
    abstract member VisitMaterialPbrMetallicRoughnessSchema : MaterialPbrMetallicRoughnessSchema.Root -> unit
    abstract member VisitMaterialSchema : MaterialSchema.Root -> unit
    abstract member VisitMeshPrimitiveSchema : MeshPrimitiveSchema.Root -> unit
    abstract member VisitMeshSchema : MeshSchema.Root -> unit
    abstract member VisitNodeSchema : NodeSchema.Root -> unit
    abstract member VisitSamplerSchema : SamplerSchema.Root -> unit
    abstract member VisitSceneSchema : SceneSchema.Root -> unit
    abstract member VisitSkinSchema : SkinSchema.Root -> unit
    abstract member VisitTextureSchema : TextureSchema.Root -> unit
    abstract member VisitTextureInfoSchema : TextureInfoSchema.Root -> unit
    member self.VisitSchemaDir (pathToSchemaDir:string) =
        let accessorschema = Path.Combine(pathToSchemaDir, @"accessor.schema.json" ) |> AccessorSchema.Load
        let accessorsparseindicesschema = Path.Combine(pathToSchemaDir, @"accessor.sparse.indices.schema.json" ) |> AccessorSparseIndicesSchema.Load
        let accessorsparseschema = Path.Combine(pathToSchemaDir, @"accessor.sparse.schema.json" ) |> AccessorSparseSchema.Load
        let accessorsparsevaluesschema = Path.Combine(pathToSchemaDir, @"accessor.sparse.values.schema.json" ) |> AccessorSparseValuesSchema.Load
        let animationchannelschema = Path.Combine(pathToSchemaDir, @"animation.channel.schema.json" ) |> AnimationChannelSchema.Load
        let animationchanneltargetschema = Path.Combine(pathToSchemaDir, @"animation.channel.target.schema.json" ) |> AnimationChannelTargetSchema.Load
        let animationsamplerschema = Path.Combine(pathToSchemaDir, @"animation.sampler.schema.json" ) |> AnimationSamplerSchema.Load
        let animationschema = Path.Combine(pathToSchemaDir, @"animation.schema.json" ) |> AnimationSchema.Load
        let assetschema = Path.Combine(pathToSchemaDir, @"asset.schema.json" ) |> AssetSchema.Load
        let bufferschema = Path.Combine(pathToSchemaDir, @"buffer.schema.json" ) |> BufferSchema.Load
        let bufferviewschema = Path.Combine(pathToSchemaDir, @"bufferView.schema.json" ) |> BufferViewSchema.Load
        let cameraorthographicschema = Path.Combine(pathToSchemaDir, @"camera.orthographic.schema.json" ) |> CameraOrthographicSchema.Load
        let cameraperspectiveschema = Path.Combine(pathToSchemaDir, @"camera.perspective.schema.json" ) |> CameraPerspectiveSchema.Load
        let cameraschema = Path.Combine(pathToSchemaDir, @"camera.schema.json" ) |> CameraSchema.Load
        let extensionschema = Path.Combine(pathToSchemaDir, @"extension.schema.json" ) |> ExtensionSchema.Load
        let extrasschema = Path.Combine(pathToSchemaDir, @"extras.schema.json" ) |> ExtrasSchema.Load
        let gltfschema = Path.Combine(pathToSchemaDir, @"glTF.schema.json" ) |> GlTFSchema.Load
        let gltfchildofrootpropertyschema = Path.Combine(pathToSchemaDir, @"glTFChildOfRootProperty.schema.json" ) |> GlTFChildOfRootPropertySchema.Load
        let gltfidschema = Path.Combine(pathToSchemaDir, @"glTFid.schema.json" ) |> GlTFidSchema.Load
        let gltfpropertyschema = Path.Combine(pathToSchemaDir, @"glTFProperty.schema.json" ) |> GlTFPropertySchema.Load
        let imageschema = Path.Combine(pathToSchemaDir, @"image.schema.json" ) |> ImageSchema.Load
        let materialnormaltextureinfoschema = Path.Combine(pathToSchemaDir, @"material.normalTextureInfo.schema.json" ) |> MaterialNormalTextureInfoSchema.Load
        let materialocclusiontextureinfoschema = Path.Combine(pathToSchemaDir, @"material.occlusionTextureInfo.schema.json" ) |> MaterialOcclusionTextureInfoSchema.Load
        let materialpbrmetallicroughnessschema = Path.Combine(pathToSchemaDir, @"material.pbrMetallicRoughness.schema.json" ) |> MaterialPbrMetallicRoughnessSchema.Load
        let materialschema = Path.Combine(pathToSchemaDir, @"material.schema.json" ) |> MaterialSchema.Load
        let meshprimitiveschema = Path.Combine(pathToSchemaDir, @"mesh.primitive.schema.json" ) |> MeshPrimitiveSchema.Load
        let meshschema = Path.Combine(pathToSchemaDir, @"mesh.schema.json" ) |> MeshSchema.Load
        let nodeschema = Path.Combine(pathToSchemaDir, @"node.schema.json" ) |> NodeSchema.Load
        let samplerschema = Path.Combine(pathToSchemaDir, @"sampler.schema.json" ) |> SamplerSchema.Load
        let sceneschema = Path.Combine(pathToSchemaDir, @"scene.schema.json" ) |> SceneSchema.Load
        let skinschema = Path.Combine(pathToSchemaDir, @"skin.schema.json" ) |> SkinSchema.Load
        let textureschema = Path.Combine(pathToSchemaDir, @"texture.schema.json" ) |> TextureSchema.Load
        let textureinfoschema = Path.Combine(pathToSchemaDir, @"textureInfo.schema.json" ) |> TextureInfoSchema.Load
        self.VisitAccessorSchema accessorschema
        self.VisitAccessorSparseIndicesSchema accessorsparseindicesschema
        self.VisitAccessorSparseSchema accessorsparseschema
        self.VisitAccessorSparseValuesSchema accessorsparsevaluesschema
        self.VisitAnimationChannelSchema animationchannelschema
        self.VisitAnimationChannelTargetSchema animationchanneltargetschema
        self.VisitAnimationSamplerSchema animationsamplerschema
        self.VisitAnimationSchema animationschema
        self.VisitAssetSchema assetschema
        self.VisitBufferSchema bufferschema
        self.VisitBufferViewSchema bufferviewschema
        self.VisitCameraOrthographicSchema cameraorthographicschema
        self.VisitCameraPerspectiveSchema cameraperspectiveschema
        self.VisitCameraSchema cameraschema
        self.VisitExtensionSchema extensionschema
        self.VisitExtrasSchema extrasschema
        self.VisitGlTFSchema gltfschema
        self.VisitGlTFChildOfRootPropertySchema gltfchildofrootpropertyschema
        self.VisitGlTFidSchema gltfidschema
        self.VisitGlTFPropertySchema gltfpropertyschema
        self.VisitImageSchema imageschema
        self.VisitMaterialNormalTextureInfoSchema materialnormaltextureinfoschema
        self.VisitMaterialOcclusionTextureInfoSchema materialocclusiontextureinfoschema
        self.VisitMaterialPbrMetallicRoughnessSchema materialpbrmetallicroughnessschema
        self.VisitMaterialSchema materialschema
        self.VisitMeshPrimitiveSchema meshprimitiveschema
        self.VisitMeshSchema meshschema
        self.VisitNodeSchema nodeschema
        self.VisitSamplerSchema samplerschema
        self.VisitSceneSchema sceneschema
        self.VisitSkinSchema skinschema
        self.VisitTextureSchema textureschema
        self.VisitTextureInfoSchema textureinfoschema

now it would be just needed to inherit from the abstract class to get such sourcefile (currently unfilled).

type FSharpLoaderGenerator() =
    inherit GeneratedSchemaDefinitions.Generator()
    override __.VisitAccessorSchema schema =

        ()

    override __.VisitAccessorSparseIndicesSchema schema =

        ()

    override __.VisitAccessorSparseSchema schema =

        ()

    override __.VisitAccessorSparseValuesSchema schema =

        ()

    override __.VisitAnimationChannelSchema schema =

        ()

    override __.VisitAnimationChannelTargetSchema schema =

        ()

    override __.VisitAnimationSamplerSchema schema =

        ()

    override __.VisitAnimationSchema schema =

        ()

    override __.VisitAssetSchema schema =

        ()

    override __.VisitBufferSchema schema =

        ()

    override __.VisitBufferViewSchema schema =

        ()

    override __.VisitCameraOrthographicSchema schema =

        ()

    override __.VisitCameraPerspectiveSchema schema =

        ()

    override __.VisitCameraSchema schema =

        ()

    override __.VisitExtensionSchema schema =

        ()

    override __.VisitExtrasSchema schema =

        ()

    override __.VisitGlTFSchema schema =

        ()

    override __.VisitGlTFChildOfRootPropertySchema schema =

        ()

    override __.VisitGlTFidSchema schema =

        ()

    override __.VisitGlTFPropertySchema schema =

        ()

    override __.VisitImageSchema schema =

        ()

    override __.VisitMaterialNormalTextureInfoSchema schema =

        ()

    override __.VisitMaterialOcclusionTextureInfoSchema schema =

        ()

    override __.VisitMaterialPbrMetallicRoughnessSchema schema =

        ()

    override __.VisitMaterialSchema schema =

        ()

    override __.VisitMeshPrimitiveSchema schema =

        ()

    override __.VisitMeshSchema schema =

        ()

    override __.VisitNodeSchema schema =

        ()

    override __.VisitSamplerSchema schema =

        ()

    override __.VisitSceneSchema schema =

        ()

    override __.VisitSkinSchema schema =

        ()

    override __.VisitTextureSchema schema =

        ()

    override __.VisitTextureInfoSchema schema =

        ()

To start the generation people just need to call the VisitSchemaDir method with the path to the schema directory as parameter.


This was a proof of concept and also a showcase of how easily such stuff can be written in F# without even touching a single json file manually. My question is, what do you think? Would you like to see more implementation examples? (just note down here, although it's written in F# the visitor class can also be used for generating C# code).

realvictorprm commented 6 years ago

Side note, it was really fun to write this! :smile:

bghgary commented 6 years ago

I'm not familiar with F#. Are you saying we should consider using F# instead of C# to generate the C# code?

realvictorprm commented 6 years ago

I think F# could reduce the amount of needed code and aditionally adds safety.

It would be awesome if you would switch to F# in this repository. F# would definitely fit better here.

Currently I'm trying to rewrite the complete code of this repository in F#, I hope to be able to present it to you soon.

PS: Don't worry, C# compatibility persist with F#

realvictorprm commented 6 years ago

I'm still on it. Just a bit occupied with the F# compiler too.

realvictorprm commented 6 years ago

I'm delaying this, got too much to do. Sorry :-(