-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for KHR_materials_variants
in cesium-native
and implementations
#676
Comments
There are some possibly-relevant ideas about switching materials based on metadata in this Unreal issue: |
The linked issue sounds like it could go far beyond what is required for Other than that, I started creating a simple test data set. This is only intended as a DRAFT, while the technical questions are still being sorted out here. It is an asset that contains two glTF assets, each having material variants The tileset defines
There is a sandcastle that pragmatically extracts that metadata, and puts it into a UI, so that the variants can be selected: The exact behavior and some corner cases have to be clarified. For example: Will there only be either tileset-level metadata or group metadata (or could there be both, and how is that supposed to be handled?). Beyond that, there should also be a test data set where the glTF assets have different sets of material variants, just to see how this can be handled sensibly. |
A first, early draft PR is at #693 The current idea is to have a simple structure, called
This would then be obtained from the
This structure is filled from the metadata that is contained in the
NOTE: In the ZIP that is attached to the previous comment, the tileset only defined a single class with a single property for storing these material variants. With that alone, there is no way to determine whether someting is really a "material variant", or just some string array. Therefore, I think that it will be necessary to add a semantic to the property. For now, this is done with...
but the naming and other details still have to be sorted out. |
The approach for accessing the material variants information based on #709 is shown in #693 (comment) |
This was completed with #709 . (This only covers the The linked PR adds support for general metadata on the tileset level. The following is an example of how the material variants information can be accessed. It is wrapped into a small utility class for convenience here: struct MaterialVariants {
std::vector<std::string> tilesetMaterialVariantNames;
std::vector<std::vector<std::string>> groupsMaterialVariantNames;
};
class MaterialVariantsUtilities {
public:
static CesiumAsync::Future<MaterialVariants>
fromTileset(Cesium3DTilesSelection::Tileset &tileset) {
return tileset.loadMetadata().thenImmediately([](auto &&pMetadata)
-> MaterialVariants {
MaterialVariants materialVariants;
materialVariants.tilesetMaterialVariantNames =
MaterialVariantsUtilities::findStringPropertyValues(
*pMetadata->schema, *pMetadata->metadata,
MaterialVariantsUtilities::MATERIAL_VARIANTS_SEMANTIC_NAME);
for (const Cesium3DTiles::GroupMetadata &group : pMetadata->groups) {
materialVariants.groupsMaterialVariantNames.push_back(
MaterialVariantsUtilities::findStringPropertyValues(
*pMetadata->schema, group,
MaterialVariantsUtilities::MATERIAL_VARIANTS_SEMANTIC_NAME));
}
return materialVariants;
});
}
static void debugPrint(const MaterialVariants &materialVariants) {
std::cout << "Material variants:" << std::endl;
std::cout << " Tileset:" << std::endl;
const auto &t = materialVariants.tilesetMaterialVariantNames;
for (size_t i = 0; i < t.size(); i++) {
std::cout << " " << t[i] << std::endl;
}
std::cout << " Groups:" << std::endl;
const auto &gs = materialVariants.groupsMaterialVariantNames;
for (size_t i = 0; i < gs.size(); i++) {
std::cout << " Group " << i << ":" << std::endl;
const auto &g = gs[i];
for (size_t j = 0; j < g.size(); j++) {
std::cout << " " << g[j] << std::endl;
}
}
}
private:
static inline constexpr const char *MATERIAL_VARIANTS_SEMANTIC_NAME =
"TILESET_MATERIALS_VARIANTS_NAMES";
static std::vector<std::string>
findStringPropertyValues(const Cesium3DTiles::Schema &schema,
const Cesium3DTiles::MetadataEntity &metadataEntity,
const std::string &semantic) {
std::optional<Cesium3DTiles::FoundMetadataProperty> propertiesWithSemantic =
Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic(
schema, metadataEntity, semantic);
const CesiumUtility::JsonValue::Array &propertiesJson =
propertiesWithSemantic->propertyValue.getArray();
std::vector<std::string> propertyValues(propertiesJson.size());
std::transform(propertiesJson.begin(), propertiesJson.end(),
propertyValues.begin(),
[](const CesiumUtility::JsonValue &value) {
return value.getStringOrDefault("");
});
return propertyValues;
}
}; From a given auto &&future =
MaterialVariantsUtilities::fromTileset(tileset).thenImmediately(
[](MaterialVariants &&materialVariants) -> void {
MaterialVariantsUtilities::debugPrint(materialVariants);
}); (Note that this future will be resolved only after the tileset JSON has been fetched and processed. So it will be resolved after one of the Here are two very basic examples for testing - it is the same tileset and the same GLBs as above, once with a schema in the tileset, and once with an external schema: |
tl;dr Summary:
KHR_material_variants
extension allows different material configurations for glTF assets that can be selected at runtimecesium-unreal
andcesium-unity
Goal
KHR_material_variants
is a ratified glTF extension that allows defining different material variants for meshes:It should be possible to apply the concept of "material variants" to the more coarse-grained level of tilesets. The tileset should contain information about the variants that are expected to be present in the glTF assets that it refers to. Runtime engines should offer a mechanism to switch between these variants (i.e. the materials).
cesium-native
glTF levelThere already is very basic support for
KHR_materials_variants
inCesiumGltf
, as of #630 : This PR added the classes that are auto-generated from the schema, allowing to obtain the top-level information about the available variants, and the mapping between materials and variants for each primitive.cesium-native
tileset levelThere could be information about the avaialable material variants for a tileset, that summarizes the variants that are available in the glTF assets that are used as the tile content of the tileset.
In the most simple case, there could be an array of (unique) strings that match the names of the material variants that are supposed to be contained in the glTF assets. The user should then be able to select one of these material variants, and this variant should be activated for all glTF assets. When a glTF does not contain the respective variant name, then it should be rendered with its default material.
Some technical details still have to be sorted out. But the rough idea is to store the information about the variants as Tileset- or Group metadata. So the information could be stored as
Depending on the level of integration, there could be "convenience functions" for accessing this information. Specifically: It would be preferable if
cesium-native
offered an infrastructure where it is not necessary to manually search through the schema and check for the presence of certain properties and extract data from property tables, but just saystd::vector<str::string> variantNames = Magic.getThemFrom(tileset);
Where exactly the support for metadata in general (and the variants in particular) should be added still has to be decided.
Cesium3DTiles
Cesium3DTilesSelection
Runtime engine level
Once the basics have been sorted out, it has to be decided how runtime engines could process this information. Regardless of how the information is transported from the
tileset.json
into the runtime engine, the engine will have to offer things like a UI component for selecting the variant, and (and that may be the most challenging part) switch between different materials, for all tile contents that are currently loaded.Collecting approaches (and possible constraints) for the implementations in each runtime engine can be done here, and moved into issues of the respective engine implementations when the overall approach as been agreed on.
Support in
cesium-unreal
I started zooming into the relevant code paths, for the example of
cesium-unreal
. But this was really only a first pass. High level thoughts:IPrepareRendererResources
interfaceCesiumGltf::Model
instanance, and generates/fills the engine-specific representation (i.e. aUCesiumGltfComponent
)UCesiumGltfComponent
already has aBaseMaterial
, which is the engine-specificUMaterialInterface
instanceBaseMaterialWithWater
andBaseMaterialWithTranslucency
. This already seems to be some sort of a "material variants"...-> Maybe the material variants could be represented in a similar way?
The variants could be stored as an array of
UMaterialInterface
objects. TheCesium3DTileset
could have a method likesetVariant(std::string variant)
that activates the respective material for all the glTF assets. All this should be possible without reloading the whole tileset (otherwise it would defeat the purpose of the extension...), but ... I know that this can be tricky.The text was updated successfully, but these errors were encountered: