gltfio: various improvements to skinning normalization.
- Allow JavaScript and Java / Kotlin clients to configure this setting. - Opt in by default (this was disabled in #2621) - Optimize large assets like Bistro by iterating through prims only when the asset actually has skins. Fixes #2714.
This commit is contained in:
@@ -70,6 +70,9 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
val progress
|
||||
get() = resourceLoader.asyncGetLoadProgress()
|
||||
|
||||
var normalizeSkinningWeights = true
|
||||
var recomputeBoundingBoxes = false
|
||||
|
||||
val scene: Scene
|
||||
val view: View
|
||||
val camera: Camera
|
||||
@@ -101,7 +104,7 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
|
||||
view.camera = camera
|
||||
|
||||
assetLoader = AssetLoader(engine, MaterialProvider(engine), EntityManager.get())
|
||||
resourceLoader = ResourceLoader(engine, false, false)
|
||||
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes)
|
||||
|
||||
// Always add a direct light source since it is required for shadowing.
|
||||
// We highly recommend adding an indirect light as well.
|
||||
|
||||
@@ -296,7 +296,7 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
}
|
||||
asset->mResourcesLoaded = true;
|
||||
mPool->addAsset(asset);
|
||||
auto gltf = (cgltf_data*) asset->mSourceAsset;
|
||||
const cgltf_data* gltf = asset->mSourceAsset;
|
||||
cgltf_options options {};
|
||||
|
||||
// For emscripten and Android builds we have a custom implementation of cgltf_load_buffers which
|
||||
@@ -361,7 +361,7 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
#else
|
||||
|
||||
// Read data from the file system and base64 URIs.
|
||||
cgltf_result result = cgltf_load_buffers(&options, gltf, pImpl->mGltfPath.c_str());
|
||||
cgltf_result result = cgltf_load_buffers(&options, (cgltf_data*) gltf, pImpl->mGltfPath.c_str());
|
||||
if (result != cgltf_result_success) {
|
||||
slog.e << "Unable to load resources." << io::endl;
|
||||
return false;
|
||||
@@ -371,7 +371,7 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
SYSTRACE_NAME_END();
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (cgltf_validate(gltf) != cgltf_result_success) {
|
||||
if (cgltf_validate((cgltf_data*) gltf) != cgltf_result_success) {
|
||||
slog.e << "Failed cgltf validation." << io::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -381,13 +381,19 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
// tangent generation.
|
||||
decodeDracoMeshes(asset);
|
||||
|
||||
// To be robust against the glTF conformance suite, we optionally ensure that skinning weights
|
||||
// sum to 1.0 at every vertex. Note that if the same weights buffer is shared in multiple
|
||||
// places, this will needlessly repeat the work. In the future we would like to remove this
|
||||
// feature, and instead simply require correct models. See also:
|
||||
// https://github.com/KhronosGroup/glTF-Sample-Models/issues/215
|
||||
if (pImpl->mNormalizeSkinningWeights) {
|
||||
normalizeSkinningWeights(asset);
|
||||
// Normalize skinning weights, then "import" each skin into the asset by building a mapping of
|
||||
// skins to their affected entities.
|
||||
if (gltf->skins_count > 0) {
|
||||
if (pImpl->mNormalizeSkinningWeights) {
|
||||
normalizeSkinningWeights(asset);
|
||||
}
|
||||
if (asset->mInstances.empty()) {
|
||||
importSkins(gltf, asset->mNodeMap, asset->mSkins);
|
||||
} else {
|
||||
for (FFilamentInstance* instance : asset->mInstances) {
|
||||
importSkins(gltf, instance->nodeMap, instance->skins);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pImpl->mRecomputeBoundingBoxes) {
|
||||
@@ -425,16 +431,6 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
slot.indexBuffer->setBuffer(engine, std::move(bd));
|
||||
}
|
||||
|
||||
// For each skin, build a list of renderables that it affects.
|
||||
const cgltf_data* const_gltf = gltf;
|
||||
if (asset->mInstances.empty()) {
|
||||
importSkins(const_gltf, asset->mNodeMap, asset->mSkins);
|
||||
} else {
|
||||
for (FFilamentInstance* instance : asset->mInstances) {
|
||||
importSkins(const_gltf, instance->nodeMap, instance->skins);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply sparse data modifications to base arrays, then upload the result.
|
||||
applySparseData(asset);
|
||||
|
||||
|
||||
@@ -620,6 +620,7 @@ int main(int argc, char** argv) {
|
||||
configuration.engine = app.engine;
|
||||
configuration.gltfPath = gltfPath.c_str();
|
||||
configuration.recomputeBoundingBoxes = app.recomputeAabb;
|
||||
configuration.normalizeSkinningWeights = true;
|
||||
if (!app.resourceLoader) {
|
||||
app.resourceLoader = new gltfio::ResourceLoader(configuration);
|
||||
}
|
||||
|
||||
@@ -396,12 +396,20 @@ Filament.loadClassExtensions = function() {
|
||||
//
|
||||
// The optional asyncInterval argument allows clients to control how decoding is amortized
|
||||
// over time. It represents the number of milliseconds between each texture decoding task.
|
||||
//
|
||||
// The optional config argument is an object with boolean fields `normalizeSkinningWeights` and
|
||||
// `recomputeBoundingBoxes`.
|
||||
Filament.gltfio$FilamentAsset.prototype.loadResources = function(onDone, onFetched, basePath,
|
||||
asyncInterval) {
|
||||
asyncInterval, config) {
|
||||
const asset = this;
|
||||
const engine = this.getEngine();
|
||||
const names = this.getResourceUris();
|
||||
const interval = asyncInterval || 30;
|
||||
const defaults = {
|
||||
normalizeSkinningWeights: true,
|
||||
recomputeBoundingBoxes: false
|
||||
};
|
||||
config = Object.assign(defaults, config || {});
|
||||
|
||||
basePath = basePath || document.location;
|
||||
onFetched = onFetched || ((name) => {});
|
||||
@@ -420,7 +428,9 @@ Filament.loadClassExtensions = function() {
|
||||
}
|
||||
|
||||
// Construct a resource loader and start decoding after all textures are fetched.
|
||||
const resourceLoader = new Filament.gltfio$ResourceLoader(engine, false, false);
|
||||
const resourceLoader = new Filament.gltfio$ResourceLoader(engine,
|
||||
config.normalizeSkinningWeights,
|
||||
config.recomputeBoundingBoxes);
|
||||
const onComplete = () => {
|
||||
resourceLoader.asyncBeginLoad(asset);
|
||||
|
||||
|
||||
2
web/filament-js/filament.d.ts
vendored
2
web/filament-js/filament.d.ts
vendored
@@ -465,7 +465,7 @@ export class gltfio$AssetLoader {
|
||||
|
||||
export class gltfio$FilamentAsset {
|
||||
public loadResources(onDone: () => void|null, onFetched: (s: string) => void|null,
|
||||
basePath: string|null, asyncInterval: number|null): void;
|
||||
basePath: string|null, asyncInterval: number|null, options?: object): void;
|
||||
public getEntities(): Entity[];
|
||||
public getEntitiesByName(name: string): Entity[];
|
||||
public getEntityByName(name: string): Entity;
|
||||
|
||||
Reference in New Issue
Block a user