mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-13 18:52:41 +02:00
I changed names! Renaming the Geometry to NetworkModel, the GeometryResource to ModelResource, i think there is no need for the 2, only one would be enough in my opinion...
This commit is contained in:
parent
42bc91b389
commit
521ce3936b
21 changed files with 150 additions and 130 deletions
|
@ -79,7 +79,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
_missingTextureCount = 0;
|
||||
_unsupportedTextureCount = 0;
|
||||
|
||||
const auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(_avatarFSTFileUrl);
|
||||
const auto resource = DependencyManager::get<ModelCache>()->getModelResource(_avatarFSTFileUrl);
|
||||
resource->refresh();
|
||||
|
||||
const auto resourceLoaded = [this, resource](bool success) {
|
||||
|
@ -297,7 +297,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
if (resource->isLoaded()) {
|
||||
resourceLoaded(!resource->isFailed());
|
||||
} else {
|
||||
connect(resource.data(), &GeometryResource::finished, this, resourceLoaded);
|
||||
connect(resource.data(), &ModelResource::finished, this, resourceLoaded);
|
||||
}
|
||||
} else {
|
||||
addError("Model file cannot be opened", "missing-file");
|
||||
|
|
|
@ -53,7 +53,7 @@ private:
|
|||
int _materialMappingCount = 0;
|
||||
int _materialMappingLoadedCount = 0;
|
||||
|
||||
GeometryResource::Pointer _model;
|
||||
ModelResource::Pointer _model;
|
||||
|
||||
bool _isDiagnosing = false;
|
||||
};
|
||||
|
|
|
@ -134,7 +134,7 @@ bool CollisionPick::getShapeInfoReady(const CollisionRegion& pick) {
|
|||
return _mathPick.loaded;
|
||||
}
|
||||
|
||||
void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
|
||||
void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<ModelResource> resource) {
|
||||
ShapeType type = shapeInfo.getType();
|
||||
glm::vec3 dimensions = pick.transform.getScale();
|
||||
QString modelURL = (resource ? resource->getURL().toString() : "");
|
||||
|
@ -147,7 +147,7 @@ void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick,
|
|||
}
|
||||
}
|
||||
|
||||
void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
|
||||
void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<ModelResource> resource) {
|
||||
// This code was copied and modified from RenderableModelEntityItem::computeShapeInfo
|
||||
// TODO: Move to some shared code area (in entities-renderer? model-networking?)
|
||||
// after we verify this is working and do a diff comparison with RenderableModelEntityItem::computeShapeInfo
|
||||
|
@ -381,7 +381,7 @@ CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool e
|
|||
_scaleWithParent(scaleWithParent),
|
||||
_physicsEngine(physicsEngine) {
|
||||
if (collisionRegion.shouldComputeShapeInfo()) {
|
||||
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
|
||||
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionModelResource(collisionRegion.modelURL);
|
||||
}
|
||||
_mathPick.loaded = isLoaded();
|
||||
}
|
||||
|
|
|
@ -63,14 +63,14 @@ protected:
|
|||
bool isLoaded() const;
|
||||
// Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use.
|
||||
bool getShapeInfoReady(const CollisionRegion& pick);
|
||||
void computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
|
||||
void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
|
||||
void computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<ModelResource> resource);
|
||||
void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<ModelResource> resource);
|
||||
void filterIntersections(std::vector<ContactTestResult>& intersections) const;
|
||||
|
||||
bool _scaleWithParent;
|
||||
|
||||
PhysicsEnginePointer _physicsEngine;
|
||||
QSharedPointer<GeometryResource> _cachedResource;
|
||||
QSharedPointer<ModelResource> _cachedResource;
|
||||
|
||||
// Options for what information to get from collision results
|
||||
bool _includeNormals;
|
||||
|
|
|
@ -955,7 +955,7 @@ void Avatar::simulateAttachments(float deltaTime) {
|
|||
bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i);
|
||||
|
||||
// Watch for texture loading
|
||||
if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||
if (!texturesLoaded && model->getNetworkModel() && model->getNetworkModel()->areTexturesLoaded()) {
|
||||
_attachmentModelsTexturesLoaded[i] = true;
|
||||
model->updateRenderItems();
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
|
||||
// FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem,
|
||||
// but Avatars don't get updates in the same way
|
||||
if (!_texturesLoaded && getGeometry() && getGeometry()->areTexturesLoaded()) {
|
||||
if (!_texturesLoaded && getNetworkModel() && getNetworkModel()->areTexturesLoaded()) {
|
||||
_texturesLoaded = true;
|
||||
updateRenderItems();
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ bool RenderableModelEntityItem::findDetailedParabolaIntersection(const glm::vec3
|
|||
}
|
||||
|
||||
void RenderableModelEntityItem::fetchCollisionGeometryResource() {
|
||||
_collisionGeometryResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(getCollisionShapeURL());
|
||||
_collisionGeometryResource = DependencyManager::get<ModelCache>()->getCollisionModelResource(getCollisionShapeURL());
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::unableToLoadCollisionShape() {
|
||||
|
@ -504,7 +504,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
meshes.push_back(hfmMesh._mesh);
|
||||
}
|
||||
} else {
|
||||
meshes = model->getGeometry()->getMeshes();
|
||||
meshes = model->getNetworkModel()->getMeshes();
|
||||
}
|
||||
int32_t numMeshes = (int32_t)(meshes.size());
|
||||
|
||||
|
@ -1431,7 +1431,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
}
|
||||
|
||||
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||
if (!_texturesLoaded && model->getNetworkModel() && model->getNetworkModel()->areTexturesLoaded()) {
|
||||
withWriteLock([&] {
|
||||
_texturesLoaded = true;
|
||||
});
|
||||
|
|
|
@ -120,7 +120,7 @@ private:
|
|||
bool readyToAnimate() const;
|
||||
void fetchCollisionGeometryResource();
|
||||
|
||||
GeometryResource::Pointer _collisionGeometryResource;
|
||||
ModelResource::Pointer _collisionGeometryResource;
|
||||
std::vector<int> _jointMap;
|
||||
QVariantMap _originalTextures;
|
||||
bool _jointMapCompleted { false };
|
||||
|
|
|
@ -194,7 +194,7 @@ float importanceSample3DDimension(float startDim) {
|
|||
}
|
||||
|
||||
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
|
||||
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource,
|
||||
const ShapeType& shapeType, const ModelResource::Pointer& geometryResource,
|
||||
const TriangleInfo& triangleInfo) {
|
||||
CpuParticle particle;
|
||||
|
||||
|
@ -379,7 +379,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
|
||||
particle::Properties particleProperties;
|
||||
ShapeType shapeType;
|
||||
GeometryResource::Pointer geometryResource;
|
||||
ModelResource::Pointer geometryResource;
|
||||
withReadLock([&] {
|
||||
particleProperties = _particleProperties;
|
||||
shapeType = _shapeType;
|
||||
|
@ -482,7 +482,7 @@ void ParticleEffectEntityRenderer::fetchGeometryResource() {
|
|||
if (hullURL.isEmpty()) {
|
||||
_geometryResource.reset();
|
||||
} else {
|
||||
_geometryResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
|
||||
_geometryResource = DependencyManager::get<ModelCache>()->getCollisionModelResource(hullURL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ private:
|
|||
} _triangleInfo;
|
||||
|
||||
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
|
||||
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource,
|
||||
const ShapeType& shapeType, const ModelResource::Pointer& geometryResource,
|
||||
const TriangleInfo& triangleInfo);
|
||||
void stepSimulation();
|
||||
|
||||
|
@ -108,7 +108,7 @@ private:
|
|||
QString _compoundShapeURL;
|
||||
|
||||
void fetchGeometryResource();
|
||||
GeometryResource::Pointer _geometryResource;
|
||||
ModelResource::Pointer _geometryResource;
|
||||
|
||||
NetworkTexturePointer _networkTexture;
|
||||
ScenePointer _scene;
|
||||
|
|
|
@ -345,7 +345,7 @@ bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, c
|
|||
}
|
||||
|
||||
bool ZoneEntityItem::contains(const glm::vec3& point) const {
|
||||
GeometryResource::Pointer resource = _shapeResource;
|
||||
ModelResource::Pointer resource = _shapeResource;
|
||||
if (_shapeType == SHAPE_TYPE_COMPOUND && resource) {
|
||||
if (resource->isLoaded()) {
|
||||
const HFMModel& hfmModel = resource->getHFMModel();
|
||||
|
@ -462,7 +462,7 @@ void ZoneEntityItem::fetchCollisionGeometryResource() {
|
|||
if (hullURL.isEmpty()) {
|
||||
_shapeResource.reset();
|
||||
} else {
|
||||
_shapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
|
||||
_shapeResource = DependencyManager::get<ModelCache>()->getCollisionModelResource(hullURL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ protected:
|
|||
static bool _zonesArePickable;
|
||||
|
||||
void fetchCollisionGeometryResource();
|
||||
GeometryResource::Pointer _shapeResource;
|
||||
ModelResource::Pointer _shapeResource;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1377,11 +1377,13 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
|
|||
|
||||
// Now that we've initialized the joint, we can define the transform
|
||||
// modelIDs is ordered from parent to children, so we can safely get parent transforms from earlier joints as we iterate
|
||||
joint.globalTransform = glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation * joint.postRotation) * joint.postTransform;
|
||||
joint.localTransform = glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation * joint.postRotation) * joint.postTransform;
|
||||
joint.globalTransform = joint.localTransform;
|
||||
if (joint.parentIndex != -1 && joint.parentIndex < (int)jointIndex && !needMixamoHack) {
|
||||
hfm::Joint& parentJoint = hfmModel.joints[joint.parentIndex];
|
||||
// joint.globalTransform = joint.globalTransform * parentJoint.globalTransform;
|
||||
joint.globalTransform = parentJoint.globalTransform * joint.globalTransform;
|
||||
// SG Change: i think this not correct and the [parent]*[local] is the correct answer here
|
||||
//joint.globalTransform = joint.globalTransform * parentJoint.globalTransform;
|
||||
joint.globalTransform = parentJoint.globalTransform * joint.localTransform;
|
||||
if (parentJoint.hasGeometricOffset) {
|
||||
// Per the FBX standard, geometric offset should not propagate to children.
|
||||
// However, we must be careful when modifying the behavior of FBXSerializer.
|
||||
|
@ -1396,13 +1398,21 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
|
|||
joint.globalTransform = joint.globalTransform * geometricOffset;
|
||||
}
|
||||
|
||||
// accumulate local transforms
|
||||
// TODO: Remove these lines, just here to make sure we are not breaking the transform computation
|
||||
// QString modelID = fbxModels.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key());
|
||||
glm::mat4 anotherModelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, hfmModel.applicationName == "mixamo.com", url);
|
||||
/* if (anotherModelTransform != joint.globalTransform) {
|
||||
joint.globalTransform = anotherModelTransform;
|
||||
auto col0 = (glm::epsilonNotEqual(anotherModelTransform[0], joint.globalTransform[0], 0.001f));
|
||||
auto col1 = (glm::epsilonNotEqual(anotherModelTransform[1], joint.globalTransform[1], 0.001f));
|
||||
auto col2 = (glm::epsilonNotEqual(anotherModelTransform[2], joint.globalTransform[2], 0.001f));
|
||||
auto col3 = (glm::epsilonNotEqual(anotherModelTransform[3], joint.globalTransform[3], 0.001f));
|
||||
if ( glm::any(col0)
|
||||
|| glm::any(col1)
|
||||
|| glm::any(col2)
|
||||
|| glm::any(col3)) {
|
||||
anotherModelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, hfmModel.applicationName == "mixamo.com", url);
|
||||
// joint.globalTransform = anotherModelTransform;
|
||||
}
|
||||
*/
|
||||
|
||||
hfmModel.joints.push_back(joint);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ public:
|
|||
glm::vec3 geometricScaling;
|
||||
|
||||
// globalTransform is the transform of the joint with all parent transforms applied, plus the geometric offset
|
||||
glm::mat4 localTransform;
|
||||
glm::mat4 globalTransform;
|
||||
};
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
Textures getTextures() { return _textures; }
|
||||
|
||||
protected:
|
||||
friend class Geometry;
|
||||
friend class NetworkModel;
|
||||
|
||||
Textures _textures;
|
||||
|
||||
|
|
|
@ -203,23 +203,23 @@ QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
|
|||
return textureBaseUrl.isValid() ? textureBaseUrl : url;
|
||||
}
|
||||
|
||||
GeometryResource::GeometryResource(const GeometryResource& other) :
|
||||
ModelResource::ModelResource(const ModelResource& other) :
|
||||
Resource(other),
|
||||
Geometry(other),
|
||||
NetworkModel(other),
|
||||
_modelLoader(other._modelLoader),
|
||||
_mappingPair(other._mappingPair),
|
||||
_textureBaseURL(other._textureBaseURL),
|
||||
_combineParts(other._combineParts),
|
||||
_isCacheable(other._isCacheable)
|
||||
{
|
||||
if (other._geometryResource) {
|
||||
if (other._modelResource) {
|
||||
_startedLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryResource::downloadFinished(const QByteArray& data) {
|
||||
void ModelResource::downloadFinished(const QByteArray& data) {
|
||||
if (_effectiveBaseURL.fileName().toLower().endsWith(".fst")) {
|
||||
PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString(), { { "url", _url.toString() } });
|
||||
PROFILE_ASYNC_BEGIN(resource_parse_geometry, "ModelResource::downloadFinished", _url.toString(), { { "url", _url.toString() } });
|
||||
|
||||
// store parsed contents of FST file
|
||||
_mapping = FSTReader::readMapping(data);
|
||||
|
@ -267,19 +267,19 @@ void GeometryResource::downloadFinished(const QByteArray& data) {
|
|||
auto modelCache = DependencyManager::get<ModelCache>();
|
||||
GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false };
|
||||
|
||||
// Get the raw GeometryResource
|
||||
_geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash<GeometryExtra>()(extra)).staticCast<GeometryResource>();
|
||||
// Get the raw ModelResource
|
||||
_modelResource = modelCache->getResource(url, QUrl(), &extra, std::hash<GeometryExtra>()(extra)).staticCast<ModelResource>();
|
||||
// Avoid caching nested resources - their references will be held by the parent
|
||||
_geometryResource->_isCacheable = false;
|
||||
_modelResource->_isCacheable = false;
|
||||
|
||||
if (_geometryResource->isLoaded()) {
|
||||
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
|
||||
if (_modelResource->isLoaded()) {
|
||||
onGeometryMappingLoaded(!_modelResource->getURL().isEmpty());
|
||||
} else {
|
||||
if (_connection) {
|
||||
disconnect(_connection);
|
||||
}
|
||||
|
||||
_connection = connect(_geometryResource.data(), &Resource::finished, this, &GeometryResource::onGeometryMappingLoaded);
|
||||
_connection = connect(_modelResource.data(), &Resource::finished, this, &ModelResource::onGeometryMappingLoaded);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -291,32 +291,32 @@ void GeometryResource::downloadFinished(const QByteArray& data) {
|
|||
}
|
||||
}
|
||||
|
||||
void GeometryResource::onGeometryMappingLoaded(bool success) {
|
||||
if (success && _geometryResource) {
|
||||
_hfmModel = _geometryResource->_hfmModel;
|
||||
_materialMapping = _geometryResource->_materialMapping;
|
||||
_meshParts = _geometryResource->_meshParts;
|
||||
_meshes = _geometryResource->_meshes;
|
||||
_materials = _geometryResource->_materials;
|
||||
void ModelResource::onGeometryMappingLoaded(bool success) {
|
||||
if (success && _modelResource) {
|
||||
_hfmModel = _modelResource->_hfmModel;
|
||||
_materialMapping = _modelResource->_materialMapping;
|
||||
_meshParts = _modelResource->_meshParts;
|
||||
_meshes = _modelResource->_meshes;
|
||||
_materials = _modelResource->_materials;
|
||||
|
||||
// Avoid holding onto extra references
|
||||
_geometryResource.reset();
|
||||
_modelResource.reset();
|
||||
// Make sure connection will not trigger again
|
||||
disconnect(_connection); // FIXME Should not have to do this
|
||||
}
|
||||
|
||||
PROFILE_ASYNC_END(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString());
|
||||
PROFILE_ASYNC_END(resource_parse_geometry, "ModelResource::downloadFinished", _url.toString());
|
||||
finishedLoading(success);
|
||||
}
|
||||
|
||||
void GeometryResource::setExtra(void* extra) {
|
||||
void ModelResource::setExtra(void* extra) {
|
||||
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
|
||||
_mappingPair = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash());
|
||||
_textureBaseURL = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();
|
||||
_combineParts = geometryExtra ? geometryExtra->combineParts : true;
|
||||
}
|
||||
|
||||
void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) {
|
||||
void ModelResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) {
|
||||
// Assume ownership of the processed HFMModel
|
||||
_hfmModel = hfmModel;
|
||||
_materialMapping = materialMapping;
|
||||
|
@ -348,12 +348,12 @@ void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const M
|
|||
finishedLoading(true);
|
||||
}
|
||||
|
||||
void GeometryResource::deleter() {
|
||||
void ModelResource::deleter() {
|
||||
resetTextures();
|
||||
Resource::deleter();
|
||||
}
|
||||
|
||||
void GeometryResource::setTextures() {
|
||||
void ModelResource::setTextures() {
|
||||
if (_hfmModel) {
|
||||
for (const HFMMaterial& material : _hfmModel->materials) {
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseURL));
|
||||
|
@ -361,7 +361,7 @@ void GeometryResource::setTextures() {
|
|||
}
|
||||
}
|
||||
|
||||
void GeometryResource::resetTextures() {
|
||||
void ModelResource::resetTextures() {
|
||||
_materials.clear();
|
||||
}
|
||||
|
||||
|
@ -377,17 +377,17 @@ ModelCache::ModelCache() {
|
|||
}
|
||||
|
||||
QSharedPointer<Resource> ModelCache::createResource(const QUrl& url) {
|
||||
return QSharedPointer<Resource>(new GeometryResource(url, _modelLoader), &GeometryResource::deleter);
|
||||
return QSharedPointer<Resource>(new ModelResource(url, _modelLoader), &ModelResource::deleter);
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ModelCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||
return QSharedPointer<Resource>(new GeometryResource(*resource.staticCast<GeometryResource>()), &GeometryResource::deleter);
|
||||
return QSharedPointer<Resource>(new ModelResource(*resource.staticCast<ModelResource>()), &ModelResource::deleter);
|
||||
}
|
||||
|
||||
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) {
|
||||
ModelResource::Pointer ModelCache::getModelResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) {
|
||||
bool combineParts = true;
|
||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
|
||||
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();
|
||||
ModelResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<ModelResource>();
|
||||
if (resource) {
|
||||
if (resource->isLoaded() && resource->shouldSetTextures()) {
|
||||
resource->setTextures();
|
||||
|
@ -396,12 +396,12 @@ GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const
|
|||
return resource;
|
||||
}
|
||||
|
||||
GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& url,
|
||||
ModelResource::Pointer ModelCache::getCollisionModelResource(const QUrl& url,
|
||||
const GeometryMappingPair& mapping,
|
||||
const QUrl& textureBaseUrl) {
|
||||
bool combineParts = false;
|
||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
|
||||
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();
|
||||
ModelResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<ModelResource>();
|
||||
if (resource) {
|
||||
if (resource->isLoaded() && resource->shouldSetTextures()) {
|
||||
resource->setTextures();
|
||||
|
@ -410,7 +410,7 @@ GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& u
|
|||
return resource;
|
||||
}
|
||||
|
||||
const QVariantMap Geometry::getTextures() const {
|
||||
const QVariantMap NetworkModel::getTextures() const {
|
||||
QVariantMap textures;
|
||||
for (const auto& material : _materials) {
|
||||
for (const auto& texture : material->_textures) {
|
||||
|
@ -424,22 +424,22 @@ const QVariantMap Geometry::getTextures() const {
|
|||
}
|
||||
|
||||
// FIXME: The materials should only be copied when modified, but the Model currently caches the original
|
||||
Geometry::Geometry(const Geometry& geometry) {
|
||||
_hfmModel = geometry._hfmModel;
|
||||
_materialMapping = geometry._materialMapping;
|
||||
_meshes = geometry._meshes;
|
||||
_meshParts = geometry._meshParts;
|
||||
NetworkModel::NetworkModel(const NetworkModel& networkModel) {
|
||||
_hfmModel = networkModel._hfmModel;
|
||||
_materialMapping = networkModel._materialMapping;
|
||||
_meshes = networkModel._meshes;
|
||||
_meshParts = networkModel._meshParts;
|
||||
|
||||
_materials.reserve(geometry._materials.size());
|
||||
for (const auto& material : geometry._materials) {
|
||||
_materials.reserve(networkModel._materials.size());
|
||||
for (const auto& material : networkModel._materials) {
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(*material));
|
||||
}
|
||||
|
||||
_animGraphOverrideUrl = geometry._animGraphOverrideUrl;
|
||||
_mapping = geometry._mapping;
|
||||
_animGraphOverrideUrl = networkModel._animGraphOverrideUrl;
|
||||
_mapping = networkModel._mapping;
|
||||
}
|
||||
|
||||
void Geometry::setTextures(const QVariantMap& textureMap) {
|
||||
void NetworkModel::setTextures(const QVariantMap& textureMap) {
|
||||
if (_meshes->size() > 0) {
|
||||
for (auto& material : _materials) {
|
||||
// Check if any material textures actually changed
|
||||
|
@ -447,7 +447,7 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
|
|||
[&textureMap](const NetworkMaterial::Textures::value_type& it) { return it.second.texture && textureMap.contains(it.second.name); })) {
|
||||
|
||||
// FIXME: The Model currently caches the materials (waste of space!)
|
||||
// so they must be copied in the Geometry copy-ctor
|
||||
// so they must be copied in the NetworkModel copy-ctor
|
||||
// if (material->isOriginal()) {
|
||||
// // Copy the material to avoid mutating the cached version
|
||||
// material = std::make_shared<NetworkMaterial>(*material);
|
||||
|
@ -461,11 +461,11 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
|
|||
// If we only use cached textures, they should all be loaded
|
||||
areTexturesLoaded();
|
||||
} else {
|
||||
qCWarning(modelnetworking) << "Ignoring setTextures(); geometry not ready";
|
||||
qCWarning(modelnetworking) << "Ignoring setTextures(); NetworkModel not ready";
|
||||
}
|
||||
}
|
||||
|
||||
bool Geometry::areTexturesLoaded() const {
|
||||
bool NetworkModel::areTexturesLoaded() const {
|
||||
if (!_areTexturesLoaded) {
|
||||
for (auto& material : _materials) {
|
||||
if (material->isMissingTexture()) {
|
||||
|
@ -500,30 +500,35 @@ bool Geometry::areTexturesLoaded() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
const std::shared_ptr<NetworkMaterial> Geometry::getShapeMaterial(int partID) const {
|
||||
if ((partID >= 0) && (partID < (int)_meshParts->size())) {
|
||||
const std::shared_ptr<NetworkMaterial> NetworkModel::getShapeMaterial(int partID) const {
|
||||
/* if ((partID >= 0) && (partID < (int)_meshParts->size())) {
|
||||
int materialID = _meshParts->at(partID)->materialID;
|
||||
if ((materialID >= 0) && (materialID < (int)_materials.size())) {
|
||||
return _materials[materialID];
|
||||
}
|
||||
}*/
|
||||
|
||||
auto materialID = getHFMModel().shapes[partID].material;
|
||||
if ((materialID >= 0) && (materialID < (int)_materials.size())) {
|
||||
return _materials[materialID];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GeometryResourceWatcher::startWatching() {
|
||||
connect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished);
|
||||
connect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed);
|
||||
void ModelResourceWatcher::startWatching() {
|
||||
connect(_resource.data(), &Resource::finished, this, &ModelResourceWatcher::resourceFinished);
|
||||
connect(_resource.data(), &Resource::onRefresh, this, &ModelResourceWatcher::resourceRefreshed);
|
||||
if (_resource->isLoaded()) {
|
||||
resourceFinished(!_resource->getURL().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryResourceWatcher::stopWatching() {
|
||||
disconnect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished);
|
||||
disconnect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed);
|
||||
void ModelResourceWatcher::stopWatching() {
|
||||
disconnect(_resource.data(), &Resource::finished, this, &ModelResourceWatcher::resourceFinished);
|
||||
disconnect(_resource.data(), &Resource::onRefresh, this, &ModelResourceWatcher::resourceRefreshed);
|
||||
}
|
||||
|
||||
void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) {
|
||||
void ModelResourceWatcher::setResource(ModelResource::Pointer resource) {
|
||||
if (_resource) {
|
||||
stopWatching();
|
||||
}
|
||||
|
@ -537,14 +542,14 @@ void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) {
|
|||
}
|
||||
}
|
||||
|
||||
void GeometryResourceWatcher::resourceFinished(bool success) {
|
||||
void ModelResourceWatcher::resourceFinished(bool success) {
|
||||
if (success) {
|
||||
_geometryRef = std::make_shared<Geometry>(*_resource);
|
||||
_networkModelRef = std::make_shared<NetworkModel>(*_resource);
|
||||
}
|
||||
emit finished(success);
|
||||
}
|
||||
|
||||
void GeometryResourceWatcher::resourceRefreshed() {
|
||||
void ModelResourceWatcher::resourceRefreshed() {
|
||||
// FIXME: Model is not set up to handle a refresh
|
||||
// _instance.reset();
|
||||
}
|
||||
|
|
|
@ -27,14 +27,14 @@ class MeshPart;
|
|||
using GeometryMappingPair = std::pair<QUrl, QVariantHash>;
|
||||
Q_DECLARE_METATYPE(GeometryMappingPair)
|
||||
|
||||
class Geometry {
|
||||
class NetworkModel {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<Geometry>;
|
||||
using WeakPointer = std::weak_ptr<Geometry>;
|
||||
using Pointer = std::shared_ptr<NetworkModel>;
|
||||
using WeakPointer = std::weak_ptr<NetworkModel>;
|
||||
|
||||
Geometry() = default;
|
||||
Geometry(const Geometry& geometry);
|
||||
virtual ~Geometry() = default;
|
||||
NetworkModel() = default;
|
||||
NetworkModel(const NetworkModel& geometry);
|
||||
virtual ~NetworkModel() = default;
|
||||
|
||||
// Immutable over lifetime
|
||||
using GeometryMeshes = std::vector<std::shared_ptr<const graphics::Mesh>>;
|
||||
|
@ -76,22 +76,22 @@ private:
|
|||
};
|
||||
|
||||
/// A geometry loaded from the network.
|
||||
class GeometryResource : public Resource, public Geometry {
|
||||
class ModelResource : public Resource, public NetworkModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using Pointer = QSharedPointer<GeometryResource>;
|
||||
using Pointer = QSharedPointer<ModelResource>;
|
||||
|
||||
GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {}
|
||||
GeometryResource(const GeometryResource& other);
|
||||
ModelResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {}
|
||||
ModelResource(const ModelResource& other);
|
||||
|
||||
QString getType() const override { return "Geometry"; }
|
||||
QString getType() const override { return "Model"; }
|
||||
|
||||
virtual void deleter() override;
|
||||
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
void setExtra(void* extra) override;
|
||||
|
||||
virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); }
|
||||
virtual bool areTexturesLoaded() const override { return isLoaded() && NetworkModel::areTexturesLoaded(); }
|
||||
|
||||
private slots:
|
||||
void onGeometryMappingLoaded(bool success);
|
||||
|
@ -115,21 +115,21 @@ private:
|
|||
QUrl _textureBaseURL;
|
||||
bool _combineParts;
|
||||
|
||||
GeometryResource::Pointer _geometryResource;
|
||||
ModelResource::Pointer _modelResource;
|
||||
QMetaObject::Connection _connection;
|
||||
|
||||
bool _isCacheable{ true };
|
||||
};
|
||||
|
||||
class GeometryResourceWatcher : public QObject {
|
||||
class ModelResourceWatcher : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using Pointer = std::shared_ptr<GeometryResourceWatcher>;
|
||||
using Pointer = std::shared_ptr<ModelResourceWatcher>;
|
||||
|
||||
GeometryResourceWatcher() = delete;
|
||||
GeometryResourceWatcher(Geometry::Pointer& geometryPtr) : _geometryRef(geometryPtr) {}
|
||||
ModelResourceWatcher() = delete;
|
||||
ModelResourceWatcher(NetworkModel::Pointer& geometryPtr) : _networkModelRef(geometryPtr) {}
|
||||
|
||||
void setResource(GeometryResource::Pointer resource);
|
||||
void setResource(ModelResource::Pointer resource);
|
||||
|
||||
QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); }
|
||||
int getResourceDownloadAttempts() { return _resource ? _resource->getDownloadAttempts() : 0; }
|
||||
|
@ -147,8 +147,8 @@ private slots:
|
|||
void resourceRefreshed();
|
||||
|
||||
private:
|
||||
GeometryResource::Pointer _resource;
|
||||
Geometry::Pointer& _geometryRef;
|
||||
ModelResource::Pointer _resource;
|
||||
NetworkModel::Pointer& _networkModelRef;
|
||||
};
|
||||
|
||||
/// Stores cached model geometries.
|
||||
|
@ -158,18 +158,18 @@ class ModelCache : public ResourceCache, public Dependency {
|
|||
|
||||
public:
|
||||
|
||||
GeometryResource::Pointer getGeometryResource(const QUrl& url,
|
||||
ModelResource::Pointer getModelResource(const QUrl& url,
|
||||
const GeometryMappingPair& mapping =
|
||||
GeometryMappingPair(QUrl(), QVariantHash()),
|
||||
const QUrl& textureBaseUrl = QUrl());
|
||||
|
||||
GeometryResource::Pointer getCollisionGeometryResource(const QUrl& url,
|
||||
ModelResource::Pointer getCollisionModelResource(const QUrl& url,
|
||||
const GeometryMappingPair& mapping =
|
||||
GeometryMappingPair(QUrl(), QVariantHash()),
|
||||
const QUrl& textureBaseUrl = QUrl());
|
||||
|
||||
protected:
|
||||
friend class GeometryResource;
|
||||
friend class ModelResource;
|
||||
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
|
||||
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
|
||||
|
|
|
@ -87,7 +87,7 @@ void CauterizedModel::createRenderItemSet() {
|
|||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
||||
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
||||
auto material = getGeometry()->getShapeMaterial(shapeID);
|
||||
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
||||
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
|
||||
shapeID++;
|
||||
|
|
|
@ -202,9 +202,13 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
|||
|
||||
assert(model && model->isLoaded());
|
||||
|
||||
auto shape = model->getHFMModel().shapes[shapeIndex];
|
||||
assert(shape.mesh == meshIndex);
|
||||
assert(shape.meshPart == partIndex);
|
||||
|
||||
bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning();
|
||||
|
||||
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
|
||||
auto& modelMesh = model->getNetworkModel()->getMeshes().at(_meshIndex);
|
||||
_meshNumVertices = (int)modelMesh->getNumVertices();
|
||||
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
||||
|
||||
|
@ -263,7 +267,7 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) {
|
|||
_hasTangents = !mesh.tangents.isEmpty();
|
||||
}
|
||||
|
||||
auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID);
|
||||
auto networkMaterial = model->getNetworkModel()->getShapeMaterial(_shapeID);
|
||||
if (networkMaterial) {
|
||||
addMaterial(graphics::MaterialLayer(networkMaterial, 0));
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
using namespace std;
|
||||
|
||||
int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();
|
||||
int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<Geometry::WeakPointer>();
|
||||
int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<NetworkModel::WeakPointer>();
|
||||
int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3>>();
|
||||
int normalTypeVecTypeId = qRegisterMetaType<QVector<NormalType>>("QVector<NormalType>");
|
||||
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
||||
|
@ -71,7 +71,7 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
|
|||
|
||||
setSnapModelToRegistrationPoint(true, glm::vec3(0.5f));
|
||||
|
||||
connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished);
|
||||
connect(&_renderWatcher, &ModelResourceWatcher::finished, this, &Model::loadURLFinished);
|
||||
}
|
||||
|
||||
Model::~Model() {
|
||||
|
@ -151,7 +151,7 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
}
|
||||
|
||||
void Model::calculateTextureInfo() {
|
||||
if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItemsMap.isEmpty()) {
|
||||
if (!_hasCalculatedTextureInfo && isLoaded() && getNetworkModel()->areTexturesLoaded() && !_modelMeshRenderItemsMap.isEmpty()) {
|
||||
size_t textureSize = 0;
|
||||
int textureCount = 0;
|
||||
bool allTexturesLoaded = true;
|
||||
|
@ -178,12 +178,12 @@ int Model::getRenderInfoTextureCount() {
|
|||
}
|
||||
|
||||
bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) {
|
||||
if (!getGeometry()) {
|
||||
if (!getNetworkModel()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const HFMModel& hfmModel = getHFMModel();
|
||||
const auto& networkMeshes = getGeometry()->getMeshes();
|
||||
const auto& networkMeshes = getNetworkModel()->getMeshes();
|
||||
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
||||
// to false to rebuild out mesh groups.
|
||||
if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() || meshIndex >= (int)_meshStates.size()) {
|
||||
|
@ -643,8 +643,8 @@ glm::mat4 Model::getWorldToHFMMatrix() const {
|
|||
// TODO: deprecate and remove
|
||||
MeshProxyList Model::getMeshes() const {
|
||||
MeshProxyList result;
|
||||
const Geometry::Pointer& renderGeometry = getGeometry();
|
||||
const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes();
|
||||
const NetworkModel::Pointer& renderGeometry = getNetworkModel();
|
||||
const NetworkModel::GeometryMeshes& meshes = renderGeometry->getMeshes();
|
||||
|
||||
if (!isLoaded()) {
|
||||
return result;
|
||||
|
@ -772,7 +772,7 @@ scriptable::ScriptableModelBase Model::getScriptableModel() {
|
|||
int numParts = (int)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
auto& materialName = _modelMeshMaterialNames[shapeID];
|
||||
result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName);
|
||||
result.appendMaterial(graphics::MaterialLayer(getNetworkModel()->getShapeMaterial(shapeID), 0), shapeID, materialName);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_materialMappingMutex);
|
||||
|
@ -1196,7 +1196,7 @@ void Model::setURL(const QUrl& url) {
|
|||
invalidCalculatedMeshBoxes();
|
||||
deleteGeometry();
|
||||
|
||||
auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(url);
|
||||
auto resource = DependencyManager::get<ModelCache>()->getModelResource(url);
|
||||
if (resource) {
|
||||
resource->setLoadPriority(this, _loadingPriority);
|
||||
_renderWatcher.setResource(resource);
|
||||
|
@ -1487,7 +1487,7 @@ void Model::createRenderItemSet() {
|
|||
int numParts = (int)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
||||
auto material = getGeometry()->getShapeMaterial(shapeID);
|
||||
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
||||
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
|
||||
shapeID++;
|
||||
|
@ -1680,7 +1680,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string
|
|||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
class CollisionRenderGeometry : public Geometry {
|
||||
class CollisionRenderGeometry : public NetworkModel {
|
||||
public:
|
||||
CollisionRenderGeometry(graphics::MeshPointer mesh) {
|
||||
_hfmModel = std::make_shared<HFMModel>();
|
||||
|
@ -1838,7 +1838,7 @@ void Blender::run() {
|
|||
|
||||
bool Model::maybeStartBlender() {
|
||||
if (isLoaded()) {
|
||||
QThreadPool::globalInstance()->start(new Blender(getThisPointer(), getGeometry()->getConstHFMModelPointer(),
|
||||
QThreadPool::globalInstance()->start(new Blender(getThisPointer(), getNetworkModel()->getConstHFMModelPointer(),
|
||||
++_blendNumber, _blendshapeCoefficients));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ public:
|
|||
virtual void updateClusterMatrices();
|
||||
|
||||
/// Returns a reference to the shared geometry.
|
||||
const Geometry::Pointer& getGeometry() const { return _renderGeometry; }
|
||||
const NetworkModel::Pointer& getNetworkModel() const { return _renderGeometry; }
|
||||
|
||||
const QVariantMap getTextures() const { assert(isLoaded()); return _renderGeometry->getTextures(); }
|
||||
Q_INVOKABLE virtual void setTextures(const QVariantMap& textures);
|
||||
|
@ -391,9 +391,9 @@ protected:
|
|||
/// \return true if joint exists
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
|
||||
Geometry::Pointer _renderGeometry; // only ever set by its watcher
|
||||
NetworkModel::Pointer _renderGeometry; // only ever set by its watcher
|
||||
|
||||
GeometryResourceWatcher _renderWatcher;
|
||||
ModelResourceWatcher _renderWatcher;
|
||||
|
||||
SpatiallyNestable* _spatiallyNestableOverride;
|
||||
|
||||
|
@ -515,7 +515,7 @@ private:
|
|||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ModelPointer)
|
||||
Q_DECLARE_METATYPE(Geometry::WeakPointer)
|
||||
Q_DECLARE_METATYPE(NetworkModel::WeakPointer)
|
||||
Q_DECLARE_METATYPE(BlendshapeOffset)
|
||||
|
||||
/// Handle management of pending models that need blending
|
||||
|
|
Loading…
Reference in a new issue