add GPU morpher + Dart API

This commit is contained in:
Nick Fisher
2021-09-17 13:45:41 +08:00
parent 8288758e78
commit 76f25ae7d5
25 changed files with 5736 additions and 44 deletions

View File

@@ -14,9 +14,8 @@
* limitations under the License.
*/
#include "FilamentViewer.hpp"
#include <filament/Camera.h>
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
@@ -37,6 +36,7 @@
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/Animator.h>
#include <camutils/Manipulator.h>
@@ -50,13 +50,9 @@
#include <image/KtxUtility.h>
#include <gltfio/Animator.h>
#include <chrono>
#include <iostream>
using namespace filament;
using namespace filament::math;
using namespace gltfio;
@@ -67,6 +63,10 @@ namespace filament {
class LightManager;
}
namespace gltfio {
MaterialProvider* createGPUMorphShaderLoader(const void* data, uint64_t size, Engine* engine);
}
namespace mimetic {
const double kNearPlane = 0.05; // 5 cm
@@ -76,10 +76,9 @@ const float kAperture = 16.0f;
const float kShutterSpeed = 1.0f / 125.0f;
const float kSensitivity = 100.0f;
MaterialProvider* createGPUShaderLoader(Engine* engine);
FilamentViewer::FilamentViewer(
void* layer,
const char* shaderPath,
LoadResource loadResource,
FreeResource freeResource) : _layer(layer),
_loadResource(loadResource),
@@ -99,8 +98,13 @@ FilamentViewer::FilamentViewer(
_swapChain = _engine->createSwapChain(_layer);
// _materialProvider = createGPUShaderLoader(_engine);
_materialProvider = createUbershaderLoader(_engine);
if(shaderPath) {
ResourceBuffer rb = _loadResource(shaderPath);
_materialProvider = createGPUMorphShaderLoader(rb.data, rb.size, _engine);
// _freeResource((void*)rb.data, rb.size, nullptr);
} else {
_materialProvider = createUbershaderLoader(_engine);
}
EntityManager& em = EntityManager::get();
_ncm = new NameComponentManager(em);
_assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em});
@@ -111,6 +115,7 @@ FilamentViewer::FilamentViewer(
Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT);
//Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT);
_asset = nullptr;
}
FilamentViewer::~FilamentViewer() {
@@ -138,23 +143,26 @@ void FilamentViewer::loadResources(string relativeResourcePath) {
}
_animator = _asset->getAnimator();
_asset->releaseSourceData();
// _asset->releaseSourceData();
_scene->addEntities(_asset->getEntities(), _asset->getEntityCount());
};
void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath) {
_resourceLoader->asyncCancelLoad();
_resourceLoader->evictResourceData();
void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath, const char* materialInstanceName) {
if(_asset) {
_resourceLoader->evictResourceData();
_scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
_assetLoader->destroyAsset(_asset);
}
_asset = nullptr;
_animator = nullptr;
ResourceBuffer rbuf = _loadResource(uri);
// Parse the glTF file and create Filament entities.
_asset = _assetLoader->createAssetFromJson((uint8_t*)rbuf.data, rbuf.size);
if (!_asset) {
std::cerr << "Unable to parse asset" << std::endl;
exit(1);
@@ -167,6 +175,11 @@ void FilamentViewer::loadGltf(const char* const uri, const char* const relativeR
transformToUnitCube();
startTime = std::chrono::high_resolution_clock::now();
}
void FilamentViewer::createMorpher(const char* meshName, const char* entityName, const char* materialInstanceName) {
morphHelper = new gltfio::GPUMorphHelper((FFilamentAsset*)_asset, meshName, entityName, materialInstanceName);
}
@@ -241,13 +254,12 @@ void FilamentViewer::render() {
_mainCamera->lookAt(eye, target, upward);
if(_animator) {
// typedef std::chrono::high_resolution_clock clock;
typedef std::chrono::duration<float, std::milli> duration;
/*typedef std::chrono::duration<float, std::milli> duration;
duration dur = std::chrono::high_resolution_clock::now() - startTime;
if (_animator->getAnimationCount() > 0) {
_animator->applyAnimation(0, dur.count() / 1000);
}
_animator->updateBoneMatrices();
_animator->updateBoneMatrices(); */
}
// Render the scene, unless the renderer wants to skip the frame.

View File

@@ -30,6 +30,8 @@
#include <string>
#include <chrono>
#include "GPUMorphHelper.h"
using namespace std;
using namespace filament;
using namespace filament::math;
@@ -51,13 +53,16 @@ namespace mimetic {
class FilamentViewer {
public:
FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource);
FilamentViewer(void* layer, const char* shaderPath, LoadResource loadResource, FreeResource freeResource);
~FilamentViewer();
void loadGltf(const char* const uri, const char* relativeResourcePath);
void loadGltf(const char* const uri, const char* relativeResourcePath, const char* materialInstanceName);
void loadSkybox(const char* const skyboxUri, const char* const iblUri);
void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
void render();
void createMorpher(const char* meshName, const char* entityName, const char* materialInstanceName);
Manipulator<float>* manipulator;
GPUMorphHelper* morphHelper;
private:
void loadResources(std::string relativeResourcePath);
void transformToUnitCube();
@@ -83,6 +88,7 @@ namespace mimetic {
FilamentAsset* _asset = nullptr;
NameComponentManager* _ncm;
Entity _sun;
Texture* _skyboxTexture;
Skybox* _skybox;

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_DEPENDENCY_GRAPH_H
#define GLTFIO_DEPENDENCY_GRAPH_H
#include <utils/Entity.h>
#include <tsl/robin_map.h>
#include <tsl/robin_set.h>
#include <queue>
#include <string>
namespace filament {
class MaterialInstance;
class Texture;
}
namespace gltfio {
/**
* Internal graph that enables FilamentAsset to discover "ready-to-render" entities by tracking
* the loading status of Texture objects that each entity depends on.
*
* Renderables connect to a set of material instances, which in turn connect to a set of parameter
* names, which in turn connect to a set of texture objects. These relationships are not easily
* inspectable using the Filament API or ECS.
*
* One graph corresponds to a single glTF asset. The graph only contains weak references, it does
* not have ownership over any Filament objects. Here's an example:
*
* Entity Entity Entity Entity
* | / \ | /
* | / \ | /
* Material Material Material
* / | \ |
* / | \ |
* Param Param Param Param
* \ / | |
* \ / | |
* Texture Texture Texture
*
* Note that the left-most entity in the above graph has no textures, so it becomes ready as soon as
* finalize is called.
*/
class DependencyGraph {
public:
using Material = filament::MaterialInstance;
using Entity = utils::Entity;
// Pops up to "count" ready-to-render entities off the queue.
// If "result" is non-null, returns the number of written items.
// If "result" is null, returns the number of available entities.
size_t popRenderables(Entity* result, size_t count) noexcept;
// These are called during the initial asset loader phase.
void addEdge(Entity entity, Material* material);
void addEdge(Material* material, const char* parameter);
// This is called at the end of the initial asset loading phase.
// Makes a guarantee that no new material nodes or parameter nodes will be added to the graph.
void finalize();
// This can be called after finalization to allow for dynamic addition of entities.
// It is slower than finalize() because it checks the readiness of existing materials.
void refinalize();
// These are called after textures have created and decoded.
void addEdge(filament::Texture* texture, Material* material, const char* parameter);
void markAsReady(filament::Texture* texture);
private:
struct TextureNode {
filament::Texture* texture;
bool ready;
};
struct MaterialNode {
tsl::robin_map<std::string, TextureNode*> params;
};
struct EntityNode {
tsl::robin_set<Material*> materials;
size_t numReadyMaterials = 0;
};
void checkReadiness(Material* material);
void markAsReady(Material* material);
TextureNode* getStatus(filament::Texture* texture);
// The following maps contain the directed edges in the graph.
tsl::robin_map<Entity, EntityNode> mEntityToMaterial;
tsl::robin_map<Material*, tsl::robin_set<Entity>> mMaterialToEntity;
tsl::robin_map<Material*, MaterialNode> mMaterialToTexture;
tsl::robin_map<filament::Texture*, tsl::robin_set<Material*>> mTextureToMaterial;
// Each texture (and its readiness flag) can be referenced from multiple nodes, so we own
// a collection of wrapper objects in the following map. This uses std::unique_ptr to allow
// nodes to refer to a texture wrapper using a stable weak pointer.
tsl::robin_map<filament::Texture*, std::unique_ptr<TextureNode>> mTextureNodes;
std::queue<Entity> mReadyRenderables;
bool mFinalized = false;
};
} // namespace gltfio
#endif // GLTFIO_DEPENDENCY_GRAPH_H

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_DRACO_CACHE_H
#define GLTFIO_DRACO_CACHE_H
#include <cgltf.h>
#include <tsl/robin_map.h>
#include <memory>
#ifndef GLTFIO_DRACO_SUPPORTED
#define GLTFIO_DRACO_SUPPORTED 0
#endif
namespace gltfio {
class DracoMesh;
// Manages a set of Draco meshes that can be looked up using cgltf_buffer_view.
//
// The cache key is the buffer view that holds the compressed data. This allows the loader to
// avoid duplicated work when a single Draco mesh is referenced from multiple primitives.
class DracoCache {
public:
DracoMesh* findOrCreateMesh(const cgltf_buffer_view* key);
private:
tsl::robin_map<const cgltf_buffer_view*, std::unique_ptr<DracoMesh>> mCache;
};
// Decodes a Draco mesh upon construction and retains the results.
//
// The DracoMesh API leverages cgltf accessor structs in a way that bears explanation. These are
// read / write parameters that tell the decoder where to write the decoded data, and what format
// is desired. The buffer_view in the accessor should be null unless decompressed data is already
// loaded. This tells the decoder that it should create a buffer_view and a buffer. The buffer
// view, the buffer, and the buffer's data are all automatically freed when DracoMesh is destroyed.
//
// Note that in the gltfio architecture, the AssetLoader has the job of constructing VertexBuffer
// objects while the ResourceLoader has the job of populating them asychronously. This means that
// our Draco decoder relies on the accessor fields being 100% correct. If we had to be robust
// against faulty accessor information, we would need to replace the VertexBuffer object that was
// created in the AssetLoader, which would be a messy process.
class DracoMesh {
public:
static DracoMesh* decode(const uint8_t* compressedData, size_t compressedSize);
bool getFaceIndices(cgltf_accessor* destination) const;
bool getVertexAttributes(uint32_t attributeId, cgltf_accessor* destination) const;
~DracoMesh();
private:
DracoMesh(struct DracoMeshDetails* details);
std::unique_ptr<struct DracoMeshDetails> mDetails;
};
} // namespace gltfio
#endif // GLTFIO_DRACO_CACHE_H

View File

@@ -0,0 +1,286 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_FFILAMENTASSET_H
#define GLTFIO_FFILAMENTASSET_H
#include <gltfio/FilamentAsset.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/MaterialInstance.h>
#include <filament/RenderableManager.h>
#include <filament/Texture.h>
#include <filament/TextureSampler.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <gltfio/MaterialProvider.h>
#include <math/mat4.h>
#include <utils/FixedCapacityVector.h>
#include <utils/CString.h>
#include <utils/Entity.h>
#include <cgltf.h>
#include "upcast.h"
#include "DependencyGraph.h"
#include "DracoCache.h"
#include "FFilamentInstance.h"
#include <tsl/robin_map.h>
#include <trie/htrie_map.h>
#include <vector>
#ifdef NDEBUG
#define GLTFIO_VERBOSE 0
#define GLTFIO_WARN(msg)
#else
#define GLTFIO_VERBOSE 1
#define GLTFIO_WARN(msg) slog.w << msg << io::endl
#endif
namespace utils {
class NameComponentManager;
class EntityManager;
}
namespace gltfio {
class Animator;
class Wireframe;
// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer().
struct BufferSlot {
const cgltf_accessor* accessor;
cgltf_attribute_type attribute;
int bufferIndex; // for vertex buffers only
int morphTarget; // 0 if no morphing, otherwise 1-based index
filament::VertexBuffer* vertexBuffer;
filament::IndexBuffer* indexBuffer;
};
// Encapsulates a connection between Texture and MaterialInstance.
struct TextureSlot {
const cgltf_texture* texture;
filament::MaterialInstance* materialInstance;
const char* materialParameter;
filament::TextureSampler sampler;
bool srgb;
};
// MeshCache
// ---------
// If a given glTF mesh is referenced by multiple glTF nodes, then it generates a separate Filament
// renderable for each of those nodes. All renderables generated by a given mesh share a common set
// of VertexBuffer and IndexBuffer objects. To achieve the sharing behavior, the loader maintains a
// small cache. The cache keys are glTF mesh definitions and the cache entries are lists of
// primitives, where a "primitive" is a reference to a Filament VertexBuffer and IndexBuffer.
struct Primitive {
filament::VertexBuffer* vertices = nullptr;
filament::IndexBuffer* indices = nullptr;
filament::Aabb aabb; // object-space bounding box
UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes)
uint8_t morphPositions[4] = {}; // Buffer indices for MORPH_POSITION_0, MORPH_POSITION_1 etc.
uint8_t morphTangents[4] = {}; // Buffer indices for MORPH_TANGENTS_0, MORPH_TANGENTS_1, etc.
};
using MeshCache = tsl::robin_map<const cgltf_mesh*, std::vector<Primitive>>;
// MatInstanceCache
// ----------------
// Each glTF material definition corresponds to a single filament::MaterialInstance, which are
// temporarily cached during loading. The filament::Material objects that are used to create instances are
// cached in MaterialProvider. If a given glTF material is referenced by multiple glTF meshes, then
// their corresponding filament primitives will share the same Filament MaterialInstance and UvMap.
// The UvMap is a mapping from each texcoord slot in glTF to one of Filament's 2 texcoord sets.
struct MaterialEntry {
filament::MaterialInstance* instance;
UvMap uvmap;
};
using MatInstanceCache = tsl::robin_map<intptr_t, MaterialEntry>;
struct FFilamentAsset : public FilamentAsset {
FFilamentAsset(filament::Engine* engine, utils::NameComponentManager* names,
utils::EntityManager* entityManager, const cgltf_data* srcAsset) :
mEngine(engine), mNameManager(names), mEntityManager(entityManager) {
mSourceAsset.reset(new SourceAsset {(cgltf_data*)srcAsset});
}
~FFilamentAsset();
size_t getEntityCount() const noexcept {
return mEntities.size();
}
const utils::Entity* getEntities() const noexcept {
return mEntities.empty() ? nullptr : mEntities.data();
}
const utils::Entity* getLightEntities() const noexcept {
return mLightEntities.empty() ? nullptr : mLightEntities.data();
}
size_t getLightEntityCount() const noexcept {
return mLightEntities.size();
}
const utils::Entity* getCameraEntities() const noexcept {
return mCameraEntities.empty() ? nullptr : mCameraEntities.data();
}
size_t getCameraEntityCount() const noexcept {
return mCameraEntities.size();
}
utils::Entity getRoot() const noexcept {
return mRoot;
}
size_t popRenderables(utils::Entity* entities, size_t count) noexcept {
return mDependencyGraph.popRenderables(entities, count);
}
size_t getMaterialInstanceCount() const noexcept {
return mMaterialInstances.size();
}
const filament::MaterialInstance* const* getMaterialInstances() const noexcept {
return mMaterialInstances.data();
}
filament::MaterialInstance* const* getMaterialInstances() noexcept {
return mMaterialInstances.data();
}
size_t getResourceUriCount() const noexcept {
return mResourceUris.size();
}
const char* const* getResourceUris() const noexcept {
return mResourceUris.data();
}
filament::Aabb getBoundingBox() const noexcept {
return mBoundingBox;
}
const char* getName(utils::Entity entity) const noexcept;
const char* getExtras(utils::Entity entity) const noexcept;
utils::Entity getFirstEntityByName(const char* name) noexcept;
size_t getEntitiesByName(const char* name, utils::Entity* entities,
size_t maxCount) const noexcept;
size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities,
size_t maxCount) const noexcept;
Animator* getAnimator() noexcept;
utils::Entity getWireframe() noexcept;
filament::Engine* getEngine() const noexcept {
return mEngine;
}
void releaseSourceData() noexcept;
const void* getSourceAsset() const noexcept {
return mSourceAsset.get() ? mSourceAsset->hierarchy : nullptr;
}
FilamentInstance** getAssetInstances() noexcept {
return (FilamentInstance**) mInstances.data();
}
size_t getAssetInstanceCount() const noexcept {
return mInstances.size();
}
void takeOwnership(filament::Texture* texture) {
mTextures.push_back(texture);
}
void bindTexture(const TextureSlot& tb, filament::Texture* texture) {
tb.materialInstance->setParameter(tb.materialParameter, texture, tb.sampler);
mDependencyGraph.addEdge(texture, tb.materialInstance, tb.materialParameter);
}
bool isInstanced() const {
return mInstances.size() > 0;
}
filament::Engine* mEngine;
utils::NameComponentManager* mNameManager;
utils::EntityManager* mEntityManager;
std::vector<utils::Entity> mEntities;
std::vector<utils::Entity> mLightEntities;
std::vector<utils::Entity> mCameraEntities;
std::vector<filament::MaterialInstance*> mMaterialInstances;
std::vector<filament::VertexBuffer*> mVertexBuffers;
std::vector<filament::BufferObject*> mBufferObjects;
std::vector<filament::IndexBuffer*> mIndexBuffers;
std::vector<filament::Texture*> mTextures;
filament::Aabb mBoundingBox;
utils::Entity mRoot;
std::vector<FFilamentInstance*> mInstances;
SkinVector mSkins; // unused for instanced assets
Animator* mAnimator = nullptr;
Wireframe* mWireframe = nullptr;
bool mResourcesLoaded = false;
DependencyGraph mDependencyGraph;
tsl::htrie_map<char, std::vector<utils::Entity>> mNameToEntity;
tsl::robin_map<utils::Entity, utils::CString> mNodeExtras;
utils::CString mAssetExtras;
// Sentinels for situations where ResourceLoader needs to generate data.
const cgltf_accessor mGenerateNormals = {};
const cgltf_accessor mGenerateTangents = {};
// Encapsulates reference-counted source data, which includes the cgltf hierachy
// and potentially also includes buffer data that can be uploaded to the GPU.
struct SourceAsset {
~SourceAsset() { cgltf_free(hierarchy); }
cgltf_data* hierarchy;
DracoCache dracoCache;
utils::FixedCapacityVector<uint8_t> glbData;
};
// We used shared ownership for the raw cgltf data in order to permit ResourceLoader to
// complete various asynchronous work (e.g. uploading buffers to the GPU) even after the asset
// or ResourceLoader have been destroyed.
using SourceHandle = std::shared_ptr<SourceAsset>;
SourceHandle mSourceAsset;
// Transient source data that can freed via releaseSourceData:
std::vector<BufferSlot> mBufferSlots;
std::vector<TextureSlot> mTextureSlots;
std::vector<const char*> mResourceUris;
NodeMap mNodeMap; // unused for instanced assets
std::vector<std::pair<const cgltf_primitive*, filament::VertexBuffer*> > mPrimitives;
MatInstanceCache mMatInstanceCache;
MeshCache mMeshCache;
};
FILAMENT_UPCAST(FilamentAsset)
} // namespace gltfio
#endif // GLTFIO_FFILAMENTASSET_H

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_FFILAMENTINSTANCE_H
#define GLTFIO_FFILAMENTINSTANCE_H
#include <gltfio/FilamentInstance.h>
#include <utils/Entity.h>
#include <math/mat4.h>
#include <tsl/robin_map.h>
#include <string>
#include <vector>
#include "upcast.h"
struct cgltf_node;
namespace gltfio {
struct FFilamentAsset;
class Animator;
struct Skin {
std::string name;
std::vector<filament::math::mat4f> inverseBindMatrices;
std::vector<utils::Entity> joints;
std::vector<utils::Entity> targets;
};
using SkinVector = std::vector<Skin>;
using NodeMap = tsl::robin_map<const cgltf_node*, utils::Entity>;
struct FFilamentInstance : public FilamentInstance {
std::vector<utils::Entity> entities;
utils::Entity root;
Animator* animator;
FFilamentAsset* owner;
SkinVector skins;
NodeMap nodeMap;
Animator* getAnimator() noexcept;
};
FILAMENT_UPCAST(FilamentInstance)
} // namespace gltfio
#endif // GLTFIO_FFILAMENTINSTANCE_H

View File

@@ -0,0 +1,368 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GPUMorphHelper.h"
#include <backend/BufferDescriptor.h>
#include <filament/BufferObject.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filamat/Package.h>
#include <filamat/MaterialBuilder.h>
#include <filament/Color.h>
#include "GltfEnums.h"
#include "TangentsJob.h"
#include <iostream>
#include <math.h>
using namespace filament;
using namespace filamat;
using namespace filament::math;
using namespace utils;
namespace gltfio {
static constexpr uint8_t kUnused = 0xff;
uint32_t computeBindingSize(const cgltf_accessor *accessor);
uint32_t computeBindingSize(const cgltf_accessor *accessor);
uint32_t computeBindingOffset(const cgltf_accessor *accessor);
static const auto FREE_CALLBACK = [](void *mem, size_t s, void *) {
free(mem);
};
GPUMorphHelper::GPUMorphHelper(FFilamentAsset *asset, const char* meshName, const char* entityName, const char* materialInstanceName) : mAsset(asset) {
cgltf_size num_primitives = 0;
NodeMap &sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap
: asset->mNodeMap;
for (auto pair : sourceNodes) {
cgltf_node const *node = pair.first;
cgltf_mesh const *mesh = node->mesh;
if (mesh) {
std::cout << "Mesh " << mesh->name <<std::endl;
if(strcmp(meshName, mesh->name) == 0) {
targetMesh = mesh;
num_primitives = mesh->primitives_count;
for (cgltf_size pi = 0, count = mesh->primitives_count; pi < count; ++pi) {
addPrimitive(mesh, pi, &mMorphTable[pair.second]);
}
}
}
}
createTextures();
}
GPUMorphHelper::~GPUMorphHelper() {
}
///
/// Creates the texture that will store the morphable attributes. The texture will be sized according to the total number of vertices in the mesh, meaning all primitives share the same texture.
///
void GPUMorphHelper::createTextures() {
auto materialInstances = mAsset->getMaterialInstances();
auto &engine = *(mAsset->mEngine);
for (auto &entry : mMorphTable) {
for (auto prim : entry.second.primitives) {
// for a single morph target, each vertex will be assigned 2 pixels, corresponding to a position vec3 and a normal vec3
// these two vectors will be laid out adjacent in memory
// the total texture "width" is the total number of these pixels
// morph targets are then assigned to the depth channel
auto textureWidth = prim.numVertices * 2;
// the total size of the texture in bytes
// equal to (numVertices * numAttributes * vectorSize (3) * sizeof(float) * numMorphTargets)
auto textureSize = textureWidth * 3 * sizeof(float) * prim.numTargets;
auto textureBuffer = (float *const) malloc(textureSize);
if(!textureBuffer) {
std::cout << "Error allocating texture buffer" << std::endl;
exit(-1);
}
uint32_t offset = 0;
// assume the primitive morph target source buffer is laid out like:
// |target0_v0_pos * 3|target0_v0_norm * 3|target0_v1_pos * 3|target0_v1_norm * 3|...|target1_v0_pos * 3|target1_v0_norm * 3|target1_v1_pos * 3|target1_v1_norm * 3|...
// where:
// - target0/target1/etc is the first/second/etc morph target
// - v0/v1/etc is the first/second/etc vertex
// - pos/norm are each 3-float vectors
for (auto &target : prim.targets) {
if(target.type == cgltf_attribute_type_position
|| target.type == cgltf_attribute_type_normal
) {
memcpy(textureBuffer+offset, target.bufferObject, target.bufferSize);
offset += int(target.bufferSize / sizeof(float));
}
}
prim.texture = Texture::Builder()
.width(textureWidth) //
.height(1)
.depth(prim.numTargets)
.sampler(Texture::Sampler::SAMPLER_2D_ARRAY)
.format(Texture::InternalFormat::RGB32F)
.levels(0x01)
.build(engine);
Texture::PixelBufferDescriptor descriptor(
textureBuffer,
textureSize,
Texture::Format::RGB,
Texture::Type::FLOAT,
FREE_CALLBACK,
nullptr);
prim.texture->setImage(engine, 0, 0,0, 0, textureWidth, 1, prim.numTargets, std::move(descriptor));
for(int i = 0; i < mAsset->getMaterialInstanceCount(); i++) {
const char* name = materialInstances[i]->getName();
std::cout << name << std::endl;
if(strcmp(name, prim.materialName) == 0) {
prim.materialInstance = materialInstances[i];
break;
}
}
if(!prim.materialInstance) {
exit(-1);
}
// this won't work if material instance is shared between primitives?
prim.materialInstance->setParameter("dimensions", filament::math::int3 { prim.numVertices * 2, numAttributes, prim.numTargets });
prim.materialInstance->setParameter("morphTargets", prim.texture, TextureSampler());
float weights[prim.numTargets];
memset(weights, 0, prim.numTargets * sizeof(float));
prim.materialInstance->setParameter("morphTargetWeights", weights, prim.numTargets);
}
}
}
void GPUMorphHelper::applyWeights(float const *weights, size_t count, int primitiveIndex) noexcept {
auto materialInstance = mAsset->getMaterialInstances()[primitiveIndex];
materialInstance->setParameter("morphTargetWeights", weights, count);
// assert(count <= numTargets);
}
void
GPUMorphHelper::addPrimitive(cgltf_mesh const *mesh, int primitiveIndex, TableEntry *entry) {
auto &engine = *mAsset->mEngine;
const cgltf_primitive &prim = mesh->primitives[primitiveIndex];
const auto &gltfioPrim = mAsset->mMeshCache.at(mesh)[primitiveIndex];
VertexBuffer *vertexBuffer = gltfioPrim.vertices;
entry->primitives.push_back({vertexBuffer});
auto &morphHelperPrim = entry->primitives.back();
morphHelperPrim.materialName = prim.material->name;
morphHelperPrim.numTargets = prim.targets_count;
morphHelperPrim.numVertices = vertexBuffer->getVertexCount();
cgltf_size maxIndex = 0;
for(int i = 0; i < prim.indices->count; i++) {
maxIndex = std::max(cgltf_accessor_read_index(prim.indices, i), maxIndex);
}
std::cout << "Max index for primitive index " << primitiveIndex << " is " << maxIndex << " and numVertices was " << morphHelperPrim.numVertices << std::endl;
const cgltf_accessor *previous = nullptr;
// for this primitive, iterate over every target
for (int targetIndex = 0; targetIndex < prim.targets_count; targetIndex++) {
const cgltf_morph_target &morphTarget = prim.targets[targetIndex];
for (cgltf_size aindex = 0; aindex < morphTarget.attributes_count; aindex++) {
const cgltf_attribute &attribute = morphTarget.attributes[aindex];
const cgltf_accessor *accessor = attribute.data;
const cgltf_attribute_type atype = attribute.type;
if (atype == cgltf_attribute_type_tangent) {
continue;
}
if (
atype == cgltf_attribute_type_normal ||
atype == cgltf_attribute_type_position
) {
//
// the texture needs to be sized according to the total number of vertices in the mesh
// this is identified by the highest vertex index of all primitives in the mesh
//
// if(numVertices == 0)
// numVertices = accessor->count;
//assert(numVertices == accessor->count);
// All position & normal attributes must have the same data type.
assert_invariant(
!previous || previous->component_type == accessor->component_type);
assert_invariant(!previous || previous->type == accessor->type);
previous = accessor;
// This should always be non-null, but don't crash if the glTF is malformed.
if (accessor->buffer_view) {
auto bufferData = (const uint8_t *) accessor->buffer_view->buffer->data;
assert_invariant(bufferData);
const uint8_t *data = computeBindingOffset(accessor) + bufferData;
const uint32_t size = computeBindingSize(accessor);
morphHelperPrim.targets.push_back({data, size, targetIndex, atype});
}
}
}
}
}
}
//VertexBuffer* vBuf = VertexBuffer::Builder()
// .vertexCount(numVertices)
// .bufferCount(numPrimitives)
// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT4, 0)
// .build(*engine);
//numIndices = maxIndex+1; */
//numIndices = prim.indices->count;
/*indicesBuffer = (uint32_t*)malloc(sizeof(unsigned int) * prim.indices->count);
//materialInstance->setParameter("vertexIndices", indicesBuffer, numIndices);
//.require(VertexAttribute::UV0)
//.require(MaterialBuilder.VertexAttribute.CUSTOM0)
//MaterialBuilder::init();
//MaterialBuilder builder = MaterialBuilder()
// .name("DefaultMaterial")
// .platform(MaterialBuilder::Platform::MOBILE)
// .targetApi(MaterialBuilder::TargetApi::ALL)
// .optimization(MaterialBuilderBase::Optimization::NONE)
// .shading(MaterialBuilder::Shading::LIT)
// .parameter(MaterialBuilder::UniformType::FLOAT3, "baseColor")
// .parameter(MaterialBuilder::UniformType::INT3, "dimensions")
// .parameter(MaterialBuilder::UniformType::FLOAT, numTargets, MaterialBuilder::ParameterPrecision::DEFAULT, "morphTargetWeights")
// .parameter(MaterialBuilder::SamplerType::SAMPLER_2D_ARRAY, MaterialBuilder::SamplerFormat::FLOAT, MaterialBuilder::ParameterPrecision::DEFAULT, "morphTargets")
// .vertexDomain(VertexDomain::WORLD)
// .material(R"SHADER(void material(inout MaterialInputs material) {
// prepareMaterial(material);
// material.baseColor.rgb = materialParams.baseColor;
// })SHADER")
// .materialVertex(R"SHADER(
// vec3 getMorphTarget(int vertexIndex, int morphTargetIndex) {
// // our texture is laid out as (x,y,z) where y is 1, z is the number of morph targets, and x is the number of vertices * 2 (multiplication accounts for position + normal)
// // UV coordinates are normalized to (-1,1), so we divide the current vertex index by the total number of vertices to find the correct coordinate for this vertex
// vec3 uv = vec3(
// (float(vertexIndex) + 0.5) / float(materialParams.dimensions.x),
// 0.0f,
// //(float(morphTargetIndex) + 0.5f) / float(materialParams.dimensions.z));
// float(morphTargetIndex));
// return texture(materialParams_morphTargets, uv).xyz;
// }
//
// void materialVertex(inout MaterialVertexInputs material) {
// return;
// // for every morph target
// for(int morphTargetIndex = 0; morphTargetIndex < materialParams.dimensions.z; morphTargetIndex++) {
//
// // get the weight to apply
// float weight = materialParams.morphTargetWeights[morphTargetIndex];
//
// // get the ID of this vertex, which will be the x-offset of the position attribute in the texture sampler
// int vertexId = getVertexIndex();
//
// // get the position of the target for this vertex
// vec3 morphTargetPosition = getMorphTarget(vertexId, morphTargetIndex);
// // update the world position of this vertex
// material.worldPosition.xyz += (weight * morphTargetPosition);
//
// // increment the vertexID by half the size of the texture to get the x-offset of the normal (all positions stored in the first half, all normals stored in the second half)
//
// vertexId += (materialParams.dimensions.x / 2);
//
// // get the normal of this target for this vertex
// vec3 morphTargetNormal = getMorphTarget(vertexId, morphTargetIndex);
// material.worldNormal += (weight * morphTargetNormal);
// }
// mat4 transform = getWorldFromModelMatrix();
// material.worldPosition = mulMat4x4Float3(transform, material.worldPosition.xyz);
// })SHADER");
//
//Package pkg = builder.build(mAsset->mEngine->getJobSystem());
//Material* material = Material::Builder().package(pkg.getData(), pkg.getSize())
// .build(*mAsset->mEngine);
//size_t normal_size = sizeof(short4);
//assert(textureWidth * (position_size + normal_size) == textureSize);
//assert(textureWidth * position_size == textureSize);
/*__android_log_print(ANDROID_LOG_INFO, "MyTag", "Expected size %d width at level 0 %d height", Texture::PixelBufferDescriptor::computeDataSize(Texture::Format::RGB,
Texture::Type::FLOAT, 24, 1, 4), texture->getWidth(0),texture->getHeight(0)); */
/* Texture::PixelBufferDescriptor descriptor(
textureBuffer,
textureSize,
Texture::Format::RGB,
Texture::Type::FLOAT,
4, 0,0, 24,
FREE_CALLBACK,
nullptr); */
/*for(int i = 0; i < int(textureSize / sizeof(float)); i++) {
__android_log_print(ANDROID_LOG_INFO, "MyTag", "offset %d %f", i, *(textureBuffer+i));
//}*/
//std::cout << "Checking for " << materialInstanceName << std::endl;
//if(materialInstanceName) {
// for(int i = 0; i < asset->getMaterialInstanceCount(); i++) {
// const char* name = instances[i]->getName();
// std::cout << name << std::endl;
// if(strcmp(name, materialInstanceName) == 0) {
// materialInstance = instances[i];
// break;
// }
// }
//} else {
// materialInstance = instances[0];
//}
//
//if(!materialInstance) {
// exit(-1);
//}
// std::cout << std::endl;
/* for (int i = 0; i < 4; i++) {
morphHelperPrim.positions[i] = gltfioPrim.morphPositions[i];
morphHelperPrim.tangents[i] = gltfioPrim.morphTangents[i];
} */
// applyTextures(materialInstance);
/* const Entity* entities = mAsset->getEntities();
for(int i=0; i < mAsset->getEntityCount();i++) {
std::cout << mAsset->getName(entities[i]);
} */
// Entity entity = mAsset->getFirstEntityByName(entityName);
// RenderableManager::Instance rInst = mAsset->mEngine->getRenderableManager().getInstance(entity);
// for(int i = 0; i<num_primitives;i++) {
// mAsset->mEngine->getRenderableManager().setMaterialInstanceAt(rInst, i, materialInstance);
// }

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FFilamentAsset.h"
#include "FFilamentInstance.h"
#include "filament/Texture.h"
#include "filament/Engine.h"
#include <math/vec4.h>
#include <tsl/robin_map.h>
#include <vector>
using namespace filament;
struct cgltf_node;
struct cgltf_mesh;
struct cgltf_primitive;
namespace gltfio {
///
/// A GPUMorphHelper instance can be created per mesh (this avoids creating textures for meshes that do not require animation).
/// For each primitive in the mesh, a texture is created to store the target positions and normals.
/// The texture is laid out as x * 1 * z, where z is the number of morph targets and x is the number of vertices for the primitive.
/// A MaterialInstance is created for each primitive, then applied to the entity identified by entityName.
///
class GPUMorphHelper {
public:
using Entity = utils::Entity;
GPUMorphHelper(FFilamentAsset *asset, const char* meshName, const char* entityName, const char* materialInstanceName);
~GPUMorphHelper();
void applyWeights(float const *weights, size_t count, int primitiveIndex) noexcept;
private:
struct GltfTarget {
const void *bufferObject;
uint32_t bufferSize;
int morphTargetIndex;
cgltf_attribute_type type;
};
struct GltfPrimitive {
filament::VertexBuffer *vertexBuffer;
Texture* texture;
std::vector <GltfTarget> targets; // TODO: flatten this?
const char* materialName;
cgltf_size numTargets = 0;
cgltf_size numVertices = 0;
MaterialInstance* materialInstance = nullptr;
};
struct TableEntry {
std::vector <GltfPrimitive> primitives; // TODO: flatten this?
};
int numAttributes = 2; // position & normal
uint32_t* indicesBuffer = nullptr;
void addPrimitive(cgltf_mesh const *mesh, int primitiveIndex, TableEntry *entry);
void createTextures();
cgltf_mesh const* targetMesh;
tsl::robin_map <Entity, TableEntry> mMorphTable;
FFilamentAsset *mAsset;
};
}

View File

@@ -0,0 +1,235 @@
#include <gltfio/MaterialProvider.h>
#include <filamat/MaterialBuilder.h>
#include <filament/MaterialInstance.h>
#include <filament/Texture.h>
#include <filament/TextureSampler.h>
#include <math/mat4.h>
#include <utils/Log.h>
#include "gltfio/resources/gltfresources_lite.h"
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace utils;
namespace {
using CullingMode = MaterialInstance::CullingMode;
class GPUMorphShaderLoader : public MaterialProvider {
public:
GPUMorphShaderLoader(const void* mData, uint64_t size, filament::Engine* engine);
~GPUMorphShaderLoader() {}
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) override;
size_t getMaterialsCount() const noexcept override;
const Material* const* getMaterials() const noexcept override;
void destroyMaterials() override;
bool needsDummyData(VertexAttribute attrib) const noexcept override {
switch (attrib) {
case VertexAttribute::UV0:
case VertexAttribute::UV1:
case VertexAttribute::COLOR:
return true;
default:
return false;
}
}
const void* mData;
const size_t mSize;
Material* getMaterial(const MaterialKey& config) const;
enum ShadingMode {
UNLIT = 0,
LIT = 1,
SPECULAR_GLOSSINESS = 2,
};
mutable Material* mMaterials[12] = {};
Texture* mDummyTexture = nullptr;
Engine* mEngine;
};
GPUMorphShaderLoader::GPUMorphShaderLoader(const void* data, uint64_t size, Engine* engine) : mData(data), mSize(size), mEngine(engine) {
unsigned char texels[4] = {};
mDummyTexture = Texture::Builder()
.width(1).height(1)
.format(Texture::InternalFormat::RGBA8)
.build(*mEngine);
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
Texture::Type::UBYTE);
mDummyTexture->setImage(*mEngine, 0, std::move(pbd));
}
size_t GPUMorphShaderLoader::getMaterialsCount() const noexcept {
return sizeof(mMaterials) / sizeof(mMaterials[0]);
}
const Material* const* GPUMorphShaderLoader::getMaterials() const noexcept {
return &mMaterials[0];
}
void GPUMorphShaderLoader::destroyMaterials() {
for (auto& material : mMaterials) {
mEngine->destroy(material);
material = nullptr;
}
mEngine->destroy(mDummyTexture);
}
Material* GPUMorphShaderLoader::getMaterial(const MaterialKey& config) const {
const ShadingMode shading = config.unlit ? UNLIT :
(config.useSpecularGlossiness ? SPECULAR_GLOSSINESS : LIT);
const int matindex = 0;
if (mMaterials[matindex] != nullptr) {
return mMaterials[matindex];
}
filamat::Package pkg = filamat::Package(mData, mSize);
return Material::Builder().package(pkg.getData(), pkg.getSize()).build(*mEngine);
}
MaterialInstance* GPUMorphShaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) {
// Diagnostics are not supported with LOAD_UBERSHADERS, please use GENERATE_SHADERS instead.
if (config->enableDiagnostics) {
return nullptr;
}
if (config->hasVolume && config->hasSheen) {
slog.w << "Volume and sheen are not supported together in ubershader mode,"
" removing sheen (" << label << ")." << io::endl;
config->hasSheen = false;
}
if (config->hasTransmission && config->hasSheen) {
slog.w << "Transmission and sheen are not supported together in ubershader mode,"
" removing sheen (" << label << ")." << io::endl;
config->hasSheen = false;
}
const bool clearCoatConflict = config->hasVolume || config->hasTransmission || config->hasSheen;
// Due to sampler overload, disable transmission if necessary and print a friendly warning.
if (config->hasClearCoat && clearCoatConflict) {
slog.w << "Volume, transmission and sheen are not supported in ubershader mode for clearcoat"
" materials (" << label << ")." << io::endl;
config->hasVolume = false;
config->hasTransmission = false;
config->hasSheen = false;
}
constrainMaterial(config, uvmap);
auto getUvIndex = [uvmap](uint8_t srcIndex, bool hasTexture) -> int {
return hasTexture ? int(uvmap->at(srcIndex)) - 1 : -1;
};
Material* material = getMaterial(*config);
MaterialInstance* mi = material->createInstance(label);
mi->setParameter("baseColorIndex",
getUvIndex(config->baseColorUV, config->hasBaseColorTexture));
mi->setParameter("normalIndex", getUvIndex(config->normalUV, config->hasNormalTexture));
mi->setParameter("metallicRoughnessIndex",
getUvIndex(config->metallicRoughnessUV, config->hasMetallicRoughnessTexture));
mi->setParameter("aoIndex", getUvIndex(config->aoUV, config->hasOcclusionTexture));
mi->setParameter("emissiveIndex", getUvIndex(config->emissiveUV, config->hasEmissiveTexture));
mi->setDoubleSided(config->doubleSided);
mi->setCullingMode(config->doubleSided ? CullingMode::NONE : CullingMode::BACK);
// Initially, assume that the clear coat texture can be honored. This is changed to false when
// running into a sampler count limitation. TODO: check if these constraints can now be relaxed.
bool clearCoatNeedsTexture = true;
mat3f identity;
mi->setParameter("baseColorUvMatrix", identity);
mi->setParameter("metallicRoughnessUvMatrix", identity);
mi->setParameter("normalUvMatrix", identity);
mi->setParameter("occlusionUvMatrix", identity);
mi->setParameter("emissiveUvMatrix", identity);
if (config->hasClearCoat) {
mi->setParameter("clearCoatIndex",
getUvIndex(config->clearCoatUV, config->hasClearCoatTexture));
mi->setParameter("clearCoatRoughnessIndex",
getUvIndex(config->clearCoatRoughnessUV, config->hasClearCoatRoughnessTexture));
mi->setParameter("clearCoatNormalIndex",
getUvIndex(config->clearCoatNormalUV, config->hasClearCoatNormalTexture));
mi->setParameter("clearCoatUvMatrix", identity);
mi->setParameter("clearCoatRoughnessUvMatrix", identity);
mi->setParameter("clearCoatNormalUvMatrix", identity);
} else {
if (config->hasSheen) {
clearCoatNeedsTexture = false;
mi->setParameter("sheenColorIndex",
getUvIndex(config->sheenColorUV, config->hasSheenColorTexture));
mi->setParameter("sheenRoughnessIndex",
getUvIndex(config->sheenRoughnessUV, config->hasSheenRoughnessTexture));
mi->setParameter("sheenColorUvMatrix", identity);
mi->setParameter("sheenRoughnessUvMatrix", identity);
}
if (config->hasVolume) {
clearCoatNeedsTexture = false;
mi->setParameter("volumeThicknessUvMatrix", identity);
mi->setParameter("volumeThicknessIndex",
getUvIndex(config->transmissionUV, config->hasVolumeThicknessTexture));
}
if (config->hasTransmission) {
clearCoatNeedsTexture = false;
mi->setParameter("transmissionUvMatrix", identity);
mi->setParameter("transmissionIndex",
getUvIndex(config->transmissionUV, config->hasTransmissionTexture));
}
}
TextureSampler sampler;
mi->setParameter("normalMap", mDummyTexture, sampler);
mi->setParameter("baseColorMap", mDummyTexture, sampler);
mi->setParameter("metallicRoughnessMap", mDummyTexture, sampler);
mi->setParameter("occlusionMap", mDummyTexture, sampler);
mi->setParameter("emissiveMap", mDummyTexture, sampler);
if (clearCoatNeedsTexture) {
mi->setParameter("clearCoatMap", mDummyTexture, sampler);
mi->setParameter("clearCoatRoughnessMap", mDummyTexture, sampler);
mi->setParameter("clearCoatNormalMap", mDummyTexture, sampler);
}
if (!config->hasClearCoat) {
if (config->hasTransmission) {
mi->setParameter("transmissionMap", mDummyTexture, sampler);
}
if (config->hasSheen) {
mi->setParameter("sheenColorMap", mDummyTexture, sampler);
mi->setParameter("sheenRoughnessMap", mDummyTexture, sampler);
}
}
if (mi->getMaterial()->hasParameter("ior")) {
mi->setParameter("ior", 1.5f);
}
if (mi->getMaterial()->hasParameter("reflectance")) {
mi->setParameter("reflectance", 0.5f);
}
return mi;
}
} // anonymous namespace
namespace gltfio {
MaterialProvider* createGPUMorphShaderLoader(const void* data, uint64_t size, filament::Engine* engine) {
return new GPUMorphShaderLoader(data, size,engine);
}
} // namespace gltfio

269
ios/src/morph/GltfEnums.h Normal file
View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_GLTFENUMS_H
#define GLTFIO_GLTFENUMS_H
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/TextureSampler.h>
#include <filament/VertexBuffer.h>
#include <cgltf.h>
#define GL_NEAREST 0x2600
#define GL_LINEAR 0x2601
#define GL_NEAREST_MIPMAP_NEAREST 0x2700
#define GL_LINEAR_MIPMAP_NEAREST 0x2701
#define GL_NEAREST_MIPMAP_LINEAR 0x2702
#define GL_LINEAR_MIPMAP_LINEAR 0x2703
#define GL_REPEAT 0x2901
#define GL_MIRRORED_REPEAT 0x8370
#define GL_CLAMP_TO_EDGE 0x812F
inline filament::TextureSampler::WrapMode getWrapMode(cgltf_int wrap) {
switch (wrap) {
case GL_REPEAT:
return filament::TextureSampler::WrapMode::REPEAT;
case GL_MIRRORED_REPEAT:
return filament::TextureSampler::WrapMode::MIRRORED_REPEAT;
case GL_CLAMP_TO_EDGE:
return filament::TextureSampler::WrapMode::CLAMP_TO_EDGE;
}
return filament::TextureSampler::WrapMode::REPEAT;
}
inline filament::TextureSampler::MinFilter getMinFilter(cgltf_int minFilter) {
switch (minFilter) {
case GL_NEAREST:
return filament::TextureSampler::MinFilter::NEAREST;
case GL_LINEAR:
return filament::TextureSampler::MinFilter::LINEAR;
case GL_NEAREST_MIPMAP_NEAREST:
return filament::TextureSampler::MinFilter::NEAREST_MIPMAP_NEAREST;
case GL_LINEAR_MIPMAP_NEAREST:
return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_NEAREST;
case GL_NEAREST_MIPMAP_LINEAR:
return filament::TextureSampler::MinFilter::NEAREST_MIPMAP_LINEAR;
case GL_LINEAR_MIPMAP_LINEAR:
return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR;
}
return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR;
}
inline filament::TextureSampler::MagFilter getMagFilter(cgltf_int magFilter) {
switch (magFilter) {
case GL_NEAREST:
return filament::TextureSampler::MagFilter::NEAREST;
case GL_LINEAR:
return filament::TextureSampler::MagFilter::LINEAR;
}
return filament::TextureSampler::MagFilter::LINEAR;
}
inline bool getVertexAttrType(cgltf_attribute_type atype, filament::VertexAttribute* attrType) {
switch (atype) {
case cgltf_attribute_type_position:
*attrType = filament::VertexAttribute::POSITION;
return true;
case cgltf_attribute_type_texcoord:
*attrType = filament::VertexAttribute::UV0;
return true;
case cgltf_attribute_type_color:
*attrType = filament::VertexAttribute::COLOR;
return true;
case cgltf_attribute_type_joints:
*attrType = filament::VertexAttribute::BONE_INDICES;
return true;
case cgltf_attribute_type_weights:
*attrType = filament::VertexAttribute::BONE_WEIGHTS;
return true;
case cgltf_attribute_type_normal:
case cgltf_attribute_type_tangent:
default:
return false;
}
}
inline bool getIndexType(cgltf_component_type ctype, filament::IndexBuffer::IndexType* itype) {
switch (ctype) {
case cgltf_component_type_r_8u:
case cgltf_component_type_r_16u:
*itype = filament::IndexBuffer::IndexType::USHORT;
return true;
case cgltf_component_type_r_32u:
*itype = filament::IndexBuffer::IndexType::UINT;
return true;
default:
break;
}
return false;
}
inline bool getPrimitiveType(cgltf_primitive_type in,
filament::RenderableManager::PrimitiveType* out) {
switch (in) {
case cgltf_primitive_type_points:
*out = filament::RenderableManager::PrimitiveType::POINTS;
return true;
case cgltf_primitive_type_lines:
*out = filament::RenderableManager::PrimitiveType::LINES;
return true;
case cgltf_primitive_type_triangles:
*out = filament::RenderableManager::PrimitiveType::TRIANGLES;
return true;
case cgltf_primitive_type_line_loop:
case cgltf_primitive_type_line_strip:
case cgltf_primitive_type_triangle_strip:
case cgltf_primitive_type_triangle_fan:
return false;
}
return false;
}
// This converts a cgltf component type into a Filament Attribute type.
//
// This function has two out parameters. One result is a safe "permitted type" which we know is
// universally accepted across GPU's and backends, but may require conversion (see Transcoder). The
// other result is the "actual type" which requires no conversion.
//
// Returns false if the given component type is invalid.
inline bool getElementType(cgltf_type type, cgltf_component_type ctype,
filament::VertexBuffer::AttributeType* permitType,
filament::VertexBuffer::AttributeType* actualType) {
switch (type) {
case cgltf_type_scalar:
switch (ctype) {
case cgltf_component_type_r_8:
*permitType = filament::VertexBuffer::AttributeType::BYTE;
*actualType = filament::VertexBuffer::AttributeType::BYTE;
return true;
case cgltf_component_type_r_8u:
*permitType = filament::VertexBuffer::AttributeType::UBYTE;
*actualType = filament::VertexBuffer::AttributeType::UBYTE;
return true;
case cgltf_component_type_r_16:
*permitType = filament::VertexBuffer::AttributeType::SHORT;
*actualType = filament::VertexBuffer::AttributeType::SHORT;
return true;
case cgltf_component_type_r_16u:
*permitType = filament::VertexBuffer::AttributeType::USHORT;
*actualType = filament::VertexBuffer::AttributeType::USHORT;
return true;
case cgltf_component_type_r_32u:
*permitType = filament::VertexBuffer::AttributeType::UINT;
*actualType = filament::VertexBuffer::AttributeType::UINT;
return true;
case cgltf_component_type_r_32f:
*permitType = filament::VertexBuffer::AttributeType::FLOAT;
*actualType = filament::VertexBuffer::AttributeType::FLOAT;
return true;
default:
return false;
}
break;
case cgltf_type_vec2:
switch (ctype) {
case cgltf_component_type_r_8:
*permitType = filament::VertexBuffer::AttributeType::BYTE2;
*actualType = filament::VertexBuffer::AttributeType::BYTE2;
return true;
case cgltf_component_type_r_8u:
*permitType = filament::VertexBuffer::AttributeType::UBYTE2;
*actualType = filament::VertexBuffer::AttributeType::UBYTE2;
return true;
case cgltf_component_type_r_16:
*permitType = filament::VertexBuffer::AttributeType::SHORT2;
*actualType = filament::VertexBuffer::AttributeType::SHORT2;
return true;
case cgltf_component_type_r_16u:
*permitType = filament::VertexBuffer::AttributeType::USHORT2;
*actualType = filament::VertexBuffer::AttributeType::USHORT2;
return true;
case cgltf_component_type_r_32f:
*permitType = filament::VertexBuffer::AttributeType::FLOAT2;
*actualType = filament::VertexBuffer::AttributeType::FLOAT2;
return true;
default:
return false;
}
break;
case cgltf_type_vec3:
switch (ctype) {
case cgltf_component_type_r_8:
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
*actualType = filament::VertexBuffer::AttributeType::BYTE3;
return true;
case cgltf_component_type_r_8u:
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
*actualType = filament::VertexBuffer::AttributeType::UBYTE3;
return true;
case cgltf_component_type_r_16:
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
*actualType = filament::VertexBuffer::AttributeType::SHORT3;
return true;
case cgltf_component_type_r_16u:
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
*actualType = filament::VertexBuffer::AttributeType::USHORT3;
return true;
case cgltf_component_type_r_32f:
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
*actualType = filament::VertexBuffer::AttributeType::FLOAT3;
return true;
default:
return false;
}
break;
case cgltf_type_vec4:
switch (ctype) {
case cgltf_component_type_r_8:
*permitType = filament::VertexBuffer::AttributeType::BYTE4;
*actualType = filament::VertexBuffer::AttributeType::BYTE4;
return true;
case cgltf_component_type_r_8u:
*permitType = filament::VertexBuffer::AttributeType::UBYTE4;
*actualType = filament::VertexBuffer::AttributeType::UBYTE4;
return true;
case cgltf_component_type_r_16:
*permitType = filament::VertexBuffer::AttributeType::SHORT4;
*actualType = filament::VertexBuffer::AttributeType::SHORT4;
return true;
case cgltf_component_type_r_16u:
*permitType = filament::VertexBuffer::AttributeType::USHORT4;
*actualType = filament::VertexBuffer::AttributeType::USHORT4;
return true;
case cgltf_component_type_r_32f:
*permitType = filament::VertexBuffer::AttributeType::FLOAT4;
*actualType = filament::VertexBuffer::AttributeType::FLOAT4;
return true;
default:
return false;
}
break;
default:
return false;
}
return false;
}
inline bool requiresConversion(cgltf_type type, cgltf_component_type ctype) {
filament::VertexBuffer::AttributeType permitted;
filament::VertexBuffer::AttributeType actual;
bool supported = getElementType(type, ctype, &permitted, &actual);
return supported && permitted != actual;
}
#endif // GLTFIO_GLTFENUMS_H

120
ios/src/morph/GltfHelpers.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_GLTFHELPERS_H
#define GLTFIO_GLTFHELPERS_H
#include <cgltf.h>
static const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) {
if (view->data)
return (const uint8_t*)view->data;
if (!view->buffer->data)
return NULL;
const uint8_t* result = (const uint8_t*)view->buffer->data;
result += view->offset;
return result;
}
static cgltf_size cgltf_component_size(cgltf_component_type component_type) {
switch (component_type)
{
case cgltf_component_type_r_8:
case cgltf_component_type_r_8u:
return 1;
case cgltf_component_type_r_16:
case cgltf_component_type_r_16u:
return 2;
case cgltf_component_type_r_32u:
case cgltf_component_type_r_32f:
return 4;
case cgltf_component_type_invalid:
default:
return 0;
}
}
static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) {
switch (component_type)
{
case cgltf_component_type_r_16:
return *((const int16_t*) in);
case cgltf_component_type_r_16u:
return *((const uint16_t*) in);
case cgltf_component_type_r_32u:
return *((const uint32_t*) in);
case cgltf_component_type_r_32f:
return (cgltf_size)*((const float*) in);
case cgltf_component_type_r_8:
return *((const int8_t*) in);
case cgltf_component_type_r_8u:
return *((const uint8_t*) in);
default:
return 0;
}
}
static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type,
cgltf_bool normalized) {
if (component_type == cgltf_component_type_r_32f)
{
return *((const float*) in);
}
if (normalized)
{
switch (component_type)
{
// note: glTF spec doesn't currently define normalized conversions for 32-bit integers
case cgltf_component_type_r_16:
return *((const int16_t*) in) / (cgltf_float)32767;
case cgltf_component_type_r_16u:
return *((const uint16_t*) in) / (cgltf_float)65535;
case cgltf_component_type_r_8:
return *((const int8_t*) in) / (cgltf_float)127;
case cgltf_component_type_r_8u:
return *((const uint8_t*) in) / (cgltf_float)255;
default:
return 0;
}
}
return (cgltf_float)cgltf_component_read_index(in, component_type);
}
static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type,
cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out,
cgltf_size element_size) {
cgltf_size num_components = cgltf_num_components(type);
if (element_size < num_components) {
return 0;
}
// There are three special cases for component extraction, see #data-alignment in the 2.0 spec.
cgltf_size component_size = cgltf_component_size(component_type);
for (cgltf_size i = 0; i < num_components; ++i)
{
out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized);
}
return 1;
}
#endif // GLTFIO_GLTFHELPERS_H

43
ios/src/morph/Log.h Normal file
View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_LOG_H
#define TNT_UTILS_LOG_H
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
struct UTILS_PUBLIC Loggers {
// DEBUG level logging stream
io::ostream& d;
// ERROR level logging stream
io::ostream& e;
// WARNING level logging stream
io::ostream& w;
// INFORMATION level logging stream
io::ostream& i;
};
extern UTILS_PUBLIC Loggers const slog;
} // namespace utils
#endif // TNT_UTILS_LOG_H

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cgltf.h>
#include <math/vec4.h>
namespace filament { class VertexBuffer; }
namespace gltfio {
/**
* Internal helper that examines a cgltf primitive and generates data suitable for Filament's
* TANGENTS attribute. This has been designed to be run as a JobSystem job, but clients are not
* required to do so.
*/
struct TangentsJob {
static constexpr int kMorphTargetUnused = -1;
// The inputs to the procedure. The prim is owned by the client, which should ensure that it
// stays alive for the duration of the procedure.
struct InputParams {
const cgltf_primitive* prim;
const int morphTargetIndex = kMorphTargetUnused;
};
// The context of the procedure. These fields are not used by the procedure but are provided as
// a convenience to clients. You can think of this as a scratch space for clients.
struct Context {
filament::VertexBuffer* const vb;
const uint8_t slot;
};
// The outputs of the procedure. The results array gets malloc'd by the procedure, so clients
// should remember to free it.
struct OutputParams {
cgltf_size vertexCount;
filament::math::short4* results;
};
// Clients might want to track the jobs in an array, so the arguments are bundled into a struct.
struct Params {
InputParams in;
Context context;
OutputParams out;
};
// Performs tangents generation synchronously. This can be invoked from inside a job if desired.
// The parameters structure is owned by the client.
static void run(Params* params);
};
} // namespace gltfio

48
ios/src/morph/upcast.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_UPCAST_H
#define TNT_FILAMENT_UPCAST_H
/*
* Generates functions to safely upcast a pointer Bar* to FBar*
* FILAMENT_UPCAST() should be included in the header file
* declaring FBar, e.g.:
*
* #include <Bar.h>
*
* class FBar : public Bar {
* };
*
* FILAMENT_UPCAST(Bar)
*
*/
#define FILAMENT_UPCAST(CLASS) \
inline F##CLASS& upcast(CLASS& that) noexcept { \
return static_cast<F##CLASS &>(that); \
} \
inline const F##CLASS& upcast(const CLASS& that) noexcept { \
return static_cast<const F##CLASS &>(that); \
} \
inline F##CLASS* upcast(CLASS* that) noexcept { \
return static_cast<F##CLASS *>(that); \
} \
inline F##CLASS const* upcast(CLASS const* that) noexcept { \
return static_cast<F##CLASS const *>(that); \
}
#endif // TNT_FILAMENT_UPCAST_H