surfaceMaterialToJson function

Map<String, Object?> surfaceMaterialToJson(
  1. SurfaceMaterial surface
)

surface's own scalar and colour fields, keyed the way writeFmat nests them into a .fmat — everything but its texture slots, which surfaceMaterialFromJson takes as separate, already-resolved arguments rather than reading here.

doc-25's own remaining half, once mat-01 absorbed the rest: the one place these eleven field names and shapes are spelled out, so a material editor's own SetMaterialField and this format's own writer cannot drift the way two hand-written lists of the same fields always eventually do.

Texture slots stay out on purpose. SurfaceMaterial holds a TextureBinding for each, but a binding's own imageIndex only means anything against the MaterialDocument it came from — resolving a path to one, or interning a new one, is a whole document's business, not one material's, which is why readFmat still does that part itself before handing the result to surfaceMaterialFromJson.

Implementation

Map<String, Object?> surfaceMaterialToJson(SurfaceMaterial surface) =>
    <String, Object?>{
      if (surface.name != null) 'name': surface.name,
      'baseColor': <double>[
        _cleanFloat32(surface.baseColor.r),
        _cleanFloat32(surface.baseColor.g),
        _cleanFloat32(surface.baseColor.b),
        _cleanFloat32(surface.baseColor.a),
      ],
      if (surface.metallic != 0.0) 'metallic': surface.metallic,
      if (surface.roughness != 0.5) 'roughness': surface.roughness,
      if (surface.normalScale != 1.0) 'normalScale': surface.normalScale,
      if (surface.occlusionStrength != 1.0)
        'occlusionStrength': surface.occlusionStrength,
      if (surface.emissive.length2 != 0.0)
        'emissive': <double>[
          _cleanFloat32(surface.emissive.r),
          _cleanFloat32(surface.emissive.g),
          _cleanFloat32(surface.emissive.b),
        ],
      if (surface.emissiveStrength != 1.0)
        'emissiveStrength': surface.emissiveStrength,
      if (surface.alphaMode != SurfaceAlphaMode.opaque)
        'alphaMode': surface.alphaMode.name,
      if (surface.alphaCutoff != 0.5) 'alphaCutoff': surface.alphaCutoff,
      if (surface.doubleSided) 'doubleSided': true,
      if (surface.unlit) 'unlit': true,
      if (surface.lightingModel case final LightingModel model)
        'lightingModel': _writeLighting(model),
    };