writeFmat function
Writes document back out, so a tool that edits a material can save it.
Round-trips: what this writes, readFmat reads back to an equal document.
That is the property a material editor stands on, and it is measured rather
than asserted — see fmat_test.dart.
Implementation
String writeFmat(MaterialDocument document) {
final surface = document.surface;
String? pathOf(TextureBinding? slot) =>
slot == null ||
slot.imageIndex < 0 ||
slot.imageIndex >= document.images.length
? null
: document.images[slot.imageIndex];
Object? slot(TextureBinding? binding) {
final path = pathOf(binding);
if (path == null) return null;
final sampling = binding!.sampling;
const plain = TextureSampling();
if (sampling.magLinear == plain.magLinear &&
sampling.minLinear == plain.minLinear &&
sampling.useMipmaps == plain.useMipmaps &&
sampling.mipLinear == plain.mipLinear &&
sampling.wrapS == plain.wrapS &&
sampling.wrapT == plain.wrapT) {
// A slot that asks for nothing unusual is written as the path alone,
// because that is what an artist writes by hand and what a diff should
// show when only the path changed.
return path;
}
return <String, Object?>{
'path': path,
if (!sampling.magLinear) 'magLinear': false,
if (!sampling.minLinear) 'minLinear': false,
if (!sampling.useMipmaps) 'mipmaps': false,
if (!sampling.mipLinear) 'mipLinear': false,
if (sampling.wrapS != TextureWrap.repeat) 'wrapS': sampling.wrapS.name,
if (sampling.wrapT != TextureWrap.repeat) 'wrapT': sampling.wrapT.name,
};
}
final textures = <String, Object?>{
if (slot(surface.baseColorTexture) case final Object value) 'albedo': value,
if (slot(surface.normalTexture) case final Object value) 'normal': value,
if (slot(surface.metallicRoughnessTexture) case final Object value)
'metallicRoughness': value,
if (slot(surface.occlusionTexture) case final Object value)
'occlusion': value,
if (slot(surface.emissiveTexture) case final Object value)
'emissive': value,
for (final entry in document.extraTextures.entries)
if (slot(entry.value) case final Object value) entry.key: value,
};
final lighting = document.lighting;
final json = const JsonEncoder.withIndent(' ').convert(<String, Object?>{
'fmat': kFmatVersion,
if (lighting != null) 'lighting': _writeLighting(lighting),
...surfaceMaterialToJson(surface),
if (textures.isNotEmpty) 'textures': textures,
if (surface.extensions case final layers?)
if (materialExtensionsToJson(layers, texture: slot) case final written
when written.isNotEmpty)
'extensions': written,
if (document.parameterBlock != 'MaterialParams')
'parameterBlock': document.parameterBlock,
if (document.parameters.isNotEmpty)
'parameters': <String, Object?>{
for (final entry in document.parameters.entries)
entry.key: <double>[for (final v in entry.value) _cleanFloat32(v)],
},
if (document.hints.isNotEmpty) 'hints': _writeHints(document.hints),
});
return '${_ensureFloatLiterals(json)}\n';
}